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