001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.system.repository;
018    
019    import java.io.File;
020    import java.io.FileInputStream;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.net.MalformedURLException;
024    import java.net.URI;
025    import java.net.URISyntaxException;
026    import java.net.URL;
027    import java.net.URLClassLoader;
028    import java.util.Enumeration;
029    import java.util.LinkedHashSet;
030    import java.util.Map;
031    import java.util.HashMap;
032    import java.util.zip.ZipEntry;
033    import java.util.zip.ZipException;
034    import java.util.zip.ZipFile;
035    import javax.xml.parsers.DocumentBuilderFactory;
036    import javax.xml.parsers.ParserConfigurationException;
037    
038    import org.apache.commons.logging.Log;
039    import org.apache.commons.logging.LogFactory;
040    import org.apache.geronimo.kernel.repository.Artifact;
041    import org.apache.geronimo.kernel.repository.ArtifactTypeHandler;
042    import org.apache.geronimo.kernel.repository.FileWriteMonitor;
043    import org.apache.geronimo.kernel.repository.WriteableRepository;
044    import org.apache.geronimo.kernel.util.XmlUtil;
045    import org.apache.geronimo.system.serverinfo.ServerInfo;
046    import org.w3c.dom.Document;
047    import org.w3c.dom.Element;
048    import org.w3c.dom.Node;
049    import org.w3c.dom.NodeList;
050    import org.xml.sax.InputSource;
051    import org.xml.sax.SAXException;
052    
053    /**
054     * @version $Rev: 487175 $ $Date: 2006-12-14 03:10:31 -0800 (Thu, 14 Dec 2006) $
055     */
056    public abstract class AbstractRepository implements WriteableRepository {
057        protected static final Log log = LogFactory.getLog(AbstractRepository.class);
058        private final static ArtifactTypeHandler DEFAULT_TYPE_HANDLER = new CopyArtifactTypeHandler();
059        protected final File rootFile;
060        private final Map typeHandlers = new HashMap();
061    
062        public AbstractRepository(URI root, ServerInfo serverInfo) {
063            this(resolveRoot(root, serverInfo));
064        }
065    
066        public AbstractRepository(File rootFile) {
067            if (rootFile == null) throw new NullPointerException("root is null");
068    
069            if (!rootFile.exists() || !rootFile.isDirectory() || !rootFile.canRead()) {
070                throw new IllegalStateException("Maven2Repository must have a root that's a valid readable directory (not " + rootFile.getAbsolutePath() + ")");
071            }
072    
073            this.rootFile = rootFile;
074            log.debug("Repository root is " + rootFile.getAbsolutePath());
075    
076            typeHandlers.put("car", new UnpackArtifactTypeHandler());
077        }
078    
079        private static File resolveRoot(URI root, ServerInfo serverInfo) {
080            if (root == null) throw new NullPointerException("root is null");
081    
082            if (!root.toString().endsWith("/")) {
083                try {
084                    root = new URI(root.toString() + "/");
085                } catch (URISyntaxException e) {
086                    throw new RuntimeException("Invalid repository root (does not end with / ) and can't add myself", e);
087                }
088            }
089    
090            URI resolvedUri;
091            if (serverInfo != null) {
092                resolvedUri = serverInfo.resolve(root);
093            } else {
094                resolvedUri = root;
095            }
096    
097            if (!resolvedUri.getScheme().equals("file")) {
098                throw new IllegalStateException("FileSystemRepository must have a root that's a local directory (not " + resolvedUri + ")");
099            }
100    
101            File rootFile = new File(resolvedUri);
102            return rootFile;
103        }
104    
105        public boolean contains(Artifact artifact) {
106            // Note: getLocation(artifact) does an artifact.isResolved() check - no need to do it here.
107            File location = getLocation(artifact);
108            return location.canRead() && (location.isFile() || new File(location, "META-INF").isDirectory());
109        }
110    
111        private static final String NAMESPACE = "http://geronimo.apache.org/xml/ns/deployment-1.2";
112        public LinkedHashSet getDependencies(Artifact artifact) {
113            if(!artifact.isResolved()) {
114                throw new IllegalArgumentException("Artifact "+artifact+" is not fully resolved");
115            }
116            LinkedHashSet dependencies = new LinkedHashSet();
117            URL url;
118            try {
119                File location = getLocation(artifact);
120                url = location.toURL();
121            } catch (MalformedURLException e) {
122                throw (IllegalStateException)new IllegalStateException("Unable to get URL for dependency " + artifact).initCause(e);
123            }
124            ClassLoader depCL = new URLClassLoader(new URL[]{url}, ClassLoader.getSystemClassLoader());
125            InputStream is = depCL.getResourceAsStream("META-INF/geronimo-dependency.xml");
126            try {
127                if (is != null) {
128                    InputSource in = new InputSource(is);
129                    DocumentBuilderFactory dfactory = XmlUtil.newDocumentBuilderFactory();
130                    dfactory.setNamespaceAware(true);
131                    try {
132                        Document doc = dfactory.newDocumentBuilder().parse(in);
133                        Element root = doc.getDocumentElement();
134                        NodeList configs = root.getElementsByTagNameNS(NAMESPACE, "dependency");
135                        for (int i = 0; i < configs.getLength(); i++) {
136                            Element dependencyElement = (Element) configs.item(i);
137                            String groupId = getString(dependencyElement, "groupId");
138                            String artifactId = getString(dependencyElement, "artifactId");
139                            String version = getString(dependencyElement, "version");
140                            String type = getString(dependencyElement, "type");
141                            if (type == null) {
142                                type = "jar";
143                            }
144                            dependencies.add(new Artifact(groupId, artifactId,  version, type));
145                        }
146                    } catch (IOException e) {
147                        throw (IllegalStateException)new IllegalStateException("Unable to parse geronimo-dependency.xml file in " + url).initCause(e);
148                    } catch (ParserConfigurationException e) {
149                        throw (IllegalStateException)new IllegalStateException("Unable to parse geronimo-dependency.xml file in " + url).initCause(e);
150                    } catch (SAXException e) {
151                        throw (IllegalStateException)new IllegalStateException("Unable to parse geronimo-dependency.xml file in " + url).initCause(e);
152                    }
153                }
154            } finally {
155                if (is != null) {
156                    try {
157                        is.close();
158                    } catch (IOException ignore) {
159                        // ignore
160                    }
161                }
162            }
163            return dependencies;
164        }
165    
166        private String getString(Element dependencyElement, String childName) {
167            NodeList children = dependencyElement.getElementsByTagNameNS(NAMESPACE, childName);
168            if (children == null || children.getLength() == 0) {
169            return null;
170            }
171            String value = "";
172            NodeList text = children.item(0).getChildNodes();
173            for (int t = 0; t < text.getLength(); t++) {
174                Node n = text.item(t);
175                if (n.getNodeType() == Node.TEXT_NODE) {
176                    value += n.getNodeValue();
177                }
178            }
179            return value.trim();
180        }
181    
182        public void setTypeHandler(String type, ArtifactTypeHandler handler) {
183            typeHandlers.put(type, handler);
184        }
185    
186        public void copyToRepository(File source, Artifact destination, FileWriteMonitor monitor) throws IOException {
187            if(!destination.isResolved()) {
188                throw new IllegalArgumentException("Artifact "+destination+" is not fully resolved");
189            }
190            if (!source.exists() || !source.canRead() || source.isDirectory()) {
191                throw new IllegalArgumentException("Cannot read source file at " + source.getAbsolutePath());
192            }
193            int size = 0;
194            try {
195                ZipFile zip = new ZipFile(source);
196                for (Enumeration entries=zip.entries(); entries.hasMoreElements();) {
197                    ZipEntry entry = (ZipEntry)entries.nextElement();
198                    size += entry.getSize();
199                }
200            } catch (ZipException ze) {
201                    size = (int)source.length();
202            }
203            FileInputStream is = new FileInputStream(source);
204            try {
205                copyToRepository(is, size, destination, monitor);
206            } finally {
207                try {
208                    is.close();
209                } catch (IOException ignored) {
210                    // ignored
211                }
212            }
213        }
214    
215        public void copyToRepository(InputStream source, int size, Artifact destination, FileWriteMonitor monitor) throws IOException {
216            if(!destination.isResolved()) {
217                throw new IllegalArgumentException("Artifact "+destination+" is not fully resolved");
218            }
219            // is this a writable repository
220            if (!rootFile.canWrite()) {
221                throw new IllegalStateException("This repository is not writable: " + rootFile.getAbsolutePath() + ")");
222            }
223    
224            // where are we going to install the file
225            File location = getLocation(destination);
226    
227            // assure that there isn't already a file installed at the specified location
228            if (location.exists()) {
229                throw new IllegalArgumentException("Destination " + location.getAbsolutePath() + " already exists!");
230            }
231    
232            ArtifactTypeHandler typeHandler = (ArtifactTypeHandler) typeHandlers.get(destination.getType());
233            if (typeHandler == null) typeHandler = DEFAULT_TYPE_HANDLER;
234            typeHandler.install(source, size, destination, monitor, location);
235            
236            if (destination.getType().equalsIgnoreCase("car")) {
237                log.debug("Installed module configuration; id=" + destination + "; location=" + location);
238            }
239        }
240    }