View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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;
20  
21  import java.util.Dictionary;
22  import java.util.HashMap;
23  import java.util.Hashtable;
24  import java.util.Map;
25  
26  import org.eclipse.jetty.deploy.App;
27  import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
28  import org.eclipse.jetty.osgi.boot.utils.Util;
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.FrameworkUtil;
33  import org.osgi.framework.ServiceRegistration;
34  
35  /**
36   * BundleWebAppProvider
37   * <p>
38   * A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle.
39   */
40  public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider
41  {     
42      private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
43      
44      /**
45       * Map of Bundle to App. Used when a Bundle contains a webapp.
46       */
47      private Map<Bundle, App> _bundleMap = new HashMap<Bundle, App>();
48      
49      private ServiceRegistration _serviceRegForBundles;
50      
51  
52      /* ------------------------------------------------------------ */
53      public BundleWebAppProvider (ServerInstanceWrapper wrapper)
54      {
55          super(wrapper);
56      }
57      
58      /* ------------------------------------------------------------ */
59      /** 
60       * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
61       */
62      protected void doStart() throws Exception
63      {
64          //register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
65          Dictionary<String,String> properties = new Hashtable<String,String>();
66          properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
67          _serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
68          super.doStart();
69      }
70  
71      /* ------------------------------------------------------------ */
72      /** 
73       * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
74       */
75      @Override
76      protected void doStop() throws Exception
77      {
78          //unregister ourselves
79          if (_serviceRegForBundles != null)
80          {
81              try
82              {
83                  _serviceRegForBundles.unregister();
84              }
85              catch (Exception e)
86              {
87                  LOG.warn(e);
88              }
89          }
90       
91          super.doStop();
92      }
93      
94      
95      
96  
97      
98      
99      /* ------------------------------------------------------------ */
100     /**
101      * A bundle has been added that could be a webapp 
102      * @param bundle the bundle
103      */
104     public boolean bundleAdded (Bundle bundle) throws Exception
105     {
106         if (bundle == null)
107             return false;
108 
109         ClassLoader cl = Thread.currentThread().getContextClassLoader();
110         Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
111         String contextPath = null;
112         try 
113         {
114             Dictionary headers = bundle.getHeaders();
115 
116             //does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH 
117             String resourcePath = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH, OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers);
118             if (resourcePath != null)
119             {
120                 String base = resourcePath;
121                 contextPath = getContextPath(bundle);
122                 String originId = getOriginId(bundle, base);
123  
124                 //TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all
125                 //exceptions inside the impl of addApp. Need to send the Event and also register as a service
126                 //only if the deployment succeeded
127                 OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
128                 app.setWebAppPath(base);
129                 app.setContextPath(contextPath);
130                 _bundleMap.put(bundle, app);
131                 getDeploymentManager().addApp(app);
132                 return true;
133             }
134 
135 
136             //does the bundle have a WEB-INF/web.xml
137             if (bundle.getEntry("/WEB-INF/web.xml") != null)
138             {
139                 String base = ".";
140                 contextPath = getContextPath(bundle);
141                 String originId = getOriginId(bundle, base);
142        
143                 OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
144                 app.setContextPath(contextPath);
145                 app.setWebAppPath(base);
146                 _bundleMap.put(bundle, app);
147                 getDeploymentManager().addApp(app);               
148                 return true;
149             }
150 
151             //does the bundle define a OSGiWebappConstants.RFC66_WEB_CONTEXTPATH
152             if (headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null)
153             {
154                 //Could be a static webapp with no web.xml
155                 String base = ".";
156                 contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
157                 String originId = getOriginId(bundle,base);
158                 
159                 OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
160                 app.setContextPath(contextPath);
161                 app.setWebAppPath(base);
162                 _bundleMap.put(bundle, app);
163                 getDeploymentManager().addApp(app);                
164                 return true;
165             }
166 
167             return false;
168         }
169         catch (Exception e)
170         {
171             
172             throw e;
173         }
174         finally
175         {
176             Thread.currentThread().setContextClassLoader(cl);
177         }
178     }
179 
180     
181     /* ------------------------------------------------------------ */
182     /** 
183      * Bundle has been removed. If it was a webapp we deployed, undeploy it.
184      * 
185      * @param bundle the bundle
186      * @return true if this was a webapp we had deployed, false otherwise
187      */
188     public boolean bundleRemoved (Bundle bundle) throws Exception
189     {
190         App app = _bundleMap.remove(bundle);
191         if (app != null)
192         {
193             getDeploymentManager().removeApp(app); 
194             return true;
195         }
196         return false;
197     }
198     
199    
200 
201     
202     
203     /* ------------------------------------------------------------ */
204     private static String getContextPath(Bundle bundle)
205     {
206         Dictionary<?, ?> headers = bundle.getHeaders();
207         String contextPath = (String) headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
208         if (contextPath == null)
209         {
210             // extract from the last token of the bundle's location:
211             // (really ?could consider processing the symbolic name as an alternative
212             // the location will often reflect the version.
213             // maybe this is relevant when the file is a war)
214             String location = bundle.getLocation();
215             String toks[] = location.replace('\\', '/').split("/");
216             contextPath = toks[toks.length - 1];
217             // remove .jar, .war etc:
218             int lastDot = contextPath.lastIndexOf('.');
219             if (lastDot != -1)
220                 contextPath = contextPath.substring(0, lastDot);
221         }
222         if (!contextPath.startsWith("/"))
223             contextPath = "/" + contextPath;
224  
225         return contextPath;
226     }
227     
228     
229 
230 }