001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.deployment;
023:
024: import java.io.File;
025: import java.io.FilenameFilter;
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.net.MalformedURLException;
029: import java.net.URL;
030: import java.security.Policy;
031: import java.util.Enumeration;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.jar.JarEntry;
035: import java.util.jar.JarFile;
036:
037: import javax.management.ObjectName;
038: import javax.security.jacc.PolicyConfiguration;
039: import javax.security.jacc.PolicyConfigurationFactory;
040:
041: import org.jboss.metadata.MetaData;
042: import org.jboss.metadata.XmlFileLoader;
043: import org.jboss.mx.loading.LoaderRepositoryFactory;
044: import org.jboss.mx.loading.LoaderRepositoryFactory.LoaderRepositoryConfig;
045: import org.jboss.mx.util.MBeanProxyExt;
046: import org.jboss.system.ServiceControllerMBean;
047: import org.jboss.util.file.JarUtils;
048: import org.w3c.dom.Element;
049:
050: /**
051: * Enterprise Archive Deployer.
052: *
053: * @jmx:mbean name="jboss.j2ee:service=EARDeployer"
054: * extends="org.jboss.deployment.SubDeployerMBean"
055: *
056: * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
057: * @author Scott.Stark@jboss.org
058: * @version $Revision: 60683 $
059: */
060: public class EARDeployer extends SubDeployerSupport implements
061: EARDeployerMBean {
062: /** The suffixes we accept, along with their relative order */
063: private static final String[] DEFAULT_ENHANCED_SUFFIXES = new String[] { "650:.ear" };
064:
065: private ServiceControllerMBean serviceController;
066: /** */
067: private boolean isolated = false;
068: /** */
069: private boolean callByValue = false;
070: /** Should the default library-directory of lib be assumed if no explicit library-directory was specified */
071: private boolean enablelibDirectoryByDefault = true;
072:
073: /**
074: * Default CTOR
075: */
076: public EARDeployer() {
077: setEnhancedSuffixes(DEFAULT_ENHANCED_SUFFIXES);
078: }
079:
080: /**
081: * @jmx:managed-attribute
082: * @return whether ear deployments should be isolated
083: */
084: public boolean isIsolated() {
085: return isolated;
086: }
087:
088: /**
089: * @jmx:managed-attribute
090: * @param isolated whether ear deployments should be isolated
091: */
092: public void setIsolated(boolean isolated) {
093: this .isolated = isolated;
094: }
095:
096: /**
097: * @jmx:managed-attribute
098: * @return whether ear deployments should be call by value
099: */
100: public boolean isCallByValue() {
101: return callByValue;
102: }
103:
104: /**
105: * @jmx:managed-attribute
106: * @param callByValue whether ear deployments should be call by value
107: */
108: public void setCallByValue(boolean callByValue) {
109: this .callByValue = callByValue;
110: }
111:
112: public boolean isEnablelibDirectoryByDefault() {
113: return enablelibDirectoryByDefault;
114: }
115:
116: public void setEnablelibDirectoryByDefault(
117: boolean enablelibDirectoryByDefault) {
118: this .enablelibDirectoryByDefault = enablelibDirectoryByDefault;
119: }
120:
121: protected void startService() throws Exception {
122: serviceController = (ServiceControllerMBean) MBeanProxyExt
123: .create(ServiceControllerMBean.class,
124: ServiceControllerMBean.OBJECT_NAME, server);
125: super .startService();
126: }
127:
128: public void init(DeploymentInfo di) throws DeploymentException {
129: try {
130: log.info("Init J2EE application: " + di.url);
131:
132: InputStream in = di.localCl
133: .getResourceAsStream("META-INF/application.xml");
134: if (in == null)
135: throw new DeploymentException(
136: "No META-INF/application.xml found");
137:
138: /* Don't require validation of application.xml since an ear may
139: just contain a jboss sar specified in the jboss-app.xml descriptor.
140: */
141: XmlFileLoader xfl = new XmlFileLoader(false);
142: J2eeApplicationMetaData metaData = new J2eeApplicationMetaData();
143: Element application = xfl.getDocument(in,
144: "META-INF/application.xml").getDocumentElement();
145: metaData.importXml(application);
146: di.metaData = metaData;
147: in.close();
148:
149: // Check for a jboss-app.xml descriptor
150: Element loader = null;
151: in = di.localCl
152: .getResourceAsStream("META-INF/jboss-app.xml");
153: if (in != null) {
154: // Create a new parser with validation enabled for jboss-app.xml
155: xfl = new XmlFileLoader(true);
156: Element jbossApp = xfl.getDocument(in,
157: "META-INF/jboss-app.xml").getDocumentElement();
158: in.close();
159: // Import module/service archives to metadata
160: metaData.importXml(jbossApp);
161: // Check for a loader-repository for scoping
162: loader = MetaData.getOptionalChild(jbossApp,
163: "loader-repository");
164: }
165: initLoaderRepository(di, loader);
166:
167: // Add any library-directory contents
168: String libraryDirectory = metaData.getLibraryDirectory();
169: // Allow for the default lib unless disabled at the ear deployer level
170: if (libraryDirectory == null
171: && isEnablelibDirectoryByDefault())
172: libraryDirectory = "lib";
173:
174: // resolve the watch
175: if (di.url.getProtocol().equals("file")) {
176: File file = new File(di.url.getFile());
177:
178: // If not directory we watch the package
179: if (!file.isDirectory()) {
180: di.watch = di.url;
181: }
182: // If directory we watch the xml files
183: else {
184: di.watch = new URL(di.url,
185: "META-INF/application.xml");
186: }
187: } else {
188: // We watch the top only, no directory support
189: di.watch = di.url;
190: }
191:
192: // Obtain the sub-deployment list
193: File parentDir = null;
194: HashMap extractedJars = new HashMap();
195:
196: if (di.isDirectory) {
197: parentDir = new File(di.localUrl.getFile());
198: if (libraryDirectory != null
199: && libraryDirectory.length() > 0) {
200: File lib = new File(parentDir, libraryDirectory);
201: URL libURL = lib.toURL();
202: String[] jars = lib.list(new JarFilter());
203: for (int n = 0; jars != null && n < jars.length; n++) {
204: String jar = jars[n];
205: URL libJar = new URL(libURL, jar);
206: di.addLibraryJar(libJar);
207: }
208: }
209: } else {
210: /* Extract each entry so that deployment modules can be processed
211: and any manifest entries referenced by the ear modules are located
212: in the same unpacked directory structure.
213: */
214: String urlPrefix = "jar:" + di.localUrl + "!/";
215: JarFile jarFile = new JarFile(di.localUrl.getFile());
216:
217: // For each entry, test if deployable, if so
218: // extract it and store the related URL in map
219: for (Enumeration e = jarFile.entries(); e
220: .hasMoreElements();) {
221: JarEntry entry = (JarEntry) e.nextElement();
222: String name = entry.getName();
223: try {
224: URL url = new URL(urlPrefix + name);
225: if (metaData.hasModule(name)) {
226: // Obtain a jar url for the nested jar
227: URL nestedURL = JarUtils.extractNestedJar(
228: url, this .tempDeployDir);
229: // and store in it in map
230: extractedJars.put(name, nestedURL);
231: log.debug("Extracted deployable content: "
232: + name);
233: } else if (entry.isDirectory() == false) {
234: URL nestedURL = JarUtils.extractNestedJar(
235: url, this .tempDeployDir);
236: log
237: .debug("Extracted non-deployable content: "
238: + name);
239: // Extract the library-directory
240: if (libraryDirectory != null
241: && libraryDirectory.length() > 0
242: && name
243: .startsWith(libraryDirectory)) {
244: di.addLibraryJar(nestedURL);
245: }
246: }
247: } catch (MalformedURLException mue) {
248: log.warn(
249: "Jar entry invalid. Ignoring: " + name,
250: mue);
251: } catch (IOException ex) {
252: log.warn(
253: "Failed to extract nested jar. Ignoring: "
254: + name, ex);
255: }
256: }
257: }
258:
259: // Create a top level JACC policy for linking app module policies
260: String contextID = di.shortName;
261: PolicyConfigurationFactory pcFactory = PolicyConfigurationFactory
262: .getPolicyConfigurationFactory();
263: PolicyConfiguration pc = pcFactory.getPolicyConfiguration(
264: contextID, true);
265: di.context.put("javax.security.jacc.PolicyConfiguration",
266: pc);
267:
268: // Create subdeployments for the ear modules
269: for (Iterator iter = metaData.getModules(); iter.hasNext();) {
270: J2eeModuleMetaData mod = (J2eeModuleMetaData) iter
271: .next();
272: String fileName = mod.getFileName();
273: if (fileName != null
274: && (fileName = fileName.trim()).length() > 0) {
275: DeploymentInfo sub = null;
276: if (di.isDirectory) {
277: File f = new File(parentDir, fileName);
278: sub = new DeploymentInfo(f.toURL(), di,
279: getServer());
280: } else {
281: // The nested jar url was placed into extractedJars above
282: URL nestedURL = (URL) extractedJars
283: .get(fileName);
284: if (nestedURL == null)
285: throw new DeploymentException(
286: "Failed to find module file: "
287: + fileName);
288: sub = new DeploymentInfo(nestedURL, di,
289: getServer());
290: }
291:
292: // Set the context-root on web modules
293: if (mod.isWeb())
294: sub.webContext = mod.getWebContext();
295:
296: // Set the alternative deployment descriptor if there is one
297: if (mod.alternativeDD != null)
298: sub.alternativeDD = mod.alternativeDD;
299:
300: log.debug("Deployment Info: " + sub
301: + ", isDirectory: " + sub.isDirectory);
302: }
303: }
304:
305: // Check for deployment order style
306: String moduleOrder = metaData.getModuleOrder();
307: if ("strict".equalsIgnoreCase(moduleOrder))
308: di.sortedSubDeployments = true;
309: else {
310: di.sortedSubDeployments = false;
311:
312: if (!"implicit".equalsIgnoreCase(moduleOrder))
313: log
314: .warn("supported values for <module-order> are 'strict' and 'implicit'; currently set to: "
315: + moduleOrder);
316: }
317: } catch (Exception e) {
318: DeploymentException.rethrowAsDeploymentException(
319: "Error in accessing application metadata: ", e);
320: }
321: super .init(di);
322: }
323:
324: public void create(DeploymentInfo di) throws DeploymentException {
325: super .create(di);
326:
327: // Create an MBean for the EAR deployment
328: try {
329: EARDeployment earDeployment = new EARDeployment(di);
330: String name = earDeployment.getJMXName();
331: ObjectName objectName = new ObjectName(name);
332: di.deployedObject = objectName;
333: server.registerMBean(earDeployment, objectName);
334: serviceController.create(di.deployedObject);
335: } catch (Exception e) {
336: DeploymentException.rethrowAsDeploymentException(
337: "Error during create of EARDeployment: " + di.url,
338: e);
339: }
340: }
341:
342: public void start(DeploymentInfo di) throws DeploymentException {
343: super .start(di);
344: try {
345: // Commit the top level policy configuration
346: PolicyConfiguration pc = (PolicyConfiguration) di.context
347: .get("javax.security.jacc.PolicyConfiguration");
348: pc.commit();
349: Policy.getPolicy().refresh();
350: serviceController.start(di.deployedObject);
351: } catch (Exception e) {
352: DeploymentException
353: .rethrowAsDeploymentException(
354: "Error during start of EARDeployment: "
355: + di.url, e);
356: }
357: log.info("Started J2EE application: " + di.url);
358: }
359:
360: public void stop(DeploymentInfo di) throws DeploymentException {
361: try {
362: if (di.deployedObject != null)
363: serviceController.stop(di.deployedObject);
364: } catch (Exception e) {
365: DeploymentException.rethrowAsDeploymentException(
366: "Error during stop of EARDeployment: " + di.url, e);
367: }
368: super .stop(di);
369: }
370:
371: /**
372: * Describe <code>destroy</code> method here.
373: *
374: * @param di a <code>DeploymentInfo</code> value
375: * @exception DeploymentException if an error occurs
376: */
377: public void destroy(DeploymentInfo di) throws DeploymentException {
378: log.info("Undeploying J2EE application, destroy step: "
379: + di.url);
380: try {
381: if (di.deployedObject != null) {
382: serviceController.destroy(di.deployedObject);
383: serviceController.remove(di.deployedObject);
384: }
385: } catch (Exception e) {
386: DeploymentException.rethrowAsDeploymentException(
387: "Error during destroy of EARDeployment: " + di.url,
388: e);
389: }
390: super .destroy(di);
391: log.info("Undeployed J2EE application: " + di.url);
392: }
393:
394: /** Build the ear scoped repository
395: *
396: * @param di the deployment info passed to deploy
397: * @param loader the jboss-app/loader-repository element
398: * @throws Exception
399: */
400: protected void initLoaderRepository(DeploymentInfo di,
401: Element loader) throws Exception {
402: if (loader == null) {
403: if (isolated && di.parent == null) {
404: J2eeApplicationMetaData metaData = (J2eeApplicationMetaData) di.metaData;
405: String name = EARDeployment.getJMXName(metaData, di)
406: + ",extension=LoaderRepository";
407: ObjectName objectName = new ObjectName(name);
408:
409: LoaderRepositoryConfig config = new LoaderRepositoryFactory.LoaderRepositoryConfig();
410: config.repositoryName = objectName;
411: di.setRepositoryInfo(config);
412: }
413: return;
414: }
415:
416: LoaderRepositoryConfig config = LoaderRepositoryFactory
417: .parseRepositoryConfig(loader);
418: di.setRepositoryInfo(config);
419: }
420:
421: /**
422: * Add -ds.xml and -service.xml as legitimate deployables.
423: */
424: protected boolean isDeployable(String name, URL url) {
425: // super.isDeployable() should be enough, now that the list
426: // of supported suffixes is dynamically updated.
427: return super .isDeployable(name, url)
428: || name.endsWith("-ds.xml")
429: || name.endsWith("-service.xml")
430: || name.endsWith(".har");
431: }
432:
433: /** Override the default behavior of looking into the archive for deployables
434: * as only those explicitly listed in the application.xml and jboss-app.xml
435: * should be deployed.
436: *
437: * @param di
438: */
439: protected void processNestedDeployments(DeploymentInfo di) {
440: }
441:
442: static class JarFilter implements FilenameFilter {
443: public boolean accept(File dir, String name) {
444: return name.endsWith(".jar");
445: }
446: }
447: }
|