1 /*
2 * JBoss, the OpenSource J2EE webOS
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 */
7
8 package org.jboss.deployment;
9
10 import java.io.File;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.net.MalformedURLException;
14 import java.net.URL;
15 import java.util.Enumeration;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.jar.JarEntry;
19 import java.util.jar.JarFile;
20
21 import org.jboss.metadata.MetaData;
22 import org.jboss.metadata.XmlFileLoader;
23 import org.jboss.mx.loading.LoaderRepositoryFactory;
24 import org.jboss.mx.loading.LoaderRepositoryFactory.LoaderRepositoryConfig;
25 import org.jboss.util.file.JarUtils;
26
27 import org.w3c.dom.Element;
28
29 /**
30 * Enterprise Archive Deployer.
31 *
32 * @jmx:mbean name="jboss.j2ee:service=EARDeployer"
33 * extends="org.jboss.deployment.SubDeployerMBean"
34 *
35 * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
36 * @author Scott.Stark@jboss.org
37 * @version $Revision: 1.19.2.10 $
38 */
39 public class EARDeployer
40 extends SubDeployerSupport
41 implements EARDeployerMBean
42 {
43 public EARDeployer()
44 {
45 super();
46 }
47
48 public boolean accepts(DeploymentInfo di)
49 {
50 String urlStr = di.url.getFile();
51 return urlStr.endsWith("ear") || urlStr.endsWith("ear/");
52 }
53
54 public void init(DeploymentInfo di)
55 throws DeploymentException
56 {
57 try
58 {
59 log.info("Init J2EE application: " + di.url);
60
61 InputStream in = di.localCl.getResourceAsStream("META-INF/application.xml");
62 if( in == null )
63 throw new DeploymentException("No META-INF/application.xml found");
64
65 /* Don't require validation of application.xml since an ear may
66 just contain a jboss sar specified in the jboss-app.xml descriptor.
67 */
68 XmlFileLoader xfl = new XmlFileLoader(false);
69 Element root = xfl.getDocument(in, "META-INF/application.xml").getDocumentElement();
70 J2eeApplicationMetaData metaData = new J2eeApplicationMetaData(root);
71 di.metaData = metaData;
72 in.close();
73
74 // Check for a jboss-app.xml descriptor
75 in = di.localCl.getResourceAsStream("META-INF/jboss-app.xml");
76 if( in != null )
77 {
78 Element jbossApp = xfl.getDocument(in, "META-INF/jboss-app.xml").getDocumentElement();
79 in.close();
80 // Import module/service archives to metadata
81 metaData.importXml(jbossApp, true);
82 // Check for a loader-repository for scoping
83 Element loader = MetaData.getOptionalChild(jbossApp, "loader-repository");
84 initLoaderRepository(di, loader);
85 }
86
87 // resolve the watch
88 if (di.url.getProtocol().equals("file"))
89 {
90 File file = new File(di.url.getFile());
91
92 // If not directory we watch the package
93 if (!file.isDirectory())
94 {
95 di.watch = di.url;
96 }
97 // If directory we watch the xml files
98 else
99 {
100 di.watch = new URL(di.url, "META-INF/application.xml");
101 }
102 }
103 else
104 {
105 // We watch the top only, no directory support
106 di.watch = di.url;
107 }
108
109 // Obtain the sub-deployment list
110 File parentDir = null;
111 HashMap extractedJars = new HashMap();
112
113 if (di.isDirectory)
114 {
115 parentDir = new File(di.localUrl.getFile());
116 }
117 else
118 {
119 /* Extract each entry so that deployment modules can be processed
120 and any manifest entries referenced by the ear modules are located
121 in the same unpacked directory structure.
122 */
123 String urlPrefix = "jar:" + di.localUrl + "!/";
124 JarFile jarFile = new JarFile(di.localUrl.getFile());
125 // For each entry, test if deployable, if so
126 // extract it and store the related URL in map
127 for (Enumeration e = jarFile.entries(); e.hasMoreElements();)
128 {
129 JarEntry entry = (JarEntry)e.nextElement();
130 String name = entry.getName();
131 try
132 {
133 URL url = new URL(urlPrefix + name);
134 if (isDeployable(name, url))
135 {
136 // Obtain a jar url for the nested jar
137 URL nestedURL = JarUtils.extractNestedJar(url, this.tempDeployDir);
138 // and store in it in map
139 extractedJars.put(name, nestedURL);
140 }
141 }
142 catch (MalformedURLException mue)
143 {
144 log.warn("Jar entry invalid. Ignoring: " + name, mue);
145 }
146 catch (IOException ex)
147 {
148 log.warn("Failed to extract nested jar. Ignoring: " + name, ex);
149 }
150 }
151 }
152
153 // Create subdeployments for the ear modules
154 for (Iterator iter = metaData.getModules(); iter.hasNext(); )
155 {
156 J2eeModuleMetaData mod = (J2eeModuleMetaData)iter.next();
157 String fileName = mod.getFileName();
158 if (fileName != null && (fileName = fileName.trim()).length() > 0)
159 {
160 DeploymentInfo sub = null;
161 if (di.isDirectory)
162 {
163 File f = new File(parentDir, fileName);
164 sub = new DeploymentInfo(f.toURL(), di, getServer());
165 }
166 else
167 {
168 // The nested jar url was placed into extractedJars above
169 URL nestedURL = (URL) extractedJars.get(fileName);
170 if( nestedURL == null )
171 throw new DeploymentException("Failed to find module file: "+fileName);
172 sub = new DeploymentInfo(nestedURL, di, getServer());
173 }
174 // Set the context-root on web modules
175 if( mod.isWeb() )
176 sub.webContext = mod.getWebContext();
177 log.debug("Deployment Info: " + sub + ", isDirectory: " + sub.isDirectory);
178 }
179 }
180 }
181 catch (DeploymentException e)
182 {
183 throw e;
184 }
185 catch (Exception e)
186 {
187 throw new DeploymentException("Error in accessing application metadata: ", e);
188 }
189
190 super.init(di);
191 }
192
193 public void start(DeploymentInfo di)
194 throws DeploymentException
195 {
196 super.start (di);
197 log.info ("Started J2EE application: " + di.url);
198 }
199
200
201 /**
202 * Describe <code>destroy</code> method here.
203 *
204 * @param di a <code>DeploymentInfo</code> value
205 * @exception DeploymentException if an error occurs
206 */
207 public void destroy(DeploymentInfo di) throws DeploymentException
208 {
209 log.info("Undeploying J2EE application, destroy step: " + di.url);
210 super.destroy(di);
211 }
212
213 /** Build the ear scoped repository
214 *
215 * @param di the deployment info passed to deploy
216 * @param loader the jboss-app/loader-repository element
217 * @throws Exception
218 */
219 protected void initLoaderRepository(DeploymentInfo di, Element loader)
220 throws Exception
221 {
222 if( loader == null )
223 return;
224
225 LoaderRepositoryConfig config = LoaderRepositoryFactory.parseRepositoryConfig(loader);
226 di.setRepositoryInfo(config);
227 }
228
229 /**
230 * Add -ds.xml and -service.xml as legitimate deployables.
231 */
232 protected boolean isDeployable(String name, URL url)
233 {
234 return super.isDeployable(name, url) || name.endsWith("-ds.xml") ||
235 name.endsWith("-service.xml");
236 }
237
238 /** Override the default behavior of looking into the archive for deployables
239 * as only those explicitly listed in the application.xml and jboss-app.xml
240 * should be deployed.
241 *
242 * @param di
243 */
244 protected void processNestedDeployments(DeploymentInfo di)
245 {
246 }
247 }