View Javadoc

1   /**
2    *
3    * Copyright 2005 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.geronimo.deployment.plugin;
18  
19  import java.io.BufferedReader;
20  import java.io.File;
21  import java.io.FileReader;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.io.Reader;
25  import java.util.Collection;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Stack;
29  import java.util.jar.JarEntry;
30  import java.util.jar.JarFile;
31  import javax.enterprise.deploy.spi.TargetModuleID;
32  import javax.xml.parsers.ParserConfigurationException;
33  import javax.xml.parsers.SAXParser;
34  import javax.xml.parsers.SAXParserFactory;
35  import org.apache.geronimo.common.DeploymentException;
36  import org.apache.geronimo.common.FileUtils;
37  import org.apache.geronimo.kernel.repository.Artifact;
38  import org.apache.geronimo.kernel.repository.Version;
39  import org.apache.geronimo.kernel.util.XmlUtil;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.xml.sax.Attributes;
43  import org.xml.sax.InputSource;
44  import org.xml.sax.SAXException;
45  import org.xml.sax.helpers.DefaultHandler;
46  
47  /**
48   * Knows how to suck a Config ID out of a module and/or plan
49   *
50   * @version $Rev: 437623 $ $Date: 2006-08-28 02:48:23 -0700 (Mon, 28 Aug 2006) $
51   */
52  public class ConfigIDExtractor {
53  
54      private static final Log log = LogFactory.getLog(ConfigIDExtractor.class);
55  
56      /**
57       * Attempt to calculate the Geronimo ModuleID for a J2EE application
58       * module.
59       *
60       * Given a File representing an archive (which may be a JAR file or a
61       * directory laid out like a JAR file), identify it's J2EE module type
62       * based on which (if any) deployment descriptor is present, and then look
63       * for a Geronimo deployment plan in the usual place, and if one is found,
64       * retrieve the configId from the Geronimo deployment plan.
65       *
66       * todo: Handle Spring and other weird deployment types?
67       *
68       * @param module A Jar file or directory representing a J2EE module
69       * @return The configId in the Geronimo deployment plan for this module,
70       *         or null if no Geronimo deployment plan was identified.
71       */
72      public static String extractModuleIdFromArchive(File module) throws IOException, DeploymentException {
73          if(!module.canRead()) {
74              throw new DeploymentException("Not a readable file ("+module.getAbsolutePath()+")");
75          }
76          if(module.isDirectory()) {
77              File target;
78              if(new File(module, "WEB-INF/web.xml").canRead()) {
79                  target = new File(module, "WEB-INF/geronimo-web.xml");
80              } else if(new File(module, "META-INF/application.xml").canRead()) {
81                  target = new File(module, "META-INF/geronimo-application.xml");
82              } else if(new File(module, "META-INF/ejb-jar.xml").canRead()) {
83                  target = new File(module, "META-INF/openejb-jar.xml");
84              } else if(new File(module, "META-INF/ra.xml").canRead()) {
85                  target = new File(module, "META-INF/geronimo-ra.xml");
86              } else if(new File(module, "META-INF/application-client.xml").canRead()) {
87                  target = new File(module, "META-INF/geronimo-application-client.xml");
88              } else {
89                  target = new File(module, "META-INF/geronimo-service.xml");
90              }
91              if(target.canRead()) {
92                  Reader in = new BufferedReader(new FileReader(target));
93                  String name = extractModuleIdFromPlan(in);
94                  if(name != null) {
95                      Artifact artifact = Artifact.create(name);
96                      if(artifact.getArtifactId() == null) {
97                          name = new Artifact(artifact.getGroupId(), module.getName(), artifact.getVersion(), artifact.getType()).toString();
98                      }
99                  }
100                 return name;
101             }
102         } else {
103             if(!FileUtils.isZipFile(module)) {
104                 throw new DeploymentException(module.getAbsolutePath()+" is neither a JAR file nor a directory!");
105             }
106             JarFile input = new JarFile(module);
107             //todo: instead of looking for specific file names here, do something generic.
108             //      Perhaps load a DConfigBeanRoot and look for a configId property on the first child,
109             //      though that would probably be a little heavyweight.
110             try {
111                 JarEntry entry;
112                 if(input.getJarEntry("WEB-INF/web.xml") != null) {
113                     entry = input.getJarEntry("WEB-INF/geronimo-web.xml");
114                 } else if(input.getJarEntry("META-INF/application.xml") != null) {
115                     entry = input.getJarEntry("META-INF/geronimo-application.xml");
116                 } else if(input.getJarEntry("META-INF/ejb-jar.xml") != null) {
117                     entry = input.getJarEntry("META-INF/openejb-jar.xml");
118                 } else if(input.getJarEntry("META-INF/ra.xml") != null) {
119                     entry = input.getJarEntry("META-INF/geronimo-ra.xml");
120                 } else if(input.getJarEntry("META-INF/application-client.xml") != null) {
121                     entry = input.getJarEntry("META-INF/geronimo-application-client.xml");
122                 } else {
123                     entry = input.getJarEntry("META-INF/geronimo-service.xml");
124                 }
125                 if(entry != null) {
126                     Reader in = new BufferedReader(new InputStreamReader(input.getInputStream(entry)));
127                     String name = extractModuleIdFromPlan(in);
128                     if(name != null) {
129                         Artifact artifact = Artifact.create(name);
130                         if(artifact.getArtifactId() == null) {
131                             name = new Artifact(artifact.getGroupId(), module.getName(), artifact.getVersion(), artifact.getType()).toString();
132                         }
133                     }
134                     return name;
135                 }
136             } finally {
137                 input.close();
138             }
139         }
140         return null;
141     }
142 
143     /**
144      * Attempt to calculate the Geronimo ModuleID for a Geronimo deployment
145      * plan.
146      *
147      * @param plan A Geronimo deployment plan (which must be an XML file).
148      * @return The configId in the Geronimo deployment plan for this module.
149      */
150     public static String extractModuleIdFromPlan(File plan) throws IOException {
151         if(plan.isDirectory() || !plan.canRead()) {
152             throw new IllegalArgumentException(plan.getAbsolutePath()+" is not a readable XML file!");
153         }
154         Reader in = new BufferedReader(new FileReader(plan));
155         return extractModuleIdFromPlan(in);
156     }
157 
158     /**
159      * Given a list of all available TargetModuleIDs and the name of a module,
160      * find the TargetModuleIDs that represent that module.
161      *
162      * @param allModules  The list of all available modules
163      * @param name        The module name to search for
164      * @param fromPlan    Should be true if the module name was loaded from a
165      *                    deployment plan (thus no group means the default
166      *                    group) or false if the module name was provided by
167      *                    the user (thus no group means any group).
168      *
169      * @throws DeploymentException If no TargetModuleIDs have that module.
170      */
171     public static Collection identifyTargetModuleIDs(TargetModuleID[] allModules, String name, boolean fromPlan) throws DeploymentException {
172         List list = new LinkedList();
173         int pos;
174         if((pos = name.indexOf('|')) > -1) {
175             String target = name.substring(0, pos);
176             String module = name.substring(pos+1);
177             Artifact artifact = Artifact.create(module);
178             artifact = new Artifact(artifact.getGroupId() == null && fromPlan ? Artifact.DEFAULT_GROUP_ID : artifact.getGroupId(),
179                     artifact.getArtifactId(), fromPlan ? (Version)null : artifact.getVersion(), artifact.getType());
180             // First pass: exact match
181             for(int i=0; i<allModules.length; i++) {
182                 if(allModules[i].getTarget().getName().equals(target) && artifact.matches(Artifact.create(allModules[i].getModuleID()))) {
183                     list.add(allModules[i]);
184                 }
185             }
186         }
187         if(!list.isEmpty()) {
188             return list;
189         }
190         // second pass: module matches
191         Artifact artifact;
192         if (name.indexOf("/") > -1) {
193             artifact = Artifact.create(name);
194             artifact = new Artifact(artifact.getGroupId() == null && fromPlan ? Artifact.DEFAULT_GROUP_ID : artifact.getGroupId(),
195                     artifact.getArtifactId(), fromPlan ? (Version)null : artifact.getVersion(), artifact.getType());
196         } else {
197             artifact = new Artifact(fromPlan ? Artifact.DEFAULT_GROUP_ID : null, name, (Version)null, null);
198         }
199         for(int i = 0; i < allModules.length; i++) {
200             if(artifact.matches(Artifact.create(allModules[i].getModuleID()))) {
201                 list.add(allModules[i]);
202             }
203         }
204         if(list.isEmpty()) {
205             throw new DeploymentException(name+" does not appear to be a the name of a module " +
206                     "available on the selected server. Perhaps it has already been " +
207                     "stopped or undeployed?  If you're trying to specify a " +
208                     "TargetModuleID, use the syntax TargetName|ModuleName instead. " +
209                     "If you're not sure what's running, try the list-modules command.");
210         }
211         return list;
212     }
213 
214     private static String extractModuleIdFromPlan(Reader plan) throws IOException {
215         SAXParserFactory factory = XmlUtil.newSAXParserFactory();
216         factory.setNamespaceAware(true);
217         factory.setValidating(false);
218         try {
219             SAXParser parser = factory.newSAXParser();
220             ConfigIdHandler handler = new ConfigIdHandler();
221             parser.parse(new InputSource(plan), handler);
222             if(handler.formatIs10) {
223                 log.warn("Geronimo deployment plan uses Geronimo 1.0 syntax.  Please update to Geronimo 1.1 syntax when possible.");
224             }
225             return handler.configId;
226         } catch (ParserConfigurationException e) {
227             throw new IOException("Unable to read plan: "+e.getMessage());
228         } catch (SAXException e) {
229             throw new IOException("Unable to read plan: "+e.getMessage());
230         } finally {
231             plan.close();
232         }
233     }
234 
235     /**
236      * Try to determine whether a file is a JAR File (or, at least, a ZIP file).
237      *
238      * @deprecated See org.apache.geronimo.common.FileUtils.isJarFile
239      */
240     public static boolean isJarFile(File file) throws DeploymentException {
241         try {
242             return FileUtils.isZipFile(file);
243         } catch (IOException e) {
244             throw new DeploymentException("Unable to read file "+file.getAbsolutePath());
245         }
246     }
247 
248     private static class ConfigIdHandler extends DefaultHandler {
249         private String configId;
250         private boolean inConfigId;
251         private String groupId = "", artifactId = "", version = "", type = "";
252         private String inElement = null;
253         private Stack parent = new Stack();
254         private boolean formatIs10 = false;
255         private String defaultType;
256 
257         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
258             if(defaultType == null && uri != null && !uri.equals("")) {
259                 setDefaultType(uri);
260             }
261             if(inConfigId) {
262                 if(localName.equals("groupId") || localName.equals("artifactId") || localName.equals("version") || localName.equals("type")) {
263                     inElement = localName;
264                 }
265             } else {
266                 if(parent.size() == 2 && localName.equals("moduleId")) {
267                     inConfigId = true; // only document/environment/configId, not e.g. configId in nested plan in EAR
268                 } else {
269                     if(parent.size() == 0 && attributes.getIndex("moduleId") > -1) {
270                         configId = attributes.getValue("moduleId");
271                         formatIs10 = true;
272                     }
273                 }
274             }
275             parent.push(localName);
276         }
277 
278         private void setDefaultType(String namespace) {
279             if(namespace.indexOf("web") > -1) {
280                 defaultType = "war";
281             } else if(namespace.indexOf("openejb") > -1) {
282                 defaultType = "jar";
283             } else if(namespace.indexOf("connector") > -1) {
284                 defaultType = "rar";
285             } else if(namespace.indexOf("application-client") > -1) {
286                 defaultType = "jar";
287             } else if(namespace.indexOf("application") > -1) {
288                 defaultType = "ear";
289             } else {
290                 defaultType = "car";
291             }
292         }
293 
294         public void characters(char ch[], int start, int length) throws SAXException {
295             if(inElement != null) {
296                 formatIs10 = false;
297                 if(inElement.equals("groupId")) groupId += new String(ch, start, length);
298                 else if(inElement.equals("artifactId")) artifactId += new String(ch, start, length);
299                 else if(inElement.equals("version")) version += new String(ch, start, length);
300                 else if(inElement.equals("type")) type += new String(ch, start, length);
301             }
302         }
303 
304         public void endElement(String uri, String localName, String qName) throws SAXException {
305             inElement = null;
306             if(inConfigId && localName.equals("moduleId")) {
307                 inConfigId = false;
308             }
309             if(parent.peek().equals(localName)) {
310                 parent.pop();
311             } else {
312                 throw new IllegalStateException("End of "+localName+" but expecting "+parent.peek());
313             }
314         }
315 
316         public void endDocument() throws SAXException {
317             if(!formatIs10) {
318                 if(type.equals("") && defaultType != null) {
319                     type = defaultType;
320                 }
321                 configId = groupId+"/"+artifactId+"/"+version+"/"+type;
322             }
323             if(configId.equals("///")) {
324                 configId = null;
325             }
326         }
327     }
328 }