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