View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.osgi.boot.internal.serverfactory;
20  
21  import java.io.File;
22  import java.io.InputStream;
23  import java.net.URL;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Dictionary;
27  import java.util.Enumeration;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  import java.util.StringTokenizer;
34  
35  import org.eclipse.jetty.deploy.AppLifeCycle;
36  import org.eclipse.jetty.deploy.AppProvider;
37  import org.eclipse.jetty.deploy.DeploymentManager;
38  import org.eclipse.jetty.deploy.bindings.StandardStarter;
39  import org.eclipse.jetty.deploy.bindings.StandardStopper;
40  import org.eclipse.jetty.osgi.boot.BundleContextProvider;
41  import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
42  import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
43  import org.eclipse.jetty.osgi.boot.OSGiDeployer;
44  import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
45  import org.eclipse.jetty.osgi.boot.OSGiUndeployer;
46  import org.eclipse.jetty.osgi.boot.ServiceContextProvider;
47  import org.eclipse.jetty.osgi.boot.ServiceWebAppProvider;
48  import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper;
49  import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
50  import org.eclipse.jetty.osgi.boot.utils.FakeURLClassLoader;
51  import org.eclipse.jetty.osgi.boot.utils.TldBundleDiscoverer;
52  import org.eclipse.jetty.osgi.boot.utils.Util;
53  import org.eclipse.jetty.server.Server;
54  import org.eclipse.jetty.server.handler.ContextHandlerCollection;
55  import org.eclipse.jetty.util.IO;
56  import org.eclipse.jetty.util.log.Log;
57  import org.eclipse.jetty.util.log.Logger;
58  import org.eclipse.jetty.util.resource.Resource;
59  import org.eclipse.jetty.xml.XmlConfiguration;
60  import org.xml.sax.SAXParseException;
61  
62  /**
63   * ServerInstanceWrapper
64   * 
65   *  Configures and starts a jetty Server instance. 
66   */
67  public class ServerInstanceWrapper
68  {
69  
70      /**
71       * The value of this property points to the parent director of the jetty.xml
72       * configuration file currently executed. Everything is passed as a URL to
73       * support the case where the bundle is zipped.
74       */
75      public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url";
76      
77      
78      private static Collection<TldBundleDiscoverer> __containerTldBundleDiscoverers = new ArrayList<TldBundleDiscoverer>();
79  
80      private static Logger LOG = Log.getLogger(ServerInstanceWrapper.class.getName());
81      
82   
83  
84      private final String _managedServerName;
85  
86      /**
87       * The managed jetty server
88       */
89      private Server _server;
90  
91      private ContextHandlerCollection _ctxtCollection;
92  
93      /**
94       * This is the class loader that should be the parent classloader of any
95       * webapp classloader. It is in fact the _libExtClassLoader with a trick to
96       * let the TldScanner find the jars where the tld files are.
97       */
98      private ClassLoader _commonParentClassLoaderForWebapps;
99  
100     private DeploymentManager _deploymentManager;
101     
102     
103     
104     /* ------------------------------------------------------------ */
105     public static void addContainerTldBundleDiscoverer (TldBundleDiscoverer tldBundleDiscoverer)
106     {
107         __containerTldBundleDiscoverers.add(tldBundleDiscoverer);
108     }
109     
110     /* ------------------------------------------------------------ */
111     public static Collection<TldBundleDiscoverer> getContainerTldBundleDiscoverers()
112     {
113         return __containerTldBundleDiscoverers;
114     }
115     
116  
117 
118     
119     /* ------------------------------------------------------------ */
120     public static Server configure(Server server, List<URL> jettyConfigurations, Dictionary props) throws Exception
121     {
122        
123         if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return server; }
124         
125         Map<String, Object> id_map = new HashMap<String, Object>();
126         if (server != null)
127         {
128             //Put in a mapping for the id "Server" and the name of the server as the instance being configured
129             id_map.put("Server", server);
130             id_map.put((String)props.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME), server);
131         }
132 
133         Map<String, String> properties = new HashMap<String, String>();
134         if (props != null)
135         {
136             Enumeration<Object> en = props.keys();
137             while (en.hasMoreElements())
138             {
139                 Object key = en.nextElement();
140                 Object value = props.get(key);
141                 String keyStr = String.valueOf(key);
142                 String valStr = String.valueOf(value);
143                 properties.put(keyStr, valStr);
144                 if (server != null) server.setAttribute(keyStr, valStr);
145             }
146         }
147 
148         for (URL jettyConfiguration : jettyConfigurations)
149         {
150             InputStream is = null;
151             try
152             {
153                 // Execute a Jetty configuration file
154                 Resource r = Resource.newResource(jettyConfiguration);
155                 if (!r.exists())
156                 {
157                     LOG.warn("File does not exist "+r);
158                     throw new IllegalStateException("No such jetty server config file: "+r);
159                 }
160                 is = r.getInputStream();
161                 XmlConfiguration config = new XmlConfiguration(is);
162                 config.getIdMap().putAll(id_map);
163                 config.getProperties().putAll(properties);
164                 
165                 // #334062 compute the URL of the folder that contains the
166                 // conf file and set it as a property so we can compute relative paths
167                 // from it.
168                 String urlPath = jettyConfiguration.toString();
169                 int lastSlash = urlPath.lastIndexOf('/');
170                 if (lastSlash > 4)
171                 {
172                     urlPath = urlPath.substring(0, lastSlash);
173                     config.getProperties().put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath);
174                 }
175      
176                 Object o = config.configure();
177                 if (server == null)
178                     server = (Server)o;
179                 
180                 id_map = config.getIdMap();
181             }
182             catch (SAXParseException saxparse)
183             {
184                 LOG.warn("Unable to configure the jetty/etc file " + jettyConfiguration, saxparse);
185                 throw saxparse;
186             }
187             finally
188             {
189                 IO.close(is);
190             }
191         }
192 
193         return server;
194     }
195     
196     
197     
198     
199     /* ------------------------------------------------------------ */
200     public ServerInstanceWrapper(String managedServerName)
201     {
202         _managedServerName = managedServerName;
203     }
204 
205     /* ------------------------------------------------------------ */ 
206     public String getManagedServerName()
207     {
208         return _managedServerName;
209     }
210     
211     
212     /* ------------------------------------------------------------ */
213     /**
214      * The classloader that should be the parent classloader for each webapp
215      * deployed on this server.
216      * 
217      * @return
218      */
219     public ClassLoader getParentClassLoaderForWebapps()
220     {
221         return _commonParentClassLoaderForWebapps;
222     }
223     
224     
225     /* ------------------------------------------------------------ */
226     /**
227      * @return The deployment manager registered on this server.
228      */
229     public DeploymentManager getDeploymentManager()
230     {
231         return _deploymentManager;
232     }
233     
234     
235     /* ------------------------------------------------------------ */
236     /**
237      * @return The app provider registered on this server.
238      */
239     public Server getServer()
240     {
241         return _server;
242     }
243 
244     /* ------------------------------------------------------------ */
245     /**
246      * @return The collection of context handlers
247      */
248     public ContextHandlerCollection getContextHandlerCollection()
249     {
250         return _ctxtCollection;
251     }
252     
253     
254     /* ------------------------------------------------------------ */
255     public void start(Server server, Dictionary props) throws Exception
256     {
257         _server = server;
258         ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
259         try
260         {
261             // passing this bundle's classloader as the context classloader
262             // makes sure there is access to all the jetty's bundles
263             ClassLoader libExtClassLoader = null;
264             String sharedURLs = (String) props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS);
265 
266             List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null;
267             libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null,JettyBootstrapActivator.class.getClassLoader());
268 
269             if (LOG.isDebugEnabled()) LOG.debug("LibExtClassLoader = "+libExtClassLoader);
270             
271             Thread.currentThread().setContextClassLoader(libExtClassLoader);
272 
273             String jettyConfigurationUrls = (String) props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
274             List<URL> jettyConfigurations = jettyConfigurationUrls != null ? Util.fileNamesAsURLs(jettyConfigurationUrls, Util.DEFAULT_DELIMS) : null;
275             
276             _server = configure(server, jettyConfigurations, props);
277 
278             init();
279             
280             //if support for jsp is enabled, we need to convert locations of bundles that contain tlds into urls.
281             //these are tlds that we want jasper to treat as if they are on the container's classpath. Web bundles
282             //can use the Require-TldBundle MANIFEST header to name other tld-containing bundles that should be regarded
283             //as on the webapp classpath.
284             if (!__containerTldBundleDiscoverers.isEmpty())
285             {
286                 Set<URL> urls = new HashSet<URL>();
287                 //discover bundles with tlds that need to be on the container's classpath as URLs
288                 for (TldBundleDiscoverer d:__containerTldBundleDiscoverers)
289                 {
290                     URL[] list = d.getUrlsForBundlesWithTlds(_deploymentManager, BundleFileLocatorHelperFactory.getFactory().getHelper());
291                     if (list != null)
292                     {
293                         for (URL u:list)
294                             urls.add(u);
295                     }
296                 }
297                 _commonParentClassLoaderForWebapps =  new FakeURLClassLoader(libExtClassLoader, urls.toArray(new URL[urls.size()]));
298             }
299             else
300                 _commonParentClassLoaderForWebapps = libExtClassLoader;
301 
302             
303             if (LOG.isDebugEnabled()) LOG.debug("common classloader = "+_commonParentClassLoaderForWebapps);
304 
305             server.start();
306         }
307         catch (Exception e)
308         {
309             if (server != null)
310             {
311                 try
312                 {
313                     server.stop();
314                 }
315                 catch (Exception x)
316                 {
317                     LOG.ignore(x);
318                 }
319             }
320             throw e;
321         }
322         finally
323         {
324             Thread.currentThread().setContextClassLoader(contextCl);
325         }
326     }
327     
328     /* ------------------------------------------------------------ */
329     public void stop()
330     {
331         try
332         {
333             if (_server.isRunning())
334             {
335                 _server.stop();
336             }
337         }
338         catch (Exception e)
339         {
340             LOG.warn(e);
341         }
342     }
343     
344     
345    
346     
347     /* ------------------------------------------------------------ */
348     /**
349      * Must be called after the server is configured. 
350      * 
351      * It is assumed the server has already been configured with the ContextHandlerCollection structure.
352      * 
353      */
354     private void init()
355     {
356         // Get the context handler
357         _ctxtCollection = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
358 
359         if (_ctxtCollection == null) 
360             throw new IllegalStateException("ERROR: No ContextHandlerCollection configured in Server");
361         
362         List<String> providerClassNames = new ArrayList<String>();
363         
364         // get a deployerManager and some providers
365         Collection<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
366         if (deployers != null && !deployers.isEmpty())
367         {
368             _deploymentManager = deployers.iterator().next();
369             
370             for (AppProvider provider : _deploymentManager.getAppProviders())
371             {
372                providerClassNames.add(provider.getClass().getName());
373             }
374         }
375         else
376         {
377             //add some kind of default
378             _deploymentManager = new DeploymentManager();
379             _deploymentManager.setContexts(_ctxtCollection);
380             _server.addBean(_deploymentManager);
381         }
382 
383         _deploymentManager.setUseStandardBindings(false);
384         List<AppLifeCycle.Binding> deploymentLifeCycleBindings = new ArrayList<AppLifeCycle.Binding>();
385         deploymentLifeCycleBindings.add(new OSGiDeployer());
386         deploymentLifeCycleBindings.add(new StandardStarter());
387         deploymentLifeCycleBindings.add(new StandardStopper());
388         deploymentLifeCycleBindings.add(new OSGiUndeployer());
389         _deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings);
390         
391         if (!providerClassNames.contains(BundleWebAppProvider.class.getName()))
392         {
393             // create it on the fly with reasonable default values.
394             try
395             {
396                 BundleWebAppProvider webAppProvider = new BundleWebAppProvider(this);
397                 _deploymentManager.addAppProvider(webAppProvider);
398             }
399             catch (Exception e)
400             {
401                 LOG.warn(e);
402             }
403         }
404 
405         if (!providerClassNames.contains(ServiceWebAppProvider.class.getName()))
406         {
407             // create it on the fly with reasonable default values.
408             try
409             {
410                 ServiceWebAppProvider webAppProvider = new ServiceWebAppProvider(this);
411                 _deploymentManager.addAppProvider(webAppProvider);
412             }
413             catch (Exception e)
414             {
415                 LOG.warn(e);
416             }
417         }
418 
419         if (!providerClassNames.contains(BundleContextProvider.class.getName()))
420         {
421             try
422             {
423                 BundleContextProvider contextProvider = new BundleContextProvider(this);
424                 _deploymentManager.addAppProvider(contextProvider);
425             }
426             catch (Exception e)
427             {
428                 LOG.warn(e);
429             }
430         }
431 
432         if (!providerClassNames.contains(ServiceContextProvider.class.getName()))
433         {
434             try
435             {
436                 ServiceContextProvider contextProvider = new ServiceContextProvider(this);
437                 _deploymentManager.addAppProvider(contextProvider);
438             }
439             catch (Exception e)
440             {
441                 LOG.warn(e);
442             }
443         }
444     }
445     
446 
447   
448     
449     
450     /* ------------------------------------------------------------ */
451     /**
452      * Get the folders that might contain jars for the legacy J2EE shared
453      * libraries
454      */
455     private List<File> extractFiles(String propertyValue)
456     {
457         StringTokenizer tokenizer = new StringTokenizer(propertyValue, ",;", false);
458         List<File> files = new ArrayList<File>();
459         while (tokenizer.hasMoreTokens())
460         {
461             String tok = tokenizer.nextToken();
462             try
463             {
464                 URL url = new URL(tok);
465                 url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(url);
466                 if (url.getProtocol().equals("file"))
467                 {
468                     Resource res = Resource.newResource(url);
469                     File folder = res.getFile();
470                     if (folder != null)
471                     {
472                         files.add(folder);
473                     }
474                 }
475             }
476             catch (Throwable mfe)
477             {
478                 LOG.warn(mfe);
479             }
480         }
481         return files;
482     }
483 
484 }