View Javadoc

1   // ========================================================================
2   // Copyright (c) 2010 Intalio, Inc.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // Contributors:
13  //    Hugues Malphettes - initial API and implementation
14  // ========================================================================
15  package org.eclipse.jetty.osgi.boot.internal.serverfactory;
16  
17  import java.io.File;
18  import java.net.MalformedURLException;
19  import java.net.URL;
20  import java.util.Dictionary;
21  import java.util.Enumeration;
22  import java.util.Hashtable;
23  import java.util.StringTokenizer;
24  
25  import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
26  import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
27  import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
28  import org.eclipse.jetty.server.Server;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  import org.osgi.framework.Bundle;
32  import org.osgi.framework.BundleContext;
33  
34  /**
35   * Called by the {@link JettyBootstrapActivator} during the starting of the
36   * bundle. If the system property 'jetty.home' is defined and points to a
37   * folder, then setup the corresponding jetty server.
38   */
39  public class DefaultJettyAtJettyHomeHelper
40  {
41      private static final Logger LOG = Log.getLogger(DefaultJettyAtJettyHomeHelper.class);
42  
43      /**
44       * contains a comma separated list of pathes to the etc/jetty-*.xml files
45       * used to configure jetty. By default the value is 'etc/jetty.xml' when the
46       * path is relative the file is resolved relatively to jettyhome.
47       */
48      public static final String SYS_PROP_JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
49  
50      /**
51       * Usual system property used as the hostname for a typical jetty
52       * configuration.
53       */
54      public static final String SYS_PROP_JETTY_HOME = "jetty.home";
55  
56      /**
57       * System property to point to a bundle that embeds a jetty configuration
58       * and that jetty configuration should be the default jetty server. First we
59       * look for jetty.home. If we don't find it then we look for this property.
60       */
61      public static final String SYS_PROP_JETTY_HOME_BUNDLE = "jetty.home.bundle";
62  
63      /**
64       * Usual system property used as the hostname for a typical jetty
65       * configuration.
66       */
67      public static final String SYS_PROP_JETTY_HOST = "jetty.host";
68  
69      /**
70       * Usual system property used as the port for http for a typical jetty
71       * configuration.
72       */
73      public static final String SYS_PROP_JETTY_PORT = "jetty.port";
74  
75      /**
76       * Usual system property used as the port for https for a typical jetty
77       * configuration.
78       */
79      public static final String SYS_PROP_JETTY_PORT_SSL = "jetty.port.ssl";
80      
81      /**
82       * Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
83       */
84      public static final String DEFAULT_JETTY_ETC_FILES = "jetty.xml,jetty-selector.xml,jetty-deployer.xml";
85      
86      /**
87       * Default location within bundle of a jetty home dir.
88       */
89      public static final String DEFAULT_JETTYHOME = "/jettyhome";
90  
91      /**
92       * Called by the JettyBootStrapActivator. If the system property jetty.home
93       * is defined and points to a folder, creates a corresponding jetty
94       * server.
95       * <p>
96       * If the system property jetty.home.bundle is defined and points to a
97       * bundle, look for the configuration of jetty inside that bundle.
98       * </p>
99       * <p>
100      * In both cases reads the system property 'jetty.etc.config.urls' to locate
101      * the configuration files for the deployed jetty. It is a comma separated
102      * list of URLs or relative paths inside the bundle or folder to the config
103      * files. If undefined it defaults to 'etc/jetty.xml'. In the case of the jetty.home.bundle,
104      * if no etc/jetty.xml file is found in the bundle, it will look for 
105      * /jettyhome/etc/jetty-osgi-default.xml
106      * </p>
107      * <p>
108      * In both cases the system properties jetty.host, jetty.port and
109      * jetty.port.ssl are passed to the configuration files that might use them
110      * as part of their properties.
111      * </p>
112      */
113     public static void startJettyAtJettyHome(BundleContext bundleContext) throws Exception
114     {
115         String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME);
116         String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE);
117         File jettyHome = null;
118         Bundle jettyHomeBundle = null;
119         if (jettyHomeSysProp != null)
120         {
121             jettyHomeSysProp = resolvePropertyValue(jettyHomeSysProp);
122             // bug 329621
123             if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"") || (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'")))
124             {
125                 jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1);
126             }
127             if (jettyHomeBundleSysProp != null)
128             {
129                 LOG.warn("Both jetty.home and jetty.home.bundle property defined: jetty.home.bundle ignored.");
130             }
131             jettyHome = new File(jettyHomeSysProp);
132             if (!jettyHome.exists() || !jettyHome.isDirectory())
133             {
134                 LOG.warn("Unable to locate the jetty.home folder " + jettyHomeSysProp);
135                 return;
136             }
137         }
138         else if (jettyHomeBundleSysProp != null)
139         {
140             jettyHomeBundleSysProp = resolvePropertyValue(jettyHomeBundleSysProp);
141             for (Bundle b : bundleContext.getBundles())
142             {
143                 if (b.getSymbolicName().equals(jettyHomeBundleSysProp))
144                 {
145                     jettyHomeBundle = b;
146                     break;
147                 }
148             }
149             if (jettyHomeBundle == null)
150             {
151                 LOG.warn("Unable to find the jetty.home.bundle named " + jettyHomeSysProp);
152                 return;
153             }
154 
155         }
156         if (jettyHome == null && jettyHomeBundle == null)
157         {
158             LOG.warn("No default jetty created.");
159             return;
160         }
161 
162         Server server = new Server();
163         Dictionary<String,String> properties = new Hashtable<String,String>();
164         properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME);
165 
166         String configURLs = jettyHome != null ? getJettyConfigurationURLs(jettyHome) : getJettyConfigurationURLs(jettyHomeBundle);
167         properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, configURLs);
168 
169         LOG.info("Configuring the default jetty server with " + configURLs);
170 
171         // these properties usually are the ones passed to this type of
172         // configuration.
173         setProperty(properties, SYS_PROP_JETTY_HOME, System.getProperty(SYS_PROP_JETTY_HOME));
174         setProperty(properties, SYS_PROP_JETTY_HOST, System.getProperty(SYS_PROP_JETTY_HOST));
175         setProperty(properties, SYS_PROP_JETTY_PORT, System.getProperty(SYS_PROP_JETTY_PORT));
176         setProperty(properties, SYS_PROP_JETTY_PORT_SSL, System.getProperty(SYS_PROP_JETTY_PORT_SSL));
177 
178         //register the Server instance as an OSGi service.
179         bundleContext.registerService(Server.class.getName(), server, properties);
180         // hookNestedConnectorToBridgeServlet(server);
181 
182     }
183 
184     /**
185      * Minimum setup for the location of the configuration files given a
186      * jettyhome folder. Reads the system property jetty.etc.config.urls and
187      * look for the corresponding jetty configuration files that will be used to
188      * setup the jetty server.
189      * 
190      * @param jettyhome
191      * @return
192      */
193     private static String getJettyConfigurationURLs(File jettyhome)
194     {
195         String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES, "etc/jetty.xml");
196         StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false);
197         StringBuilder res = new StringBuilder();
198         while (tokenizer.hasMoreTokens())
199         {
200             String next = tokenizer.nextToken().trim();
201             if (!next.startsWith("/") && next.indexOf(':') == -1)
202             {
203                 try
204                 {
205                     next = new File(jettyhome, next).toURI().toURL().toString();
206                 }
207                 catch (MalformedURLException e)
208                 {
209                     LOG.warn(e);
210                     continue;
211                 }
212             }
213             appendToCommaSeparatedList(res, next);
214         }
215         return res.toString();
216     }
217 
218     /**
219      * Minimum setup for the location of the configuration files given a
220      * configuration embedded inside a bundle. Reads the system property
221      * jetty.etc.config.urls and look for the corresponding jetty configuration
222      * files that will be used to setup the jetty server.
223      * 
224      * @param jettyhome
225      * @return
226      */
227     private static String getJettyConfigurationURLs(Bundle configurationBundle)
228     {
229         String files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
230        
231         StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
232         StringBuilder res = new StringBuilder();
233 
234         while (tokenizer.hasMoreTokens())
235         {
236             String etcFile = tokenizer.nextToken().trim();
237             if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1)
238             {
239                 //file path is absolute
240                 appendToCommaSeparatedList(res, etcFile);
241             }
242             else
243             {
244                 //relative file path
245                 Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, etcFile);
246 
247                 // default for org.eclipse.osgi.boot where we look inside
248                 // jettyhome for the default embedded configuration.
249                 // default inside jettyhome. this way fragments to the bundle
250                 // can define their own configuration.
251                 if ((enUrls == null || !enUrls.hasMoreElements()))
252                 {
253                     String tmp = DEFAULT_JETTYHOME+"/etc/"+etcFile;
254                     enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, tmp);
255                     LOG.info("Configuring jetty with the default embedded configuration:" + "bundle: "
256                                        + configurationBundle.getSymbolicName()
257                                        + " config: "+tmp);
258                 }
259                 if (enUrls == null || !enUrls.hasMoreElements())
260                 {
261                     LOG.warn("Unable to locate a jetty configuration file for " + etcFile);
262                 }
263                 if (enUrls != null)
264                 {
265                     while (enUrls.hasMoreElements())
266                     {
267                         appendToCommaSeparatedList(res, enUrls.nextElement().toString());
268                     }
269                 }
270             }
271         }
272         return res.toString();
273     }
274 
275     private static void appendToCommaSeparatedList(StringBuilder buffer, String value)
276     {
277         if (buffer.length() != 0)
278         {
279             buffer.append(",");
280         }
281         buffer.append(value);
282     }
283 
284     private static void setProperty(Dictionary<String,String> properties, String key, String value)
285     {
286         if (value != null)
287         {
288             properties.put(key, value);
289         }
290     }
291 
292     /**
293      * recursively substitute the ${sysprop} by their actual system property.
294      * ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no
295      * sysprop is defined. Not the most efficient code but we are shooting for
296      * simplicity and speed of development here.
297      * 
298      * @param value
299      * @return
300      */
301     public static String resolvePropertyValue(String value)
302     {
303         int ind = value.indexOf("${");
304         if (ind == -1) { return value; }
305         int ind2 = value.indexOf('}', ind);
306         if (ind2 == -1) { return value; }
307         String sysprop = value.substring(ind + 2, ind2);
308         String defaultValue = null;
309         int comma = sysprop.indexOf(',');
310         if (comma != -1 && comma + 1 != sysprop.length())
311         {
312             defaultValue = sysprop.substring(comma + 1);
313             defaultValue = resolvePropertyValue(defaultValue);
314             sysprop = sysprop.substring(0, comma);
315         }
316         else
317         {
318             defaultValue = "${" + sysprop + "}";
319         }
320 
321         String v = System.getProperty(sysprop);
322 
323         String reminder = value.length() > ind2 + 1 ? value.substring(ind2 + 1) : "";
324         reminder = resolvePropertyValue(reminder);
325         if (v != null)
326         {
327             return value.substring(0, ind) + v + reminder;
328         }
329         else
330         {
331             return value.substring(0, ind) + defaultValue + reminder;
332         }
333     }
334 }