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.kernel.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.URL;
025    import java.net.URLClassLoader;
026    import java.util.ArrayList;
027    import java.util.Arrays;
028    import java.util.Enumeration;
029    import java.util.HashMap;
030    import java.util.LinkedHashSet;
031    import java.util.Map;
032    import java.util.regex.Matcher;
033    import java.util.regex.Pattern;
034    import java.util.zip.ZipEntry;
035    import java.util.zip.ZipException;
036    import java.util.zip.ZipFile;
037    
038    import javax.xml.parsers.DocumentBuilderFactory;
039    import javax.xml.parsers.ParserConfigurationException;
040    
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.apache.geronimo.kernel.util.InputUtils;
044    import org.apache.geronimo.kernel.util.XmlUtil;
045    import org.w3c.dom.Document;
046    import org.w3c.dom.Element;
047    import org.w3c.dom.Node;
048    import org.w3c.dom.NodeList;
049    import org.xml.sax.InputSource;
050    import org.xml.sax.SAXException;
051    
052    /**
053     * @version $Rev: 506425 $ $Date: 2007-02-12 22:49:46 +1100 (Mon, 12 Feb 2007) $
054     */
055    public abstract class AbstractRepository implements WriteableRepository {
056        protected static final Log log = LogFactory.getLog(AbstractRepository.class);
057        private final static ArtifactTypeHandler DEFAULT_TYPE_HANDLER = new CopyArtifactTypeHandler();
058        private final static Pattern ILLEGAL_CHARS = Pattern.compile("[\\.]{2}|[()<>,;:\\\\/\"\']");
059        protected final File rootFile;
060        private final Map<String, ArtifactTypeHandler> typeHandlers = new HashMap<String, ArtifactTypeHandler>();
061    
062        public AbstractRepository(File rootFile) {
063            if (rootFile == null) throw new NullPointerException("root is null");
064    
065            if (!rootFile.exists() || !rootFile.isDirectory() || !rootFile.canRead()) {
066                throw new IllegalStateException("Maven2Repository must have a root that's a valid readable directory (not " + rootFile.getAbsolutePath() + ")");
067            }
068    
069            this.rootFile = rootFile;
070            log.debug("Repository root is " + rootFile.getAbsolutePath());
071    
072            typeHandlers.put("car", new UnpackArtifactTypeHandler());
073        }
074    
075        public boolean contains(Artifact artifact) {
076            // Note: getLocation(artifact) does an artifact.isResolved() check - no need to do it here.
077            File location = getLocation(artifact);
078            return location.canRead() && (location.isFile() || new File(location, "META-INF").isDirectory());
079        }
080    
081        private static final String NAMESPACE = "http://geronimo.apache.org/xml/ns/deployment-1.2";
082        public LinkedHashSet<Artifact> getDependencies(Artifact artifact) {
083            if(!artifact.isResolved()) {
084                throw new IllegalArgumentException("Artifact "+artifact+" is not fully resolved");
085            }
086            LinkedHashSet<Artifact> dependencies = new LinkedHashSet<Artifact>();
087            URL url;
088            try {
089                File location = getLocation(artifact);
090                url = location.toURL();
091            } catch (MalformedURLException e) {
092                throw (IllegalStateException)new IllegalStateException("Unable to get URL for dependency " + artifact).initCause(e);
093            }
094            ClassLoader depCL = new URLClassLoader(new URL[]{url}, new ClassLoader() {
095                @Override
096                public URL getResource(String name) {
097                    return null;
098                }
099            });
100            InputStream is = depCL.getResourceAsStream("META-INF/geronimo-dependency.xml");
101            try {
102                if (is != null) {
103                    InputSource in = new InputSource(is);
104                    DocumentBuilderFactory dfactory = XmlUtil.newDocumentBuilderFactory();
105                    dfactory.setNamespaceAware(true);
106                    try {
107                        Document doc = dfactory.newDocumentBuilder().parse(in);
108                        Element root = doc.getDocumentElement();
109                        NodeList configs = root.getElementsByTagNameNS(NAMESPACE, "dependency");
110                        for (int i = 0; i < configs.getLength(); i++) {
111                            Element dependencyElement = (Element) configs.item(i);
112                            String groupId = getString(dependencyElement, "groupId");
113                            String artifactId = getString(dependencyElement, "artifactId");
114                            String version = getString(dependencyElement, "version");
115                            String type = getString(dependencyElement, "type");
116                            if (type == null) {
117                                type = "jar";
118                            }
119                            dependencies.add(new Artifact(groupId, artifactId,  version, type));
120                        }
121                    } catch (IOException e) {
122                        throw (IllegalStateException)new IllegalStateException("Unable to parse geronimo-dependency.xml file in " + url).initCause(e);
123                    } catch (ParserConfigurationException e) {
124                        throw (IllegalStateException)new IllegalStateException("Unable to parse geronimo-dependency.xml file in " + url).initCause(e);
125                    } catch (SAXException e) {
126                        throw (IllegalStateException)new IllegalStateException("Unable to parse geronimo-dependency.xml file in " + url).initCause(e);
127                    }
128                }
129            } finally {
130                if (is != null) {
131                    try {
132                        is.close();
133                    } catch (IOException ignore) {
134                        // ignore
135                    }
136                }
137            }
138            return dependencies;
139        }
140    
141        private String getString(Element dependencyElement, String childName) {
142            NodeList children = dependencyElement.getElementsByTagNameNS(NAMESPACE, childName);
143            if (children == null || children.getLength() == 0) {
144            return null;
145            }
146            String value = "";
147            NodeList text = children.item(0).getChildNodes();
148            for (int t = 0; t < text.getLength(); t++) {
149                Node n = text.item(t);
150                if (n.getNodeType() == Node.TEXT_NODE) {
151                    value += n.getNodeValue();
152                }
153            }
154            return value.trim();
155        }
156    
157        public void setTypeHandler(String type, ArtifactTypeHandler handler) {
158            typeHandlers.put(type, handler);
159        }
160    
161        public void copyToRepository(File source, Artifact destination, FileWriteMonitor monitor) throws IOException {
162    
163            // ensure there are no illegal chars in destination elements
164            InputUtils.validateSafeInput(new ArrayList(Arrays.asList(destination.getGroupId(), destination.getArtifactId(), destination.getVersion().toString(), destination.getType())));
165    
166            if(!destination.isResolved()) {
167                throw new IllegalArgumentException("Artifact "+destination+" is not fully resolved");
168            }
169            if (!source.exists() || !source.canRead() || source.isDirectory()) {
170                throw new IllegalArgumentException("Cannot read source file at " + source.getAbsolutePath());
171            }
172            int size = 0;
173            ZipFile zip = null;
174            try {
175                zip = new ZipFile(source);
176                for (Enumeration entries=zip.entries(); entries.hasMoreElements();) {
177                    ZipEntry entry = (ZipEntry)entries.nextElement();
178                    size += entry.getSize();
179                }
180            } catch (ZipException ze) {
181                size = (int)source.length();
182            } finally {
183                if (zip != null) {
184                    zip.close();
185                }
186            }
187            FileInputStream is = new FileInputStream(source);
188            try {
189                copyToRepository(is, size, destination, monitor);
190            } finally {
191                try {
192                    is.close();
193                } catch (IOException ignored) {
194                    // ignored
195                }
196            }
197        }
198    
199        public void copyToRepository(InputStream source, int size, Artifact destination, FileWriteMonitor monitor) throws IOException {
200            if(!destination.isResolved()) {
201                throw new IllegalArgumentException("Artifact "+destination+" is not fully resolved");
202            }
203            // is this a writable repository
204            if (!rootFile.canWrite()) {
205                throw new IllegalStateException("This repository is not writable: " + rootFile.getAbsolutePath() + ")");
206            }
207    
208            // where are we going to install the file
209            File location = getLocation(destination);
210    
211            // assure that there isn't already a file installed at the specified location
212            if (location.exists()) {
213                throw new IllegalArgumentException("Destination " + location.getAbsolutePath() + " already exists!");
214            }
215    
216            ArtifactTypeHandler typeHandler = typeHandlers.get(destination.getType());
217            if (typeHandler == null) typeHandler = DEFAULT_TYPE_HANDLER;
218            typeHandler.install(source, size, destination, monitor, location);
219            
220            if (destination.getType().equalsIgnoreCase("car")) {
221                log.debug("Installed module configuration; id=" + destination + "; location=" + location);
222            }
223        }
224    }