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