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: */package org.apache.geronimo.deployment.plugin;
017:
018: import java.io.BufferedReader;
019: import java.io.File;
020: import java.io.FileReader;
021: import java.io.IOException;
022: import java.io.InputStreamReader;
023: import java.io.Reader;
024: import java.util.Collection;
025: import java.util.LinkedList;
026: import java.util.List;
027: import java.util.Stack;
028: import java.util.jar.JarEntry;
029: import java.util.jar.JarFile;
030: import javax.enterprise.deploy.spi.TargetModuleID;
031: import javax.xml.parsers.ParserConfigurationException;
032: import javax.xml.parsers.SAXParser;
033: import javax.xml.parsers.SAXParserFactory;
034: import org.apache.geronimo.common.DeploymentException;
035: import org.apache.geronimo.common.FileUtils;
036: import org.apache.geronimo.kernel.repository.Artifact;
037: import org.apache.geronimo.kernel.repository.Version;
038: import org.apache.geronimo.kernel.util.XmlUtil;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041: import org.xml.sax.Attributes;
042: import org.xml.sax.InputSource;
043: import org.xml.sax.SAXException;
044: import org.xml.sax.helpers.DefaultHandler;
045:
046: /**
047: * Knows how to suck a Config ID out of a module and/or plan
048: *
049: * @version $Rev: 549455 $ $Date: 2007-06-21 05:12:27 -0700 (Thu, 21 Jun 2007) $
050: */
051: public class ConfigIDExtractor {
052:
053: private static final Log log = LogFactory
054: .getLog(ConfigIDExtractor.class);
055:
056: /**
057: * Attempt to calculate the Geronimo ModuleID for a J2EE application
058: * module.
059: *
060: * Given a File representing an archive (which may be a JAR file or a
061: * directory laid out like a JAR file), identify it's J2EE module type
062: * based on which (if any) deployment descriptor is present, and then look
063: * for a Geronimo deployment plan in the usual place, and if one is found,
064: * retrieve the configId from the Geronimo deployment plan.
065: *
066: * todo: Handle Spring and other weird deployment types?
067: *
068: * @param module A Jar file or directory representing a J2EE module
069: * @return The configId in the Geronimo deployment plan for this module,
070: * or null if no Geronimo deployment plan was identified.
071: */
072: public static String extractModuleIdFromArchive(File module)
073: throws IOException, DeploymentException {
074: if (!module.canRead()) {
075: throw new DeploymentException("Not a readable file ("
076: + module.getAbsolutePath() + ")");
077: }
078: if (module.isDirectory()) {
079: File target;
080: if (new File(module, "WEB-INF/web.xml").canRead()) {
081: target = new File(module, "WEB-INF/geronimo-web.xml");
082: } else if (new File(module, "META-INF/application.xml")
083: .canRead()) {
084: target = new File(module,
085: "META-INF/geronimo-application.xml");
086: } else if (new File(module, "META-INF/ejb-jar.xml")
087: .canRead()) {
088: target = new File(module, "META-INF/openejb-jar.xml");
089: } else if (new File(module, "META-INF/ra.xml").canRead()) {
090: target = new File(module, "META-INF/geronimo-ra.xml");
091: } else if (new File(module,
092: "META-INF/application-client.xml").canRead()) {
093: target = new File(module,
094: "META-INF/geronimo-application-client.xml");
095: } else {
096: target = new File(module,
097: "META-INF/geronimo-service.xml");
098: }
099: if (target.canRead()) {
100: Reader in = new BufferedReader(new FileReader(target));
101: String name = extractModuleIdFromPlan(in);
102: if (name != null) {
103: Artifact artifact = Artifact.create(name);
104: if (artifact.getArtifactId() == null) {
105: name = new Artifact(artifact.getGroupId(),
106: module.getName(),
107: artifact.getVersion(), artifact
108: .getType()).toString();
109: }
110: }
111: return name;
112: }
113: } else {
114: if (!FileUtils.isZipFile(module)) {
115: throw new DeploymentException(module.getAbsolutePath()
116: + " is neither a JAR file nor a directory!");
117: }
118: JarFile input = new JarFile(module);
119: //todo: instead of looking for specific file names here, do something generic.
120: // Perhaps load a DConfigBeanRoot and look for a configId property on the first child,
121: // though that would probably be a little heavyweight.
122: try {
123: JarEntry entry;
124: if (input.getJarEntry("WEB-INF/web.xml") != null) {
125: entry = input
126: .getJarEntry("WEB-INF/geronimo-web.xml");
127: } else if (input
128: .getJarEntry("META-INF/application.xml") != null) {
129: entry = input
130: .getJarEntry("META-INF/geronimo-application.xml");
131: } else if (input.getJarEntry("META-INF/ejb-jar.xml") != null) {
132: entry = input
133: .getJarEntry("META-INF/openejb-jar.xml");
134: } else if (input.getJarEntry("META-INF/ra.xml") != null) {
135: entry = input
136: .getJarEntry("META-INF/geronimo-ra.xml");
137: } else if (input
138: .getJarEntry("META-INF/application-client.xml") != null) {
139: entry = input
140: .getJarEntry("META-INF/geronimo-application-client.xml");
141: } else {
142: entry = input
143: .getJarEntry("META-INF/geronimo-service.xml");
144: }
145: if (entry != null) {
146: Reader in = new BufferedReader(
147: new InputStreamReader(input
148: .getInputStream(entry)));
149: String name = extractModuleIdFromPlan(in);
150: if (name != null) {
151: Artifact artifact = Artifact.create(name);
152: if (artifact.getArtifactId() == null) {
153: name = new Artifact(artifact.getGroupId(),
154: module.getName(), artifact
155: .getVersion(), artifact
156: .getType()).toString();
157: }
158: }
159: return name;
160: }
161: } finally {
162: input.close();
163: }
164: }
165: return null;
166: }
167:
168: /**
169: * Attempt to calculate the Geronimo ModuleID for a Geronimo deployment
170: * plan.
171: *
172: * @param plan A Geronimo deployment plan (which must be an XML file).
173: * @return The configId in the Geronimo deployment plan for this module.
174: */
175: public static String extractModuleIdFromPlan(File plan)
176: throws IOException {
177: if (plan.isDirectory() || !plan.canRead()) {
178: throw new IllegalArgumentException(plan.getAbsolutePath()
179: + " is not a readable XML file!");
180: }
181: Reader in = new BufferedReader(new FileReader(plan));
182: return extractModuleIdFromPlan(in);
183: }
184:
185: /**
186: * Given a list of all available TargetModuleIDs and the name of a module,
187: * find the TargetModuleIDs that represent that module.
188: *
189: * @param allModules The list of all available modules
190: * @param name The module name to search for
191: * @param fromPlan Should be true if the module name was loaded from a
192: * deployment plan (thus no group means the default
193: * group) or false if the module name was provided by
194: * the user (thus no group means any group).
195: *
196: * @throws DeploymentException If no TargetModuleIDs have that module.
197: */
198: public static Collection identifyTargetModuleIDs(
199: TargetModuleID[] allModules, String name, boolean fromPlan)
200: throws DeploymentException {
201: List list = new LinkedList();
202: int pos;
203: if ((pos = name.indexOf('|')) > -1) {
204: String target = name.substring(0, pos);
205: String module = name.substring(pos + 1);
206: Artifact artifact = Artifact.create(module);
207: artifact = new Artifact(artifact.getGroupId() == null
208: && fromPlan ? Artifact.DEFAULT_GROUP_ID : artifact
209: .getGroupId(), artifact.getArtifactId(),
210: fromPlan ? (Version) null : artifact.getVersion(),
211: artifact.getType());
212: // First pass: exact match
213: for (int i = 0; i < allModules.length; i++) {
214: if (allModules[i].getTarget().getName().equals(target)
215: && artifact.matches(Artifact
216: .create(allModules[i].getModuleID()))) {
217: list.add(allModules[i]);
218: }
219: }
220: }
221: if (!list.isEmpty()) {
222: return list;
223: }
224: // second pass: module matches
225: Artifact artifact;
226: if (name.indexOf("/") > -1) {
227: artifact = Artifact.create(name);
228: artifact = new Artifact(artifact.getGroupId() == null
229: && fromPlan ? Artifact.DEFAULT_GROUP_ID : artifact
230: .getGroupId(), artifact.getArtifactId(),
231: fromPlan ? (Version) null : artifact.getVersion(),
232: artifact.getType());
233: } else {
234: artifact = new Artifact(
235: fromPlan ? Artifact.DEFAULT_GROUP_ID : null, name,
236: (Version) null, null);
237: }
238: for (int i = 0; i < allModules.length; i++) {
239: if (artifact.matches(Artifact.create(allModules[i]
240: .getModuleID()))) {
241: list.add(allModules[i]);
242: }
243: }
244: if (list.isEmpty()) {
245: throw new DeploymentException(
246: name
247: + " does not appear to be a the name of a module "
248: + "available on the selected server. Perhaps it has already been "
249: + "stopped or undeployed? If you're trying to specify a "
250: + "TargetModuleID, use the syntax TargetName|ModuleName instead. "
251: + "If you're not sure what's running, try the list-modules command.");
252: }
253: return list;
254: }
255:
256: private static String extractModuleIdFromPlan(Reader plan)
257: throws IOException {
258: SAXParserFactory factory = XmlUtil.newSAXParserFactory();
259: factory.setNamespaceAware(true);
260: factory.setValidating(false);
261: try {
262: SAXParser parser = factory.newSAXParser();
263: ConfigIdHandler handler = new ConfigIdHandler();
264: parser.parse(new InputSource(plan), handler);
265: if (handler.formatIs10) {
266: log
267: .warn("Geronimo deployment plan uses Geronimo 1.0 syntax. Please update to Geronimo 1.1 syntax when possible.");
268: }
269: return handler.configId;
270: } catch (ParserConfigurationException e) {
271: throw (IOException) new IOException("Unable to read plan: "
272: + e.getMessage()).initCause(e);
273: } catch (SAXException e) {
274: throw (IOException) new IOException("Unable to read plan: "
275: + e.getMessage()).initCause(e);
276: } finally {
277: plan.close();
278: }
279: }
280:
281: /**
282: * Try to determine whether a file is a JAR File (or, at least, a ZIP file).
283: *
284: * @deprecated See org.apache.geronimo.common.FileUtils.isJarFile
285: */
286: public static boolean isJarFile(File file)
287: throws DeploymentException {
288: try {
289: return FileUtils.isZipFile(file);
290: } catch (IOException e) {
291: throw new DeploymentException("Unable to read file "
292: + file.getAbsolutePath(), e);
293: }
294: }
295:
296: private static class ConfigIdHandler extends DefaultHandler {
297: private String configId;
298: private boolean inConfigId;
299: private String groupId = "", artifactId = "", version = "",
300: type = "";
301: private String inElement = null;
302: private Stack parent = new Stack();
303: private boolean formatIs10 = false;
304: private String defaultType;
305:
306: public void startElement(String uri, String localName,
307: String qName, Attributes attributes)
308: throws SAXException {
309: if (defaultType == null && uri != null && !uri.equals("")) {
310: setDefaultType(uri);
311: }
312: if (inConfigId) {
313: if (localName.equals("groupId")
314: || localName.equals("artifactId")
315: || localName.equals("version")
316: || localName.equals("type")) {
317: inElement = localName;
318: }
319: } else {
320: if (parent.size() == 2 && localName.equals("moduleId")) {
321: inConfigId = true; // only document/environment/configId, not e.g. configId in nested plan in EAR
322: } else {
323: if (parent.size() == 0
324: && attributes.getIndex("moduleId") > -1) {
325: configId = attributes.getValue("moduleId");
326: formatIs10 = true;
327: }
328: }
329: }
330: parent.push(localName);
331: }
332:
333: private void setDefaultType(String namespace) {
334: if (namespace.indexOf("web") > -1) {
335: defaultType = "war";
336: } else if (namespace.indexOf("openejb") > -1) {
337: defaultType = "jar";
338: } else if (namespace.indexOf("connector") > -1) {
339: defaultType = "rar";
340: } else if (namespace.indexOf("application-client") > -1) {
341: defaultType = "jar";
342: } else if (namespace.indexOf("application") > -1) {
343: defaultType = "ear";
344: } else {
345: defaultType = "car";
346: }
347: }
348:
349: public void characters(char ch[], int start, int length)
350: throws SAXException {
351: if (inElement != null) {
352: formatIs10 = false;
353: if (inElement.equals("groupId"))
354: groupId += new String(ch, start, length);
355: else if (inElement.equals("artifactId"))
356: artifactId += new String(ch, start, length);
357: else if (inElement.equals("version"))
358: version += new String(ch, start, length);
359: else if (inElement.equals("type"))
360: type += new String(ch, start, length);
361: }
362: }
363:
364: public void endElement(String uri, String localName,
365: String qName) throws SAXException {
366: inElement = null;
367: if (inConfigId && localName.equals("moduleId")) {
368: inConfigId = false;
369: }
370: if (parent.peek().equals(localName)) {
371: parent.pop();
372: } else {
373: throw new IllegalStateException("End of " + localName
374: + " but expecting " + parent.peek());
375: }
376: }
377:
378: public void endDocument() throws SAXException {
379: if (!formatIs10) {
380: if (type.equals("") && defaultType != null) {
381: type = defaultType;
382: }
383: configId = groupId + "/" + artifactId + "/" + version
384: + "/" + type;
385: }
386: if (configId.equals("///")) {
387: configId = null;
388: }
389: }
390: }
391: }
|