View Javadoc

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