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.ejb;
9
10 import java.io.File;
11 import java.io.InputStream;
12 import java.net.URL;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import javax.management.MBeanServer;
17 import javax.management.MalformedObjectNameException;
18 import javax.management.ObjectName;
19 import javax.transaction.TransactionManager;
20
21 import org.jboss.deployment.DeploymentInfo;
22 import org.jboss.deployment.SubDeployerSupport;
23 import org.jboss.deployment.DeploymentException;
24 import org.jboss.logging.Logger;
25 import org.jboss.system.ServiceControllerMBean;
26 import org.jboss.metadata.ApplicationMetaData;
27 import org.jboss.metadata.XmlFileLoader;
28 import org.jboss.metadata.MetaData;
29 import org.jboss.mx.loading.LoaderRepositoryFactory;
30 import org.jboss.mx.util.MBeanProxyExt;
31 import org.jboss.mx.util.ObjectNameConverter;
32 import org.jboss.verifier.BeanVerifier;
33 import org.jboss.verifier.event.VerificationEvent;
34 import org.jboss.verifier.event.VerificationListener;
35 import org.w3c.dom.Element;
36
37 /**
38 * A EJBDeployer is used to deploy EJB applications. It can be given a
39 * URL to an EJB-jar or EJB-JAR XML file, which will be used to instantiate
40 * containers and make them available for invocation.
41 *
42 * @jmx:mbean
43 * name="jboss.ejb:service=EJBDeployer"
44 * extends="org.jboss.deployment.SubDeployerMBean"
45 *
46 * @see Container
47 *
48 * @version <tt>$Revision: 1.23.2.13 $</tt>
49 * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
50 * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
51 * @author <a href="mailto:jplindfo@helsinki.fi">Juha Lindfors</a>
52 * @author <a href="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
53 * @author <a href="mailto:peter.antman@tim.se">Peter Antman</a>.
54 * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
55 * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>
56 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
57 */
58 public class EJBDeployer
59 extends SubDeployerSupport
60 implements EJBDeployerMBean
61 {
62 private ServiceControllerMBean serviceController;
63
64 /** A map of current deployments. */
65 private HashMap deployments = new HashMap();
66
67 /** Verify EJB-jar contents on deployments */
68 private boolean verifyDeployments;
69
70 /** Enable verbose verification. */
71 private boolean verifierVerbose;
72
73 /** Enable strict verification: deploy JAR only if Verifier reports
74 * no problems */
75 private boolean strictVerifier;
76
77 /** Enable metrics interceptor */
78 private boolean metricsEnabled;
79
80 /** A flag indicating if deployment descriptors should be validated */
81 private boolean validateDTDs;
82
83 private ObjectName webServiceName;
84
85 private ObjectName transactionManagerServiceName;
86 private TransactionManager tm;
87
88 /**
89 * Returns the deployed applications.
90 *
91 * @jmx:managed-operation
92 */
93 public Iterator getDeployedApplications()
94 {
95 return deployments.values().iterator();
96 }
97
98 protected ObjectName getObjectName(MBeanServer server, ObjectName name)
99 throws MalformedObjectNameException
100 {
101 return name == null ? OBJECT_NAME : name;
102 }
103
104 /**
105 * Get a reference to the ServiceController
106 */
107 protected void startService() throws Exception
108 {
109 serviceController = (ServiceControllerMBean)
110 MBeanProxyExt.create(ServiceControllerMBean.class,
111 ServiceControllerMBean.OBJECT_NAME, server);
112 tm = (TransactionManager)getServer().getAttribute(transactionManagerServiceName,
113 "TransactionManager");
114
115 // register with MainDeployer
116 super.startService();
117 }
118
119 /**
120 * Implements the template method in superclass. This method stops all the
121 * applications in this server.
122 */
123 protected void stopService() throws Exception
124 {
125
126 for( Iterator modules = deployments.values().iterator();
127 modules.hasNext(); )
128 {
129 DeploymentInfo di = (DeploymentInfo) modules.next();
130 stop(di);
131 }
132
133 // avoid concurrent modification exception
134 for( Iterator modules = new ArrayList(deployments.values()).iterator();
135 modules.hasNext(); )
136 {
137 DeploymentInfo di = (DeploymentInfo) modules.next();
138 destroy(di);
139 }
140 deployments.clear();
141
142 // deregister with MainDeployer
143 super.stopService();
144
145 serviceController = null;
146 tm = null;
147 }
148
149 /**
150 * Enables/disables the application bean verification upon deployment.
151 *
152 * @jmx:managed-attribute
153 *
154 * @param verify true to enable; false to disable
155 */
156 public void setVerifyDeployments( boolean verify )
157 {
158 verifyDeployments = verify;
159 }
160
161 /**
162 * Returns the state of bean verifier (on/off)
163 *
164 * @jmx:managed-attribute
165 *
166 * @return true if enabled; false otherwise
167 */
168 public boolean getVerifyDeployments()
169 {
170 return verifyDeployments;
171 }
172
173 /**
174 * Enables/disables the verbose mode on the verifier.
175 *
176 * @jmx:managed-attribute
177 *
178 * @param verbose true to enable; false to disable
179 */
180 public void setVerifierVerbose(boolean verbose)
181 {
182 verifierVerbose = verbose;
183 }
184
185 /**
186 * Returns the state of the bean verifier (verbose/non-verbose mode)
187 *
188 * @jmx:managed-attribute
189 *
190 * @return true if enabled; false otherwise
191 */
192 public boolean getVerifierVerbose()
193 {
194 return verifierVerbose;
195 }
196
197 /**
198 * Enables/disables the strict mode on the verifier.
199 *
200 * @jmx:managed-attribute
201 *
202 * @param strictVerifier <code>true</code> to enable; <code>false</code>
203 * to disable
204 */
205 public void setStrictVerifier( boolean strictVerifier )
206 {
207 this.strictVerifier = strictVerifier;
208 }
209
210 /**
211 * Returns the mode of the bean verifier (strict/non-strict mode)
212 *
213 * @jmx:managed-attribute
214 *
215 * @return <code>true</code> if the Verifier is in strict mode,
216 * <code>false</code> otherwise
217 */
218 public boolean getStrictVerifier()
219 {
220 return strictVerifier;
221 }
222
223
224 /**
225 * Enables/disables the metrics interceptor for containers.
226 *
227 * @jmx:managed-attribute
228 *
229 * @param enable true to enable; false to disable
230 */
231 public void setMetricsEnabled(boolean enable)
232 {
233 metricsEnabled = enable;
234 }
235
236 /**
237 * Checks if this container factory initializes the metrics interceptor.
238 *
239 * @jmx:managed-attribute
240 *
241 * @return true if metrics are enabled; false otherwise
242 */
243 public boolean isMetricsEnabled()
244 {
245 return metricsEnabled;
246 }
247
248 /**
249 * Get the flag indicating that ejb-jar.dtd, jboss.dtd &
250 * jboss-web.dtd conforming documents should be validated
251 * against the DTD.
252 *
253 * @jmx:managed-attribute
254 */
255 public boolean getValidateDTDs()
256 {
257 return validateDTDs;
258 }
259
260 /**
261 * Set the flag indicating that ejb-jar.dtd, jboss.dtd &
262 * jboss-web.dtd conforming documents should be validated
263 * against the DTD.
264 *
265 * @jmx:managed-attribute
266 */
267 public void setValidateDTDs(boolean validate)
268 {
269 this.validateDTDs = validate;
270 }
271
272
273 /**
274 * Get the WebServiceName value.
275 * @return the WebServiceName value.
276 *
277 * @jmx:managed-attribute
278 */
279 public ObjectName getWebServiceName()
280 {
281 return webServiceName;
282 }
283
284 /**
285 * Set the WebServiceName value.
286 * @param newWebServiceName The new WebServiceName value.
287 *
288 * @jmx:managed-attribute
289 */
290 public void setWebServiceName(ObjectName webServiceName)
291 {
292 this.webServiceName = webServiceName;
293 }
294
295
296 /**
297 * Get the TransactionManagerServiceName value.
298 * @return the TransactionManagerServiceName value.
299 *
300 * @jmx:managed-attribute
301 */
302 public ObjectName getTransactionManagerServiceName()
303 {
304 return transactionManagerServiceName;
305 }
306
307 /**
308 * Set the TransactionManagerServiceName value.
309 * @param transactionManagerServiceName The new TransactionManagerServiceName value.
310 *
311 * @jmx:managed-attribute
312 */
313 public void setTransactionManagerServiceName(ObjectName transactionManagerServiceName)
314 {
315 this.transactionManagerServiceName = transactionManagerServiceName;
316 }
317
318
319
320 public boolean accepts(DeploymentInfo di)
321 {
322 // To be accepted the deployment's root name must end in jar
323 String urlStr = di.url.getFile();
324 if( !urlStr.endsWith("jar") && !urlStr.endsWith("jar/") )
325 {
326 return false;
327 }
328
329 // However the jar must also contain at least one ejb-jar.xml
330 boolean accepts = false;
331 try
332 {
333 URL dd = di.localCl.findResource("META-INF/ejb-jar.xml");
334 if (dd == null)
335 {
336 return false;
337 }
338
339 // If the DD url is not a subset of the urlStr then this is coming
340 // from a jar referenced by the deployment jar manifest and the
341 // this deployment jar it should not be treated as an ejb-jar
342 if( di.localUrl != null )
343 {
344 urlStr = di.localUrl.toString();
345 }
346
347 String ddStr = dd.toString();
348 if ( ddStr.indexOf(urlStr) >= 0 )
349 {
350 accepts = true;
351 }
352 }
353 catch( Exception ignore )
354 {
355 }
356
357 return accepts;
358 }
359
360 public void init(DeploymentInfo di)
361 throws DeploymentException
362 {
363 try
364 {
365 if( di.url.getProtocol().equalsIgnoreCase("file") )
366 {
367 File file = new File(di.url.getFile());
368
369 if( !file.isDirectory() )
370 {
371 // If not directory we watch the package
372 di.watch = di.url;
373 }
374 else
375 {
376 // If directory we watch the xml files
377 di.watch = new URL(di.url, "META-INF/ejb-jar.xml");
378 }
379 }
380 else
381 {
382 // We watch the top only, no directory support
383 di.watch = di.url;
384 }
385
386 // Check for a loader-repository
387 XmlFileLoader xfl = new XmlFileLoader();
388 InputStream in = di.localCl.getResourceAsStream("META-INF/jboss.xml");
389 if( in != null )
390 {
391 Element jboss = xfl.getDocument(in, "META-INF/jboss.xml").getDocumentElement();
392 in.close();
393 // Check for a ejb level class loading config
394 Element loader = MetaData.getOptionalChild(jboss, "loader-repository");
395 if( loader != null )
396 {
397 LoaderRepositoryFactory.LoaderRepositoryConfig config =
398 LoaderRepositoryFactory.parseRepositoryConfig(loader);
399 di.setRepositoryInfo(config);
400 }
401 }
402
403 }
404 catch (Exception e)
405 {
406 if (e instanceof DeploymentException)
407 throw (DeploymentException)e;
408 throw new DeploymentException( "failed to initialize", e );
409 }
410
411 // invoke super-class initialization
412 super.init(di);
413 }
414
415 /** This is here as a reminder that we may not want to allow ejb jars to
416 * have arbitrary sub deployments. Currently we do.
417 * @param di
418 * @throws DeploymentException
419 */
420 protected void processNestedDeployments(DeploymentInfo di)
421 throws DeploymentException
422 {
423 super.processNestedDeployments(di);
424 }
425
426 public synchronized void create(DeploymentInfo di)
427 throws DeploymentException
428 {
429 try
430 {
431 // Create a file loader with which to load the files
432 XmlFileLoader efm = new XmlFileLoader(validateDTDs);
433 efm.setClassLoader(di.localCl);
434
435 // Load XML
436 di.metaData = efm.load();
437 }
438 catch (Exception e)
439 {
440 if (e instanceof DeploymentException)
441 throw (DeploymentException)e;
442 throw new DeploymentException( "Failed to load metadata", e );
443 }
444
445 if( verifyDeployments )
446 {
447 // we have a positive attitude
448 boolean allOK = true;
449
450 // wrapping this into a try - catch block to prevent errors in
451 // verifier from stopping the deployment
452 try
453 {
454 BeanVerifier verifier = new BeanVerifier();
455
456 // add a listener so we can log the results
457 verifier.addVerificationListener(new VerificationListener()
458 {
459 Logger log = Logger.getLogger(EJBDeployer.class,
460 "verifier" );
461
462 public void beanChecked(VerificationEvent event)
463 {
464 log.debug( "Bean checked: " + event.getMessage() );
465 }
466
467 public void specViolation(VerificationEvent event)
468 {
469 log.warn( "EJB spec violation: " +
470 (verifierVerbose ? event.getVerbose() : event.getMessage()));
471 }
472 });
473
474 log.debug("Verifying " + di.url);
475 verifier.verify( di.url, (ApplicationMetaData) di.metaData,
476 di.ucl );
477
478 allOK = verifier.getSuccess();
479 }
480 catch (Throwable t)
481 {
482 log.warn("Verify failed; continuing", t );
483 allOK = false;
484 }
485
486 // If the verifier is in strict mode and an error/warning
487 // was found in the Verification process, throw a Deployment
488 // Exception
489 if( strictVerifier && !allOK )
490 {
491 throw new DeploymentException( "Verification of Enterprise " +
492 "Beans failed, see above for error messages." );
493 }
494
495 }
496
497 // Create an MBean for the EJB module
498 try
499 {
500 ApplicationMetaData metadata = (ApplicationMetaData) di.metaData;
501 EjbModule ejbModule = new EjbModule(di, tm, webServiceName);
502 String name = metadata.getJmxName();
503 if( name == null )
504 {
505 name = EjbModule.BASE_EJB_MODULE_NAME + ",module=" + di.shortName;
506 }
507 // Build an escaped JMX name including deployment shortname
508 ObjectName ejbModuleName = ObjectNameConverter.convert(name);
509 // Check that the name is not registered
510 if( server.isRegistered(ejbModuleName) == true )
511 {
512 log.debug("The EJBModule name: "+ejbModuleName
513 +"is already registered, adding uid="+System.identityHashCode(ejbModule));
514 name = name + ",uid="+System.identityHashCode(ejbModule);
515 ejbModuleName = ObjectNameConverter.convert(name);
516 }
517
518 server.registerMBean(ejbModule, ejbModuleName);
519 di.deployedObject = ejbModuleName;
520
521 log.debug( "Deploying: " + di.url );
522 // Invoke the create life cycle method
523 serviceController.create(di.deployedObject);
524 }
525 catch (Exception e)
526 {
527 throw new DeploymentException("Error during create of EjbModule: "
528 + di.url, e);
529 }
530 super.create(di);
531 }
532
533 public synchronized void start(DeploymentInfo di)
534 throws DeploymentException
535 {
536 try
537 {
538 // Start application
539 log.debug( "start application, deploymentInfo: " + di +
540 ", short name: " + di.shortName +
541 ", parent short name: " +
542 (di.parent == null ? "null" : di.parent.shortName) );
543
544 serviceController.start(di.deployedObject);
545
546 log.info( "Deployed: " + di.url );
547
548 // Register deployment. Use the application name in the hashtable
549 // FIXME: this is obsolete!! (really?!)
550 deployments.put(di.url, di);
551 }
552 catch (Exception e)
553 {
554 stop(di);
555 destroy(di);
556
557 throw new DeploymentException( "Could not deploy " + di.url, e );
558 }
559 super.start(di);
560 }
561
562 public void stop(DeploymentInfo di)
563 throws DeploymentException
564 {
565 try
566 {
567 serviceController.stop(di.deployedObject);
568 }
569 catch (Exception e)
570 {
571 throw new DeploymentException( "problem stopping ejb module: " +
572 di.url, e );
573 }
574 super.stop(di);
575 }
576
577 public void destroy(DeploymentInfo di)
578 throws DeploymentException
579 {
580 // FIXME: If the put() is obsolete above, this is obsolete, too
581 deployments.remove(di.url);
582
583 try
584 {
585 serviceController.destroy( di.deployedObject );
586 serviceController.remove( di.deployedObject );
587 }
588 catch (Exception e)
589 {
590 throw new DeploymentException( "problem destroying ejb module: " +
591 di.url, e );
592 }
593 super.destroy(di);
594 }
595 }
596 /*
597 vim:ts=3:sw=3:et
598 */