KickJava   Java API By Example, From Geeks To Geeks.

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


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.io.OutputStream JavaDoc;
29 import java.net.MalformedURLException JavaDoc;
30 import java.net.URL JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.jar.JarEntry JavaDoc;
33 import java.util.jar.JarFile JavaDoc;
34
35 import javax.management.Notification JavaDoc;
36
37 import org.jboss.mx.util.MBeanProxyExt;
38 import org.jboss.system.ServiceMBeanSupport;
39 import org.jboss.system.server.ServerConfig;
40 import org.jboss.system.server.ServerConfigLocator;
41 import org.jboss.system.server.ServerConfigUtil;
42 import org.jboss.util.file.JarUtils;
43 import org.jboss.util.stream.Streams;
44
45 /**
46  * An abstract {@link SubDeployer}.
47  *
48  * Provides registration with {@link MainDeployer} as well as
49  * implementations of init, create, start, stop and destroy that
50  * generate JMX notifications on completion of the method.
51  *
52  * @version <tt>$Revision: 57108 $</tt>
53  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
54  * @author <a HREF="mailto:scott.stark@jboss.org">Scott Stark</a>
55  * @author <a HREF="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
56  */

57 public abstract class SubDeployerSupport extends ServiceMBeanSupport
58    implements SubDeployerExt, SubDeployerExtMBean
59 {
60    /**
61     * Holds the native library <em>suffix</em> for this system.
62     *
63     * Determined by examining the result of System.mapLibraryName(specialToken).
64     * The special token defaults to "XxX", but can be changed by setting the
65     * system property: <tt>org.jboss.deployment.SubDeployerSupport.nativeLibToken</tt>.
66     */

67    protected static final String JavaDoc nativeSuffix;
68
69    /**
70     * Holds the native library <em>prefix</em> for this system.
71     *
72     * @see #nativeSuffix
73     */

74    protected static final String JavaDoc nativePrefix;
75
76    /** A proxy to the MainDeployer. */
77    protected MainDeployerMBean mainDeployer;
78
79    /** The temporary directory into which deployments are unpacked */
80    protected File JavaDoc tempDeployDir;
81
82    /** The list of enhancedSuffixes for this subdeployer */
83    protected String JavaDoc[] enhancedSuffixes;
84    
85    /** The suffixes of interest to this subdeployer */
86    protected String JavaDoc[] suffixes;
87    
88    /** The relative order of this subdeployer - not really used */
89    protected int relativeOrder = -1;
90    
91    /** The temporary directory where native libs are unpacked. */
92    private File JavaDoc tempNativeDir;
93
94    /** Whether to load native libraries */
95    private boolean loadNative = false;
96    
97    /**
98     * The <code>createService</code> method is one of the ServiceMBean lifecyle operations.
99     * (no jmx tag needed from superinterface)
100     *
101     * @exception Exception if an error occurs
102     */

103    protected void createService() throws Exception JavaDoc
104    {
105       // get the temporary directories to use
106
ServerConfig config = ServerConfigLocator.locate();
107       tempNativeDir = config.getServerNativeDir();
108       tempDeployDir = config.getServerTempDeployDir();
109       loadNative = ServerConfigUtil.isLoadNative();
110
111       // Setup the proxy to mainDeployer
112
mainDeployer = (MainDeployerMBean)
113          MBeanProxyExt.create(MainDeployerMBean.class,
114                            MainDeployerMBean.OBJECT_NAME,
115                            server);
116    }
117
118    /**
119     * Performs SubDeployer registration.
120     */

121    protected void startService() throws Exception JavaDoc
122    {
123       // Register with the main deployer
124
mainDeployer.addDeployer(this);
125    }
126
127    /**
128     * Performs SubDeployer deregistration.
129     */

130    protected void stopService() throws Exception JavaDoc
131    {
132       // Unregister with the main deployer
133
mainDeployer.removeDeployer(this);
134    }
135
136    /**
137     * Clean up.
138     */

139    protected void destroyService() throws Exception JavaDoc
140    {
141       // Help the GC
142
mainDeployer = null;
143       tempNativeDir = null;
144    }
145
146    /**
147     * Set an array of suffixes of interest to this subdeployer.
148     * No need to register twice suffixes that may refer to
149     * unpacked deployments (e.g. .sar, .sar/).
150     *
151     * @param suffixes array of suffix strings
152     */

153    protected void setSuffixes(String JavaDoc[] suffixes)
154    {
155       this.suffixes = suffixes;
156    }
157    
158    /**
159     * Set the relative order of the specified suffixes
160     * all to the same value.
161     *
162     * @param relativeOrder the relative order of the specified suffixes
163     */

164    protected void setRelativeOrder(int relativeOrder)
165    {
166       this.relativeOrder = relativeOrder;
167    }
168    
169    /**
170     * Set the enhanced suffixes list for this deployer,
171     * causing also the supported suffixes list to be updated.
172     *
173     * Each enhanced suffix entries has the form:
174     *
175     * [order:]suffix
176     *
177     * No need to register twice suffixes that may refer to
178     * unpacked deployments (e.g. .sar, .sar/).
179     *
180     * @param enhancedSuffixes
181     */

182    public void setEnhancedSuffixes(String JavaDoc[] enhancedSuffixes)
183    {
184       if (enhancedSuffixes != null)
185       {
186          int len = enhancedSuffixes.length;
187          suffixes = new String JavaDoc[len];
188          
189          for (int i = 0; i < len; i++)
190          {
191             // parse each enhancedSuffix
192
SuffixOrderHelper.EnhancedSuffix e =
193                new SuffixOrderHelper.EnhancedSuffix(enhancedSuffixes[i]);
194             
195             suffixes[i] = e.suffix;
196          }
197       }
198       this.enhancedSuffixes = enhancedSuffixes;
199    }
200    
201    /**
202     * Get an array of enhancedSuffixes
203     *
204     * @return array of enhanced suffix strings
205     */

206    public String JavaDoc[] getEnhancedSuffixes()
207    {
208       return enhancedSuffixes;
209    }
210    
211    /**
212     * Get an array of suffixes of interest to this subdeployer
213     *
214     * @return array of suffix strings
215     */

216    public String JavaDoc[] getSuffixes()
217    {
218       return suffixes;
219    }
220    
221    /**
222     * Get the relative order of the specified suffixes
223     *
224     * @return the relative order of the specified suffixes
225     */

226    public int getRelativeOrder()
227    {
228       return relativeOrder;
229    }
230
231    /**
232     * A default implementation that uses the suffixes registered
233     * through either setSuffixes() or setEnhancedSuffixes(), to
234     * decide if a module is deployable by this deployer.
235     *
236     * If (according to DeploymentInfo) the deployment refers to
237     * a directory, but not an xml or script deployment, then
238     * the deployment suffix will be checked also against the
239     * registered suffixes + "/".
240     *
241     * @param sdi the DeploymentInfo to check
242     * @return whether the deployer can handle the deployment
243     */

244    public boolean accepts(DeploymentInfo sdi)
245    {
246       String JavaDoc[] acceptedSuffixes = getSuffixes();
247       if (acceptedSuffixes == null)
248       {
249          return false;
250       }
251       else
252       {
253          String JavaDoc urlPath = sdi.url.getPath();
254          String JavaDoc shortName = sdi.shortName;
255          boolean checkDir = sdi.isDirectory && !(sdi.isXML || sdi.isScript);
256          
257          for (int i = 0; i < acceptedSuffixes.length; i++)
258          {
259             // First check the urlPath the might end in "/"
260
// then check the shortName where "/" is removed
261
if (urlPath.endsWith(acceptedSuffixes[i]) ||
262                   (checkDir && shortName.endsWith(acceptedSuffixes[i])))
263             {
264                return true;
265             }
266          }
267          return false;
268       }
269    }
270
271    /**
272     * Sub-classes should override this method to provide
273     * custom 'init' logic.
274     *
275     * <p>This method calls the processNestedDeployments(di) method and then
276     * issues a JMX notification of type SubDeployer.INIT_NOTIFICATION.
277     * This behaviour can overridden by concrete sub-classes. If further
278     * initialization needs to be done, and you wish to preserve the
279     * functionality, be sure to call super.init(di) at the end of your
280     * implementation.
281     */

282    public void init(DeploymentInfo di) throws DeploymentException
283    {
284       processNestedDeployments(di);
285       
286       emitNotification(SubDeployer.INIT_NOTIFICATION, di);
287    }
288
289    /**
290     * Sub-classes should override this method to provide
291     * custom 'create' logic.
292     *
293     * This method issues a JMX notification of type SubDeployer.CREATE_NOTIFICATION.
294     */

295    public void create(DeploymentInfo di) throws DeploymentException
296    {
297       emitNotification(SubDeployer.CREATE_NOTIFICATION, di);
298    }
299
300    /**
301     * Sub-classes should override this method to provide
302     * custom 'start' logic.
303     *
304     * This method issues a JMX notification of type SubDeployer.START_NOTIFICATION.
305     */

306    public void start(DeploymentInfo di) throws DeploymentException
307    {
308       emitNotification(SubDeployer.START_NOTIFICATION, di);
309    }
310
311    /**
312     * Sub-classes should override this method to provide
313     * custom 'stop' logic.
314     *
315     * This method issues a JMX notification of type SubDeployer.START_NOTIFICATION.
316     */

317    public void stop(DeploymentInfo di) throws DeploymentException
318    {
319       emitNotification(SubDeployer.STOP_NOTIFICATION, di);
320    }
321
322    /**
323     * Sub-classes should override this method to provide
324     * custom 'destroy' logic.
325     *
326     * This method issues a JMX notification of type SubDeployer.DESTROY_NOTIFICATION.
327     */

328    public void destroy(DeploymentInfo di) throws DeploymentException
329    {
330       emitNotification(SubDeployer.DESTROY_NOTIFICATION, di);
331    }
332
333    /**
334     * Simple helper to emit a subdeployer notification containing DeploymentInfo
335     */

336    protected void emitNotification(String JavaDoc type, DeploymentInfo di)
337    {
338       Notification JavaDoc notification = new Notification JavaDoc(type, this, getNextNotificationSequenceNumber());
339       notification.setUserData(di);
340       sendNotification(notification);
341    }
342    
343    /**
344     * The <code>processNestedDeployments</code> method searches for any nested and
345     * deployable elements. Only Directories and Zipped archives are processed,
346     * and those are delegated to the addDeployableFiles and addDeployableJar
347     * methods respectively. This method can be overridden for alternate
348     * behaviour.
349     */

350    protected void processNestedDeployments(DeploymentInfo di) throws DeploymentException
351    {
352       log.debug("looking for nested deployments in : " + di.url);
353       if (di.isXML)
354       {
355          // no nested archives in an xml file
356
return;
357       }
358
359       if (di.isDirectory)
360       {
361          File JavaDoc f = new File JavaDoc(di.url.getFile());
362          if (!f.isDirectory())
363          {
364             // something is screwy
365
throw new DeploymentException
366                ("Deploy file incorrectly reported as a directory: " + di.url);
367          }
368
369          addDeployableFiles(di, f);
370       }
371       else
372       {
373          try
374          {
375             // Obtain a jar url for the nested jar
376
URL JavaDoc nestedURL = JarUtils.extractNestedJar(di.localUrl, this.tempDeployDir);
377             JarFile JavaDoc jarFile = new JarFile JavaDoc(nestedURL.getFile());
378             addDeployableJar(di, jarFile);
379          }
380          catch (Exception JavaDoc e)
381          {
382             log.warn("Failed to add deployable jar: " + di.localUrl, e);
383
384             //
385
// jason: should probably throw new DeploymentException
386
// ("Failed to add deployable jar: " + jarURLString, e);
387
// rather than make assumptions to what type of deployable
388
// file this was that failed...
389
//
390

391             return;
392          }
393       }
394    }
395
396    /**
397     * This method returns true if the name is a recognized archive file.
398     *
399     * It will query the MainDeployer that keeps a dynamically updated
400     * list of known archive extensions.
401     *
402     * @param name The "short-name" of the URL. It will have any trailing '/'
403     * characters removed, and any directory structure has been removed.
404     * @param url The full url.
405     *
406     * @return true iff the name ends in a known archive extension: .jar, .sar,
407     * .ear, .rar, .zip, .wsr, .war, or if the name matches the native
408     * library conventions.
409     */

410    protected boolean isDeployable(String JavaDoc name, URL JavaDoc url)
411    {
412       // any file under META-INF is not deployable; this method is called
413
// also for zipped content, e.g. dir1/dir2.sar/META-INF/bla.xml
414
if (url.getPath().indexOf("META-INF") != -1)
415       {
416          return false;
417       }
418       String JavaDoc[] acceptedSuffixes = mainDeployer.getSuffixOrder();
419       for (int i = 0; i < acceptedSuffixes.length; i++)
420       {
421          if (name.endsWith(acceptedSuffixes[i]))
422          {
423             return true;
424          }
425       }
426       // this is probably obsolete
427
return (name.endsWith(nativeSuffix) && name.startsWith(nativePrefix));
428    }
429
430    /**
431     * This method recursively searches the directory structure for any files
432     * that are deployable (@see isDeployable). If a directory is found to
433     * be deployable, then its subfiles and subdirectories are not searched.
434     *
435     * @param di the DeploymentInfo
436     * @param dir The root directory to start searching.
437     */

438    protected void addDeployableFiles(DeploymentInfo di, File JavaDoc dir)
439       throws DeploymentException
440    {
441       File JavaDoc[] files = dir.listFiles();
442       for (int i = 0; i < files.length; i++)
443       {
444          File JavaDoc file = files[i];
445          String JavaDoc name = file.getName();
446          try
447          {
448             URL JavaDoc url = file.toURL();
449             if (isDeployable(name, url))
450             {
451                deployUrl(di, url, name);
452                // we don't want deployable units processed any further
453
continue;
454             }
455          }
456          catch (MalformedURLException JavaDoc e)
457          {
458             log.warn("File name invalid; ignoring: " + file, e);
459          }
460          if (file.isDirectory())
461          {
462             addDeployableFiles(di, file);
463          }
464       }
465    }
466
467    /**
468     * This method searches the entire jar file for any deployable files
469     * (@see isDeployable).
470     *
471     * @param di the DeploymentInfo
472     * @param jarFile the jar file to process.
473     */

474    protected void addDeployableJar(DeploymentInfo di, JarFile JavaDoc jarFile)
475       throws DeploymentException
476    {
477       String JavaDoc urlPrefix = "jar:"+di.localUrl.toString()+"!/";
478       for (Enumeration JavaDoc e = jarFile.entries(); e.hasMoreElements();)
479       {
480          JarEntry JavaDoc entry = (JarEntry JavaDoc)e.nextElement();
481          String JavaDoc name = entry.getName();
482          try
483          {
484             URL JavaDoc url = new URL JavaDoc(urlPrefix+name);
485             if (isDeployable(name, url))
486             {
487                // Obtain a jar url for the nested jar
488
URL JavaDoc nestedURL = JarUtils.extractNestedJar(url, this.tempDeployDir);
489                deployUrl(di, nestedURL, name);
490             }
491          }
492          catch (MalformedURLException JavaDoc mue)
493          {
494             //
495
// jason: why are we eating this exception?
496
//
497
log.warn("Jar entry invalid; ignoring: " + name, mue);
498          }
499          catch (IOException JavaDoc ex)
500          {
501             log.warn("Failed to extract nested jar; ignoring: " + name, ex);
502          }
503       }
504    }
505
506    protected void deployUrl(DeploymentInfo di, URL JavaDoc url, String JavaDoc name)
507       throws DeploymentException
508    {
509       log.debug("nested deployment: " + url);
510       try
511       {
512          //
513
// jason: need better handling for os/arch specific libraries
514
// should be able to have multipule native libs in an archive
515
// one for each supported platform (os/arch), we only want to
516
// load the one for the current platform.
517
//
518
// This probably means explitly listing the libraries in a
519
// deployment descriptor, which could probably also be used
520
// to explicitly map the files, as it might be possible to
521
// share a native lib between more than one version, no need
522
// to duplicate the file, metadata can be used to tell us
523
// what needs to be done.
524
//
525
// Also need this mapping to get around the different values
526
// which are used by vm vendors for os.arch and such...
527
//
528

529          if (name.endsWith(nativeSuffix) && name.startsWith(nativePrefix))
530          {
531             File JavaDoc destFile = new File JavaDoc(tempNativeDir, name);
532             log.info("Loading native library: " + destFile.toString());
533
534             File JavaDoc parent = destFile.getParentFile();
535             if (!parent.exists()) {
536                parent.mkdirs();
537             }
538
539             InputStream JavaDoc in = url.openStream();
540             OutputStream JavaDoc out = new FileOutputStream JavaDoc(destFile);
541             Streams.copyb(in, out);
542
543             out.flush();
544             out.close();
545             in.close();
546
547             if (loadNative)
548                System.load(destFile.toString());
549          }
550          else
551          {
552             new DeploymentInfo(url, di, getServer());
553          }
554       }
555       catch (Exception JavaDoc ex)
556       {
557          throw new DeploymentException
558             ("Could not deploy sub deployment "+name+" of deployment "+di.url, ex);
559       }
560    }
561
562    /////////////////////////////////////////////////////////////////////////
563
// Class Property Configuration //
564
/////////////////////////////////////////////////////////////////////////
565

566    /**
567     * Static configuration properties for this class. Allows easy access
568     * to change defaults with system properties.
569     */

570    protected static class ClassConfiguration
571       extends org.jboss.util.property.PropertyContainer
572    {
573       private String JavaDoc nativeLibToken = "XxX";
574
575       public ClassConfiguration()
576       {
577          // properties will be settable under our enclosing classes group
578
super(SubDeployerSupport.class);
579
580          // bind the properties & the access methods
581
bindMethod("nativeLibToken");
582       }
583
584       public void setNativeLibToken(final String JavaDoc token)
585       {
586          this.nativeLibToken = token;
587       }
588
589       public String JavaDoc getNativeLibToken()
590       {
591          return nativeLibToken;
592       }
593    }
594
595    /** The singleton class configuration object for this class. */
596    protected static final ClassConfiguration CONFIGURATION = new ClassConfiguration();
597
598    //
599
// jason: the following needs to be done after setting up the
600
// class config reference, so it is moved it down here.
601
//
602

603    /**
604     * Determine the native library suffix and prefix.
605     */

606    static
607    {
608       // get the token to use from config, incase the default needs
609
// to be changed to resolve problem with a specific platform
610
String JavaDoc token = CONFIGURATION.getNativeLibToken();
611
612       // then determine what the prefix and suffixes are for this platform
613
String JavaDoc nativex = System.mapLibraryName(token);
614       int xPos = nativex.indexOf(token);
615       nativePrefix = nativex.substring(0, xPos);
616       nativeSuffix = nativex.substring(xPos + 3);
617    }
618 }
619
Popular Tags