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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
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<String, ArtifactTypeHandler> typeHandlers = new HashMap<String, ArtifactTypeHandler>();
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<Artifact> getDependencies(Artifact artifact) {
077            if(!artifact.isResolved()) {
078                throw new IllegalArgumentException("Artifact "+artifact+" is not fully resolved");
079            }
080            LinkedHashSet<Artifact> dependencies = new LinkedHashSet<Artifact>();
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            ZipFile zip = null;
164            try {
165                zip = new ZipFile(source);
166                for (Enumeration entries=zip.entries(); entries.hasMoreElements();) {
167                    ZipEntry entry = (ZipEntry)entries.nextElement();
168                    size += entry.getSize();
169                }
170            } catch (ZipException ze) {
171                size = (int)source.length();
172            } finally {
173                if (zip != null) {
174                    zip.close();
175                }
176            }
177            FileInputStream is = new FileInputStream(source);
178            try {
179                copyToRepository(is, size, destination, monitor);
180            } finally {
181                try {
182                    is.close();
183                } catch (IOException ignored) {
184                    // ignored
185                }
186            }
187        }
188    
189        public void copyToRepository(InputStream source, int size, Artifact destination, FileWriteMonitor monitor) throws IOException {
190            if(!destination.isResolved()) {
191                throw new IllegalArgumentException("Artifact "+destination+" is not fully resolved");
192            }
193            // is this a writable repository
194            if (!rootFile.canWrite()) {
195                throw new IllegalStateException("This repository is not writable: " + rootFile.getAbsolutePath() + ")");
196            }
197    
198            // where are we going to install the file
199            File location = getLocation(destination);
200    
201            // assure that there isn't already a file installed at the specified location
202            if (location.exists()) {
203                throw new IllegalArgumentException("Destination " + location.getAbsolutePath() + " already exists!");
204            }
205    
206            ArtifactTypeHandler typeHandler = typeHandlers.get(destination.getType());
207            if (typeHandler == null) typeHandler = DEFAULT_TYPE_HANDLER;
208            typeHandler.install(source, size, destination, monitor, location);
209            
210            if (destination.getType().equalsIgnoreCase("car")) {
211                log.debug("Installed module configuration; id=" + destination + "; location=" + location);
212            }
213        }
214    }