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.MalformedURLException;
23  import java.net.URL;
24  import java.util.ArrayList;
25  import java.util.Dictionary;
26  import java.util.Enumeration;
27  import java.util.Hashtable;
28  import java.util.List;
29  import java.util.StringTokenizer;
30  
31  import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
32  import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
33  import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
34  import org.eclipse.jetty.osgi.boot.utils.Util;
35  import org.eclipse.jetty.server.Server;
36  import org.eclipse.jetty.util.log.Log;
37  import org.eclipse.jetty.util.log.Logger;
38  import org.eclipse.jetty.util.resource.JarResource;
39  import org.eclipse.jetty.util.resource.Resource;
40  import org.osgi.framework.Bundle;
41  import org.osgi.framework.BundleContext;
42  
43  /**
44   * DefaultJettyAtJettyHomeHelper
45   * <p>
46   * Creates a default instance of Jetty, based on the values of the
47   * System properties "jetty.home" or "jetty.home.bundle", one of which
48   * must be specified in order to create the default instance.
49   * <p> 
50   * Called by the {@link JettyBootstrapActivator} during the starting of the
51   * bundle. 
52   */
53  public class DefaultJettyAtJettyHomeHelper
54  {
55      private static final Logger LOG = Log.getLogger(DefaultJettyAtJettyHomeHelper.class);
56  
57      /**
58       * contains a comma separated list of paths to the etc/jetty-*.xml files
59       */
60      public static final String JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
61  
62      /**
63       * Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
64       */
65      public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml";
66      
67      /**
68       * Default location within bundle of a jetty home dir.
69       */
70      public static final String DEFAULT_JETTYHOME = "/jettyhome";
71      
72      
73      
74      /* ------------------------------------------------------------ */
75      /**
76       * Called by the JettyBootStrapActivator. If the system property jetty.home
77       * is defined and points to a folder, creates a corresponding jetty
78       * server.
79       * <p>
80       * If the system property jetty.home.bundle is defined and points to a
81       * bundle, look for the configuration of jetty inside that bundle.
82       * </p>
83       * <p>
84       * In both cases reads the system property 'jetty.etc.config.urls' to locate
85       * the configuration files for the deployed jetty. It is a comma separated
86       * list of URLs or relative paths inside the bundle or folder to the config
87       * files.
88       * </p>
89       * <p>
90       * In both cases the system properties jetty.http.host, jetty.http.port and
91       * jetty.ssl.port are passed to the configuration files that might use them
92       * as part of their properties.
93       * </p>
94       * @param bundleContext the bundle context
95       * @return the configured server
96       * @throws Exception if unable to create / configure / or start the server
97       */
98      public static Server startJettyAtJettyHome(BundleContext bundleContext) throws Exception
99      {
100         String jettyHomeSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME);
101         String jettyHomeBundleSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME_BUNDLE);
102         File jettyHomeDir = null;
103         Bundle jettyHomeBundle = null;
104      
105         Dictionary<String,String> properties = new Hashtable<String,String>();
106         if (jettyHomeSysProp != null)
107         {
108             jettyHomeSysProp = Util.resolvePropertyValue(jettyHomeSysProp);
109             // bug 329621
110             if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"") || (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'")))
111                 jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1);
112          
113             if (jettyHomeBundleSysProp != null)
114                 LOG.warn("Both jetty.home and jetty.home.bundle property defined: jetty.home.bundle ignored.");
115            
116             jettyHomeDir = new File(jettyHomeSysProp);
117             if (!jettyHomeDir.exists() || !jettyHomeDir.isDirectory())
118             {
119                 LOG.warn("Unable to locate the jetty.home folder " + jettyHomeSysProp);
120                 return null;
121             }
122             
123             //set jetty.home
124             Util.setProperty(properties, OSGiServerConstants.JETTY_HOME, jettyHomeDir.getAbsolutePath());
125         }
126         else if (jettyHomeBundleSysProp != null)
127         {
128             jettyHomeBundleSysProp = Util.resolvePropertyValue(jettyHomeBundleSysProp);
129             for (Bundle b : bundleContext.getBundles())
130             {
131                 if (b.getState() == Bundle.UNINSTALLED)
132                     continue;
133                 
134                 if (b.getSymbolicName().equals(jettyHomeBundleSysProp))
135                 {
136                     jettyHomeBundle = b;
137                     break;
138                 }
139             }
140             if (jettyHomeBundle == null)
141             {
142                 LOG.warn("Unable to find the jetty.home.bundle named " + jettyHomeSysProp);
143                 return null;
144             }
145         }
146         
147         if (jettyHomeDir == null && jettyHomeBundle == null)
148         {
149             LOG.warn("No default jetty created.");
150             return null;
151         }
152         
153         
154         
155         //configure the server here rather than letting the JettyServerServiceTracker do it, because we want to be able to
156         //configure the ThreadPool, which can only be done via the constructor, ie from within the xml configuration processing
157         List<URL> configURLs = jettyHomeDir != null ? getJettyConfigurationURLs(jettyHomeDir) : getJettyConfigurationURLs(jettyHomeBundle, properties);
158 
159         LOG.info("Configuring the default jetty server with {}",configURLs);
160         String home=properties.get(OSGiServerConstants.JETTY_HOME);
161         String base=properties.get(OSGiServerConstants.JETTY_BASE);
162         if (base==null)
163             base=home;
164         LOG.info("JETTY.HOME="+home);
165         LOG.info("JETTY.BASE="+base);
166         ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
167         try
168         {
169             Thread.currentThread().setContextClassLoader(JettyBootstrapActivator.class.getClassLoader());
170             
171             // these properties usually are the ones passed to this type of
172             // configuration.
173             properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME);
174             Util.setProperty(properties, OSGiServerConstants.JETTY_HOST, System.getProperty(OSGiServerConstants.JETTY_HOST, System.getProperty("jetty.host")));
175             Util.setProperty(properties, OSGiServerConstants.JETTY_PORT, System.getProperty(OSGiServerConstants.JETTY_PORT, System.getProperty("jetty.port")));
176             Util.setProperty(properties, OSGiServerConstants.JETTY_PORT_SSL, System.getProperty(OSGiServerConstants.JETTY_PORT_SSL, System.getProperty("ssl.port")));
177             Util.setProperty(properties, OSGiServerConstants.JETTY_HOME, home);
178             Util.setProperty(properties, OSGiServerConstants.JETTY_BASE, base);
179             Server server = ServerInstanceWrapper.configure(null, configURLs, properties);
180             
181             
182             //Register the default Server instance as an OSGi service.
183             //The JettyServerServiceTracker will notice it and set it up to deploy bundles as wars etc
184             bundleContext.registerService(Server.class.getName(), server, properties);
185             LOG.info("Default jetty server configured");
186             return server;
187         }
188         catch (Exception e)
189         {
190             LOG.warn(e);
191             throw e;
192         }
193         finally
194         {
195             Thread.currentThread().setContextClassLoader(contextCl);
196         }
197     }
198 
199     
200    
201     
202     /* ------------------------------------------------------------ */
203     /**
204      * Minimum setup for the location of the configuration files given a
205      * jettyhome folder. Reads the system property jetty.etc.config.urls and
206      * look for the corresponding jetty configuration files that will be used to
207      * setup the jetty server.
208      * 
209      * @param jettyhome
210      * @return
211      * @throws MalformedURLException 
212      */
213     private static List<URL> getJettyConfigurationURLs(File jettyhome) 
214     throws MalformedURLException
215     {
216         List<URL> configURLs = new ArrayList<URL>();
217         String jettyetc = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
218         StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false);
219         while (tokenizer.hasMoreTokens())
220         {
221             String next = tokenizer.nextToken().trim();
222             //etc files can either be relative to jetty.home or absolute disk locations
223             if (!next.startsWith("/") && (next.indexOf(':') == -1))    
224                 configURLs.add(new File(jettyhome, next).toURI().toURL());
225             else 
226                 configURLs.add(new URL(next));
227         }
228         return configURLs;
229     }
230 
231     /* ------------------------------------------------------------ */
232     /**
233      * Minimum setup for the location of the configuration files given a
234      * configuration embedded inside a bundle. Reads the system property
235      * jetty.etc.config.urls and look for the corresponding jetty configuration
236      * files that will be used to setup the jetty server.
237      * 
238      * @param jettyhome
239      * @return
240      */
241     private static List<URL> getJettyConfigurationURLs(Bundle configurationBundle, Dictionary properties)
242     throws Exception
243     {
244         List<URL> configURLs = new ArrayList<URL>();
245         String files = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);       
246         StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
247 
248         while (tokenizer.hasMoreTokens())
249         {
250             String etcFile = tokenizer.nextToken().trim();
251 
252             //file path is absolute
253             if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1)
254                 configURLs.add(new URL(etcFile));
255             else //relative file path
256             {
257                 Enumeration<URL> enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, etcFile);
258 
259                 String home = null;
260                 // default for org.eclipse.osgi.boot where we look inside
261                 // jettyhome/ for the default embedded configuration.
262                 if ((enUrls == null || !enUrls.hasMoreElements()))
263                 {
264                     home = DEFAULT_JETTYHOME;
265                     String tmp = DEFAULT_JETTYHOME+(DEFAULT_JETTYHOME.endsWith("/")?"":"/")+etcFile;
266                     enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp);                    
267                     LOG.info("Configuring jetty from bundle: {} with {}", configurationBundle.getSymbolicName(),tmp);
268                 }          
269 
270                 //lazily ensure jetty.home value is set based on location of etc files
271                 if (properties.get(OSGiServerConstants.JETTY_HOME) == null)
272                 {
273                     Resource res = findDir(configurationBundle, home);
274                     if (res != null)
275                         properties.put(OSGiServerConstants.JETTY_HOME, res.toString());
276                 }
277          
278                 if (enUrls == null || !enUrls.hasMoreElements())
279                     throw new IllegalStateException ("Unable to locate a jetty configuration file for " + etcFile);
280 
281                 URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(enUrls.nextElement());
282                 configURLs.add(url);
283            
284             }
285         }
286         return configURLs;
287     }
288     
289     /* ------------------------------------------------------------ */
290     /**
291      * Get a resource representing a directory inside a bundle. If the dir is null,
292      * return a resource representing the installation location of the bundle.
293      * @param bundle the bundle
294      * @param dir the directory
295      * @return the resource found
296      */
297     public static Resource findDir (Bundle bundle, String dir)
298     {
299         if (bundle == null)
300             return null;
301 
302         try
303         {
304             File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
305             URL u = f.toURI().toURL();
306             u = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(u);
307             Resource res = Resource.newResource(u);
308             String s = res.toString();
309    
310             //check if it is an unarchived bundle
311             if (s.endsWith(".jar") && s.startsWith("file:"))
312                 res = JarResource.newJarResource(res);
313             
314             //if looking for a directory 
315             if (dir != null)
316                 res = res.addPath(dir);
317             
318             return res;
319           
320         }
321         catch (Exception e)
322         {
323             LOG.warn("Bad bundle location" , e);
324             return null;
325         }
326     }
327 }