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
108
109
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
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
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;
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 }