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