View Javadoc

1   // ========================================================================
2   // Copyright (c) 2009-2010 Mortbay, 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  //    Greg Wilkins - initial API and implementation
14  // ========================================================================
15  package org.eclipse.jetty.osgi.boot;
16  
17  import java.io.File;
18  import java.io.FilenameFilter;
19  import java.io.IOException;
20  import java.util.Map.Entry;
21  
22  import org.eclipse.jetty.deploy.App;
23  import org.eclipse.jetty.deploy.AppProvider;
24  import org.eclipse.jetty.deploy.DeploymentManager;
25  import org.eclipse.jetty.deploy.providers.ContextProvider;
26  import org.eclipse.jetty.deploy.providers.ScanningAppProvider;
27  import org.eclipse.jetty.server.handler.ContextHandler;
28  import org.eclipse.jetty.util.resource.Resource;
29  import org.eclipse.jetty.webapp.WebAppContext;
30  import org.osgi.framework.Bundle;
31  
32  /**
33   * AppProvider for OSGi. Supports the configuration of ContextHandlers and
34   * WebApps. Extends the AbstractAppProvider to support the scanning of context
35   * files located outside of the bundles.
36   * <p>
37   * This provider must not be called outside of jetty.boot: it should always be
38   * called via the OSGi service listener.
39   * </p>
40   * <p>
41   * This provider supports the same set of parameters than the WebAppProvider as
42   * it supports the deployment of WebAppContexts. Except for the scanning of the
43   * webapps directory.
44   * </p>
45   */
46  public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
47  {
48  
49      private boolean _extractWars = true;
50      private boolean _parentLoaderPriority = false;
51      private String _defaultsDescriptor;
52      private String _tldBundles;
53      
54      /**
55       * When a context file corresponds to a deployed bundle and is changed we
56       * reload the corresponding bundle.
57       */
58      private static class Filter implements FilenameFilter
59      {
60          OSGiAppProvider _enclosedInstance;
61  
62          public boolean accept(File dir, String name)
63          {
64              if (!new File(dir,name).isDirectory())
65              {
66                  String contextName = getDeployedAppName(name);
67                  if (contextName != null)
68                  {
69                      App app = _enclosedInstance.getDeployedApps().get(contextName);
70                      return app != null;
71                  }
72              }
73              return false;
74          }
75      }
76  
77      /**
78       * @param contextFileName
79       *            for example myContext.xml
80       * @return The context, for example: myContext; null if this was not a
81       *         suitable contextFileName.
82       */
83      private static String getDeployedAppName(String contextFileName)
84      {
85          String lowername = contextFileName.toLowerCase();
86          if (lowername.endsWith(".xml"))
87          {
88              String contextName = contextFileName.substring(0,lowername.length() - ".xml".length());
89              return contextName;
90          }
91          return null;
92      }
93  
94      /**
95       * Reading the display name of a webapp is really not sufficient for indexing the various
96       * deployed ContextHandlers.
97       * 
98       * @param context
99       * @return
100      */
101     private String getContextHandlerAppName(ContextHandler context) {
102         String appName = context.getDisplayName();
103         if (appName == null || appName.length() == 0  || getDeployedApps().containsKey(appName)) {
104         	if (context instanceof WebAppContext)
105         	{
106         		appName = ((WebAppContext)context).getContextPath();
107         		if (getDeployedApps().containsKey(appName)) {
108             		appName = "noDisplayName"+context.getClass().getSimpleName()+context.hashCode();
109             	}
110         	} else {
111         		appName = "noDisplayName"+context.getClass().getSimpleName()+context.hashCode();
112         	}
113         }
114         return appName;
115     }
116     
117     /**
118      * Default OSGiAppProvider consutructed when none are defined in the
119      * jetty.xml configuration.
120      */
121     public OSGiAppProvider()
122     {
123         super(new Filter());
124         ((Filter)super._filenameFilter)._enclosedInstance = this;
125     }
126 
127     /**
128      * Default OSGiAppProvider consutructed when none are defined in the
129      * jetty.xml configuration.
130      * 
131      * @param contextsDir
132      */
133     public OSGiAppProvider(File contextsDir) throws IOException
134     {
135         this();
136         setMonitoredDir(Resource.newResource(contextsDir.toURI()));
137     }
138     
139     /**
140      * Returns the ContextHandler that was created by WebappRegistractionHelper
141      * 
142      * @see AppProvider
143      */
144     public ContextHandler createContextHandler(App app) throws Exception
145     {
146         // return pre-created Context
147         if (app.getContextHandler() != null)
148         {
149             return app.getContextHandler();
150         }
151         // for some reason it was not defined when the App was constructed.
152         // we don't support this situation at this point.
153         // once the WebAppRegistrationHelper is refactored, the code
154         // that creates the ContextHandler will actually be here.
155         throw new IllegalStateException("The App must be passed the " + "instance of the ContextHandler when it is construsted");
156     }
157 
158     /**
159      * @see AppProvider
160      */
161     public void setDeploymentManager(DeploymentManager deploymentManager)
162     {
163         // _manager=deploymentManager;
164         super.setDeploymentManager(deploymentManager);
165     }
166 
167     private static String getOriginId(Bundle contributor, String pathInBundle)
168     {
169     	return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() +
170     		(pathInBundle.startsWith("/") ? pathInBundle : "/" + pathInBundle);
171     }
172     
173     /**
174      * @param context
175      * @throws Exception
176      */
177     public void addContext(Bundle contributor, String pathInBundle, ContextHandler context) throws Exception
178     {
179     	addContext(getOriginId(contributor, pathInBundle), context);
180     }
181     /**
182      * @param context
183      * @throws Exception
184      */
185     public void addContext(String originId, ContextHandler context) throws Exception
186     {
187         // TODO apply configuration specific to this provider
188     	if (context instanceof WebAppContext)
189     	{
190     		((WebAppContext)context).setExtractWAR(isExtract());
191     	}
192 
193         // wrap context as an App
194         App app = new App(getDeploymentManager(),this,originId,context);
195         String appName = getContextHandlerAppName(context);
196         getDeployedApps().put(appName,app);
197         getDeploymentManager().addApp(app);
198     }
199     
200     
201 
202     /**
203      * Called by the scanner of the context files directory. If we find the
204      * corresponding deployed App we reload it by returning the App. Otherwise
205      * we return null and nothing happens: presumably the corresponding OSGi
206      * webapp is not ready yet.
207      * 
208      * @return the corresponding already deployed App so that it will be
209      *         reloaded. Otherwise returns null.
210      */
211     @Override
212     protected App createApp(String filename)
213     {
214         // find the corresponding bundle and ContextHandler or WebAppContext
215         // and reload the corresponding App.
216         // see the 2 pass of the refactoring of the WebAppRegistrationHelper.
217         String name = getDeployedAppName(filename);
218         if (name != null)
219         {
220             return getDeployedApps().get(name);
221         }
222         return null;
223     }
224 
225     public void removeContext(ContextHandler context) throws Exception
226     {
227     	String appName = getContextHandlerAppName(context);
228         App app = getDeployedApps().remove(context.getDisplayName());
229         if (app == null) {
230         	//try harder to undeploy this context handler.
231         	//see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=330098
232         	appName = null;
233         	for (Entry<String,App> deployedApp : getDeployedApps().entrySet()) {
234         		if (deployedApp.getValue().getContextHandler() == context) {
235         			app = deployedApp.getValue();
236         			appName = deployedApp.getKey();
237         			break;
238         		}
239         	}
240         	if (appName != null) {
241         		getDeployedApps().remove(appName);
242         	}
243         }
244         if (app != null)
245         {
246             getDeploymentManager().removeApp(app);
247         }
248     }
249 
250     // //copied from WebAppProvider as the parameters are identical.
251     // //only removed the parameer related to extractWars.
252     /* ------------------------------------------------------------ */
253     /**
254      * Get the parentLoaderPriority.
255      * 
256      * @return the parentLoaderPriority
257      */
258     public boolean isParentLoaderPriority()
259     {
260         return _parentLoaderPriority;
261     }
262 
263     /* ------------------------------------------------------------ */
264     /**
265      * Set the parentLoaderPriority.
266      * 
267      * @param parentLoaderPriority
268      *            the parentLoaderPriority to set
269      */
270     public void setParentLoaderPriority(boolean parentLoaderPriority)
271     {
272         _parentLoaderPriority = parentLoaderPriority;
273     }
274 
275     /* ------------------------------------------------------------ */
276     /**
277      * Get the defaultsDescriptor.
278      * 
279      * @return the defaultsDescriptor
280      */
281     public String getDefaultsDescriptor()
282     {
283         return _defaultsDescriptor;
284     }
285 
286     /* ------------------------------------------------------------ */
287     /**
288      * Set the defaultsDescriptor.
289      * 
290      * @param defaultsDescriptor
291      *            the defaultsDescriptor to set
292      */
293     public void setDefaultsDescriptor(String defaultsDescriptor)
294     {
295         _defaultsDescriptor = defaultsDescriptor;
296     }
297 
298     /**
299      * The context xml directory. In fact it is the directory watched by the
300      * scanner.
301      */
302     public File getContextXmlDirAsFile()
303     {
304         try
305         {
306             Resource monitoredDir = getMonitoredDir();
307             if (monitoredDir == null)
308                 return null;
309             return monitoredDir.getFile();
310         }
311         catch (IOException e)
312         {
313             e.printStackTrace();
314             return null;
315         }
316     }
317 
318     /* ------------------------------------------------------------ */
319     /**
320      * The context xml directory. In fact it is the directory watched by the
321      * scanner.
322      */
323     public String getContextXmlDir()
324     {
325         try
326         {
327             Resource monitoredDir = getMonitoredDir();
328             if (monitoredDir == null)
329                 return null;
330             return monitoredDir.getFile().toURI().toString();
331         }
332         catch (IOException e)
333         {
334             e.printStackTrace();
335             return null;
336         }
337     }
338     
339     public boolean isExtract()
340     {
341         return _extractWars;
342     }
343 
344     public void setExtract(boolean extract)
345     {
346         _extractWars=extract;
347     }
348 
349 
350     /* ------------------------------------------------------------ */
351     /**
352      * Set the directory in which to look for context XML files.
353      * <p>
354      * If a webapp call "foo/" or "foo.war" is discovered in the monitored
355      * directory, then the ContextXmlDir is examined to see if a foo.xml file
356      * exists. If it does, then this deployer will not deploy the webapp and the
357      * ContextProvider should be used to act on the foo.xml file.
358      * 
359      * @see ContextProvider
360      * @param contextsDir
361      */
362     public void setContextXmlDir(String contextsDir)
363     {
364         setMonitoredDir(contextsDir);
365     }
366     
367     /**
368      * @param tldBundles Comma separated list of bundles that contain tld jars
369      * that should be setup on the jetty instances created here.
370      */
371     public void setTldBundles(String tldBundles)
372     {
373     	_tldBundles = tldBundles;
374     }
375     
376     /**
377      * @return The list of bundles that contain tld jars that should be setup
378      * on the jetty instances created here.
379      */
380     public String getTldBundles()
381     {
382     	return _tldBundles;
383     }
384 
385 }