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 }