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