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 }