KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > deployment > SARDeployer


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2005, JBoss Inc., and individual contributors as indicated
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.deployment;
23
24 import java.io.File JavaDoc;
25 import java.io.FileOutputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.net.URLClassLoader JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.ListIterator JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.jar.JarEntry JavaDoc;
38 import java.util.jar.JarFile JavaDoc;
39
40 import javax.management.MBeanServer JavaDoc;
41 import javax.management.MalformedObjectNameException JavaDoc;
42 import javax.management.ObjectName JavaDoc;
43 import javax.xml.parsers.DocumentBuilder JavaDoc;
44 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
45
46 import org.jboss.mx.loading.LoaderRepositoryFactory;
47 import org.jboss.mx.loading.LoaderRepositoryFactory.LoaderRepositoryConfig;
48 import org.jboss.mx.util.MBeanProxyExt;
49 import org.jboss.net.protocol.URLLister;
50 import org.jboss.net.protocol.URLListerFactory;
51 import org.jboss.system.ServiceControllerMBean;
52 import org.jboss.system.server.ServerConfig;
53 import org.jboss.system.server.ServerConfigLocator;
54 import org.jboss.util.StringPropertyReplacer;
55 import org.jboss.util.Strings;
56 import org.jboss.util.stream.Streams;
57 import org.jboss.util.xml.JBossEntityResolver;
58 import org.w3c.dom.Element JavaDoc;
59 import org.w3c.dom.Node JavaDoc;
60 import org.w3c.dom.NodeList JavaDoc;
61 import org.xml.sax.InputSource JavaDoc;
62
63 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
64
65 /**
66  * This is the main Service Deployer API.
67  *
68  * @see org.jboss.system.Service
69  *
70  * @author <a HREF="mailto:marc.fleury@jboss.org">Marc Fleury</a>
71  * @author <a HREF="mailto:David.Maplesden@orion.co.nz">David Maplesden</a>
72  * @author <a HREF="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
73  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
74  * @author <a HREF="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>
75  * @author <a HREF="mailto:scott.stark@jboss.org">Scott Stark</a>
76  * @author <a HREF="mailto:dimitris@jboss.org">Dimitris Andreadis<a/>
77  * @version $Revision: 57108 $
78  */

79 public class SARDeployer extends SubDeployerSupport
80    implements SARDeployerMBean
81 {
82    /** The suffixes we accept, along with their relative order */
83    private static final String JavaDoc[] DEFAULT_ENHANCED_SUFFIXES = new String JavaDoc[] {
84          "050:.deployer",
85          "050:-deployer.xml",
86          "150:.sar",
87          "150:-service.xml"
88    };
89    
90    /** The deployment descriptor to look for */
91    private static final String JavaDoc JBOSS_SERVICE = "META-INF/jboss-service.xml";
92
93    /** A proxy to the ServiceController. */
94    private ServiceControllerMBean serviceController;
95
96    /** The server data directory. */
97    private File JavaDoc dataDir;
98
99    /** The server configuration base URL. For example,
100     file:/<jboss_dist_root>/server/default. Relative service
101     descriptor codebase elements are relative to this URL.
102     */

103    private URL JavaDoc serverHomeURL;
104
105    /** A HashMap<ObjectName, DeploymentInfo> for the deployed services */
106    private HashMap JavaDoc serviceDeploymentMap = new HashMap JavaDoc();
107    
108    /**
109     * A Map<String, List<String>> of the suffix to accepted archive META-INF descriptor name
110     * @todo externalize this
111     */

112    private Map JavaDoc suffixToDescriptorMap = new ConcurrentReaderHashMap();
113
114    /** A flag indicating if the parser used for the service descriptor should be configured for namespaces */
115    private boolean useNamespaceAwareParser;
116
117    /**
118     * Default CTOR
119     */

120    public SARDeployer()
121    {
122       setEnhancedSuffixes(DEFAULT_ENHANCED_SUFFIXES);
123       // Add the .har to META-INF/{jboss-service.xml,hibernate-service.xml} mapping
124
ArrayList JavaDoc tmp = new ArrayList JavaDoc();
125       tmp.add(JBOSS_SERVICE);
126       tmp.add("META-INF/hibernate-service.xml");
127       suffixToDescriptorMap.put(".har", tmp);
128    }
129
130    public boolean isUseNamespaceAwareParser()
131    {
132       return useNamespaceAwareParser;
133    }
134
135    public void setUseNamespaceAwareParser(boolean useNamespaceAwareParser)
136    {
137       this.useNamespaceAwareParser = useNamespaceAwareParser;
138    }
139
140    /**
141     * Get the associated service DeploymentInfo if found, null otherwise
142     *
143     * @param serviceName a service object name
144     * @return The associated service DeploymentInfo if found, null otherwise
145     * @jmx.managed-operation
146     */

147    public DeploymentInfo getService(ObjectName JavaDoc serviceName)
148    {
149       DeploymentInfo di = null;
150       synchronized( serviceDeploymentMap )
151       {
152          di = (DeploymentInfo) serviceDeploymentMap.get(serviceName);
153       }
154       return di;
155    }
156
157    /**
158     * Describe <code>init</code> method here.
159     *
160     * @param di a <code>DeploymentInfo</code> value
161     * @exception DeploymentException if an error occurs
162     * @jmx.managed-operation
163     */

164    public void init(DeploymentInfo di)
165       throws DeploymentException
166    {
167       try
168       {
169          if (di.url.getPath().endsWith("/"))
170          {
171             // the URL is a unpacked collection, watch the deployment descriptor
172
di.watch = new URL JavaDoc(di.url, JBOSS_SERVICE);
173          }
174          else
175          {
176             // just watch the original URL
177
di.watch = di.url;
178          }
179
180          // Get the document if not already present
181
parseDocument(di);
182
183          // Check for a custom loader-repository for scoping
184
NodeList JavaDoc loaders = di.document.getElementsByTagName("loader-repository");
185          if( loaders.getLength() > 0 )
186          {
187             Element JavaDoc loader = (Element JavaDoc) loaders.item(0);
188             LoaderRepositoryConfig config = LoaderRepositoryFactory.parseRepositoryConfig(loader);
189             di.setRepositoryInfo(config);
190          }
191
192          // In case there is a dependent classpath defined parse it
193
parseXMLClasspath(di);
194
195          // Copy local directory if local-directory element is present
196
NodeList JavaDoc lds = di.document.getElementsByTagName("local-directory");
197          log.debug("about to copy " + lds.getLength() + " local directories");
198
199          for (int i = 0; i< lds.getLength(); i++)
200          {
201             Element JavaDoc ld = (Element JavaDoc)lds.item(i);
202             String JavaDoc path = ld.getAttribute("path");
203             log.debug("about to copy local directory at " + path);
204
205             // Get the url of the local copy from the classloader.
206
log.debug("copying from " + di.localUrl + path + " -> " + dataDir);
207
208             inflateJar(di.localUrl, dataDir, path);
209          }
210       }
211       catch (DeploymentException de)
212       {
213          throw de;
214       }
215       catch (Exception JavaDoc e)
216       {
217          throw new DeploymentException(e);
218       }
219
220       // invoke super-class initialization
221
super.init(di);
222    }
223
224    /**
225     * Describe <code>create</code> method here.
226     *
227     * @param di a <code>DeploymentInfo</code> value
228     * @exception DeploymentException if an error occurs
229     * @jmx.managed-operation
230     */

231    public void create(DeploymentInfo di)
232       throws DeploymentException
233    {
234       try
235       {
236          // install the MBeans in this descriptor
237
log.debug("Deploying SAR, create step: url " + di.url);
238
239          // Register the SAR UCL as an mbean so we can use it as the service loader
240
ObjectName JavaDoc uclName = di.ucl.getObjectName();
241          if( getServer().isRegistered(uclName) == false )
242          {
243             log.debug("Registering service UCL="+uclName);
244             getServer().registerMBean(di.ucl, uclName);
245          }
246
247          List JavaDoc mbeans = di.mbeans;
248          mbeans.clear();
249          List JavaDoc descriptorMbeans = serviceController.install(di.document.getDocumentElement(), uclName);
250          mbeans.addAll(descriptorMbeans);
251
252          // create the services
253
for (Iterator JavaDoc iter = di.mbeans.iterator(); iter.hasNext(); )
254          {
255             ObjectName JavaDoc service = (ObjectName JavaDoc)iter.next();
256
257             // The service won't be created until explicitly dependent mbeans are created
258
serviceController.create(service);
259             synchronized( this.serviceDeploymentMap )
260             {
261                serviceDeploymentMap.put(service, di);
262             }
263          }
264
265          // Generate a JMX notification for the create stage
266
super.create(di);
267       }
268       catch(DeploymentException e)
269       {
270          log.debug("create operation failed for package "+ di.url, e);
271          destroy(di);
272          throw e;
273       }
274       catch (Exception JavaDoc e)
275       {
276          log.debug("create operation failed for package "+ di.url, e);
277          destroy(di);
278          throw new DeploymentException("create operation failed for package "
279             + di.url, e);
280       }
281    }
282
283    /**
284     * The <code>start</code> method starts all the mbeans in this DeploymentInfo..
285     *
286     * @param di a <code>DeploymentInfo</code> value
287     * @exception DeploymentException if an error occurs
288     * @jmx.managed-operation
289     */

290    public void start(DeploymentInfo di) throws DeploymentException
291    {
292       log.debug("Deploying SAR, start step: url " + di.url);
293       try
294       {
295          // start the services
296

297          for (Iterator JavaDoc iter = di.mbeans.iterator(); iter.hasNext(); )
298          {
299             ObjectName JavaDoc service = (ObjectName JavaDoc)iter.next();
300
301             // The service won't be started until explicitely dependent mbeans are started
302
serviceController.start(service);
303          }
304          // Generate a JMX notification for the start stage
305
super.start(di);
306       }
307       catch (Exception JavaDoc e)
308       {
309          stop(di);
310          destroy(di);
311          throw new DeploymentException("start operation failed on package "
312             + di.url, e);
313       }
314    }
315
316    /** The stop method invokes stop on the mbeans associatedw ith the deployment
317     * in reverse order relative to create.
318     *
319     * @param di the <code>DeploymentInfo</code> value to stop.
320     * @jmx.managed-operation
321     */

322    public void stop(DeploymentInfo di)
323    {
324       log.debug("undeploying document " + di.url);
325
326       List JavaDoc services = di.mbeans;
327       int lastService = services.size();
328
329       // stop services in reverse order.
330
for (ListIterator JavaDoc i = services.listIterator(lastService); i.hasPrevious();)
331       {
332          ObjectName JavaDoc name = (ObjectName JavaDoc)i.previous();
333          log.debug("stopping mbean " + name);
334          try
335          {
336             serviceController.stop(name);
337          }
338          catch (Exception JavaDoc e)
339          {
340             log.error("Could not stop mbean: " + name, e);
341          } // end of try-catch
342
}
343
344       // Generate a JMX notification for the stop stage
345
try
346       {
347          super.stop(di);
348       }
349       catch(Exception JavaDoc ignore)
350       {
351       }
352    }
353
354    /** The destroy method invokes destroy on the mbeans associated with
355     * the deployment in reverse order relative to create.
356     *
357     * @param di a <code>DeploymentInfo</code> value
358     * @jmx.managed-operation
359     */

360    public void destroy(DeploymentInfo di)
361    {
362       List JavaDoc services = di.mbeans;
363       int lastService = services.size();
364
365       for (ListIterator JavaDoc i = services.listIterator(lastService); i.hasPrevious();)
366       {
367          ObjectName JavaDoc name = (ObjectName JavaDoc)i.previous();
368          log.debug("destroying mbean " + name);
369          synchronized( serviceDeploymentMap )
370          {
371             serviceDeploymentMap.remove(name);
372          }
373
374          try
375          {
376             serviceController.destroy(name);
377          }
378          catch (Exception JavaDoc e)
379          {
380             log.error("Could not destroy mbean: " + name, e);
381          } // end of try-catch
382
}
383
384       for (ListIterator JavaDoc i = services.listIterator(lastService); i.hasPrevious();)
385       {
386          ObjectName JavaDoc name = (ObjectName JavaDoc)i.previous();
387          log.debug("removing mbean " + name);
388          try
389          {
390             serviceController.remove(name);
391          }
392          catch (Exception JavaDoc e)
393          {
394             log.error("Could not remove mbean: " + name, e);
395          } // end of try-catch
396
}
397
398       // Unregister the SAR UCL
399
try
400       {
401          ObjectName JavaDoc uclName = di.ucl.getObjectName();
402          if( getServer().isRegistered(uclName) == true )
403          {
404             log.debug("Unregistering service UCL="+uclName);
405             getServer().unregisterMBean(uclName);
406          }
407       }
408       catch(Exception JavaDoc ignore)
409       {
410       }
411
412       // Generate a JMX notification for the destroy stage
413
try
414       {
415          super.destroy(di);
416       }
417       catch(Exception JavaDoc ignore)
418       {
419       }
420    }
421
422    // ServiceMBeanSupport overrides --------------------------------
423

424    /**
425     * The startService method gets the mbeanProxies for MainDeployer
426     * and ServiceController, used elsewhere.
427     *
428     * @exception Exception if an error occurs
429     */

430    protected void startService() throws Exception JavaDoc
431    {
432       super.startService();
433
434       // get the controller proxy
435
serviceController = (ServiceControllerMBean)
436          MBeanProxyExt.create(ServiceControllerMBean.class,
437             ServiceControllerMBean.OBJECT_NAME, server);
438
439       // Get the data directory, install url & library url
440
ServerConfig config = ServerConfigLocator.locate();
441       dataDir = config.getServerDataDir();
442       serverHomeURL = config.getServerHomeURL();
443    }
444
445    /**
446     * This method stops all the applications in this server.
447     */

448    protected void stopService() throws Exception JavaDoc
449    {
450       // deregister with MainDeployer
451
super.stopService();
452       
453       // Help GC
454
serviceController = null;
455       serverHomeURL = null;
456       dataDir = null;
457    }
458
459    
460    protected ObjectName JavaDoc getObjectName(MBeanServer JavaDoc server, ObjectName JavaDoc name)
461       throws MalformedObjectNameException JavaDoc
462    {
463       return name == null ? OBJECT_NAME : name;
464    }
465    
466    // Protected -----------------------------------------------------
467

468    protected File JavaDoc[] listFiles(final String JavaDoc urlspec) throws Exception JavaDoc
469    {
470       URL JavaDoc url = Strings.toURL(urlspec);
471
472       // url is already canonical thanks to Strings.toURL
473
File JavaDoc dir = new File JavaDoc(url.getFile());
474
475       File JavaDoc[] files = dir.listFiles(new java.io.FileFilter JavaDoc()
476          {
477             public boolean accept(File JavaDoc pathname)
478             {
479                String JavaDoc name = pathname.getName().toLowerCase();
480                return (name.endsWith(".jar") || name.endsWith(".zip"));
481             }
482          });
483
484       return files;
485    }
486
487    /**
488     * @param di
489     * @throws Exception
490     */

491    protected void parseXMLClasspath(DeploymentInfo di)
492       throws Exception JavaDoc
493    {
494       ArrayList JavaDoc classpath = new ArrayList JavaDoc();
495       URLListerFactory listerFactory = new URLListerFactory();
496
497       NodeList JavaDoc children = di.document.getDocumentElement().getChildNodes();
498       for (int i = 0; i < children.getLength(); i++)
499       {
500          if (children.item(i).getNodeType() == Node.ELEMENT_NODE)
501          {
502             Element JavaDoc classpathElement = (Element JavaDoc)children.item(i);
503             if (classpathElement.getTagName().equals("classpath"))
504             {
505                log.debug("Found classpath element: " + classpathElement);
506                if (!classpathElement.hasAttribute("codebase"))
507                {
508                   throw new DeploymentException
509                      ("Invalid classpath element missing codebase: " + classpathElement);
510                }
511                String JavaDoc codebase = classpathElement.getAttribute("codebase").trim();
512                // Replace any system property references like ${x}
513
codebase = StringPropertyReplacer.replaceProperties(codebase);
514
515                String JavaDoc archives = null;
516                if (classpathElement.hasAttribute("archives"))
517                {
518                   archives = classpathElement.getAttribute("archives").trim();
519                   // Replace any system property references like ${x}
520
archives = StringPropertyReplacer.replaceProperties(archives);
521                   if ("".equals(archives))
522                   {
523                      archives = null;
524                   }
525                }
526
527                // Convert codebase to a URL
528
// "." is resolved relative to the deployment
529
// other URLs are resolved relative to SERVER_HOME
530
URL JavaDoc codebaseUrl;
531                if (".".equals(codebase))
532                {
533                   codebaseUrl = new URL JavaDoc(di.url, "./");
534                }
535                else
536                {
537                   if (archives != null && codebase.endsWith("/") == false)
538                   {
539                      codebase += "/";
540                   }
541                   codebaseUrl = new URL JavaDoc(serverHomeURL, codebase);
542                }
543                log.debug("codebase URL is " + codebaseUrl);
544
545                if (archives == null)
546                {
547                   // archives not supplied so add the codebase itself
548
classpath.add(codebaseUrl);
549                   log.debug("added codebase to classpath");
550                }
551                else
552                {
553                   // obtain a URLLister for the codebase and use it to obtain
554
// the list of URLs to add
555
log.debug("listing codebase for archives matching " + archives);
556                   URLLister lister = listerFactory.createURLLister(codebaseUrl);
557                   log.debug("URLLister class is " + lister.getClass().getName());
558                   classpath.addAll(lister.listMembers(codebaseUrl, archives));
559                }
560             } // end of if ()
561

562          } // end of if ()
563
} //end of for
564

565       // Ok, now we've found the list of urls we need... deploy their classes.
566
Iterator JavaDoc jars = classpath.iterator();
567       while (jars.hasNext())
568       {
569          URL JavaDoc neededURL = (URL JavaDoc) jars.next();
570          di.addLibraryJar(neededURL);
571          log.debug("deployed classes for " + neededURL);
572       }
573    }
574
575    /** Parse the META-INF/jboss-service.xml descriptor
576     */

577    protected void parseDocument(DeploymentInfo di)
578       throws Exception JavaDoc
579    {
580       InputStream JavaDoc stream = null;
581       try
582       {
583          if (di.document == null)
584          {
585             DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
586             factory.setNamespaceAware(useNamespaceAwareParser);
587             DocumentBuilder JavaDoc parser = factory.newDocumentBuilder();
588             URL JavaDoc docURL = di.localUrl;
589             URLClassLoader JavaDoc localCL = di.localCl;
590             // Load jboss-service.xml from the jar or directory
591
if (di.isXML == false)
592             {
593                // Check the suffix to descriptor mapping
594
String JavaDoc[] descriptors = getDescriptorName(di);
595                for(int n = 0; n < descriptors.length; n ++)
596                {
597                   String JavaDoc descriptor = descriptors[n];
598                   docURL = localCL.findResource(descriptor);
599                   if( docURL != null )
600                   {
601                      // If this is a unpacked deployment, update the watch url
602
if (di.url.getPath().endsWith("/"))
603                      {
604                         di.watch = new URL JavaDoc(di.url, descriptor);
605                         log.debug("Updated watch URL to: "+di.watch);
606                      }
607                      break;
608                   }
609                }
610                // No descriptors, use the default META-INF/jboss-service.xml
611
if( docURL == null )
612                   docURL = localCL.findResource(JBOSS_SERVICE);
613             }
614             // Validate that the descriptor was found
615
if (docURL == null)
616                throw new DeploymentException("Failed to find META-INF/jboss-service.xml for archive " + di.shortName);
617
618             stream = docURL.openStream();
619             InputSource JavaDoc is = new InputSource JavaDoc(stream);
620             is.setSystemId(docURL.toString());
621             parser.setEntityResolver(new JBossEntityResolver());
622             di.document = parser.parse(is);
623          }
624          else
625          {
626             log.debug("Using existing deployment.document");
627          }
628       }
629       finally
630       {
631          // Close the stream to get around "Too many open files"-errors
632
try
633          {
634             stream.close();
635          }
636          catch (Exception JavaDoc ignore)
637          {
638          }
639       }
640    }
641
642    /**
643     * The <code>inflateJar</code> copies the jar entries
644     * from the jar url jarUrl to the directory destDir.
645     * It can be used on the whole jar, a directory, or
646     * a specific file in the jar.
647     *
648     * @param url the <code>URL</code> if the directory or entry to copy.
649     * @param destDir the <code>File</code> value of the directory in which to
650     * place the inflated copies.
651     *
652     * @exception DeploymentException if an error occurs
653     * @exception IOException if an error occurs
654     */

655    protected void inflateJar(URL JavaDoc url, File JavaDoc destDir, String JavaDoc path)
656       throws DeploymentException, IOException JavaDoc
657    {
658       String JavaDoc filename = url.getFile();
659       JarFile JavaDoc jarFile = new JarFile JavaDoc(filename);
660       try
661       {
662          for (Enumeration JavaDoc e = jarFile.entries(); e.hasMoreElements(); )
663          {
664             JarEntry JavaDoc entry = (JarEntry JavaDoc)e.nextElement();
665             String JavaDoc name = entry.getName();
666
667             if (path == null || name.startsWith(path))
668             {
669                File JavaDoc outFile = new File JavaDoc(destDir, name);
670                if (!outFile.exists())
671                {
672                   if (entry.isDirectory())
673                   {
674                      outFile.mkdirs();
675                   }
676                   else
677                   {
678                      Streams.copyb(jarFile.getInputStream(entry),
679                                    new FileOutputStream JavaDoc(outFile));
680                   }
681                } // end of if (outFile.exists())
682
} // end of if (matches path)
683
}
684       }
685       finally
686       {
687          jarFile.close();
688       }
689    }
690
691    // Private -------------------------------------------------------
692

693    /**
694     * Parse the deployment url for its suffix and return a list of the accepted service
695     * descriptor names to look for.
696     *
697     * @param sdi - the sar deployment info
698     * @return the array of sar archive/directory relative names of the service descriptor. If
699     * there is no suffix to descriptor mapping, the default of {JBOSS_SERVICE} will be
700     * returned.
701     */

702    private String JavaDoc[] getDescriptorName(DeploymentInfo sdi)
703    {
704       String JavaDoc[] descriptorNames = {JBOSS_SERVICE};
705       String JavaDoc shortName = sdi.shortName;
706       int dot = shortName.lastIndexOf('.');
707       if( dot >= 0 )
708       {
709          String JavaDoc suffix = shortName.substring(dot);
710          List JavaDoc descriptors = (List JavaDoc) suffixToDescriptorMap.get(suffix);
711          if( descriptors != null )
712          {
713             descriptorNames = new String JavaDoc[descriptors.size()];
714             descriptors.toArray(descriptorNames);
715          }
716       }
717       return descriptorNames;
718    }
719 }
720
Popular Tags