View Javadoc

1   // ========================================================================
2   // Copyright (c) 2009 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  // ========================================================================
13  package org.eclipse.jetty.osgi.boot.internal.webapp;
14  
15  import java.net.URL;
16  import java.util.Dictionary;
17  
18  import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
19  import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
20  import org.osgi.framework.Bundle;
21  import org.osgi.framework.BundleContext;
22  import org.osgi.framework.BundleEvent;
23  import org.osgi.framework.BundleListener;
24  
25  /**
26   * Support bundles that declare the webapp directly through headers in their
27   * manifest.
28   * <p>
29   * Those headers will define a new WebApplication:
30   * <ul>
31   * <li>Web-ContextPath</li>
32   * <li>Jetty-WarFolderPath</li>
33   * </ul>
34   * </p>
35   * <p>
36   * Those headers will define a new app started via a jetty-context or a list of
37   * them. ',' column is the separator between the various context files.
38   * <ul>
39   * <li>Jetty-ContextFilePath</li>
40   * </ul>
41   * </p>
42   * And generate a jetty WebAppContext or another ContextHandler then registers
43   * it as service. Kind of simpler than declarative services and their xml files.
44   * Also avoid having the contributing bundle depend on jetty's package for
45   * WebApp.
46   * 
47   * @author hmalphettes
48   */
49  public class JettyContextHandlerExtender implements BundleListener
50  {
51  
52      /**
53       * Receives notification that a bundle has had a lifecycle change.
54       * 
55       * @param event
56       *            The <code>BundleEvent</code>.
57       */
58      public void bundleChanged(BundleEvent event)
59      {
60          switch (event.getType())
61          {
62              case BundleEvent.STARTED:
63                  register(event.getBundle());
64                  break;
65              case BundleEvent.STOPPING:
66                  unregister(event.getBundle());
67                  break;
68          }
69      }
70  
71      /**
72  	 * 
73  	 */
74      public void init(BundleContext context)
75      {
76          Bundle bundles[] = context.getBundles();
77          for (int i = 0; i < bundles.length; i++)
78          {
79              if ((bundles[i].getState() & (Bundle.STARTING | Bundle.ACTIVE)) != 0)
80              {
81                  register(bundles[i]);
82              }
83          }
84      }
85  
86      private void register(Bundle bundle)
87      {
88          Dictionary<?, ?> dic = bundle.getHeaders();
89          String warFolderRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
90          if (warFolderRelativePath != null)
91          {
92              String contextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
93              if (contextPath == null || !contextPath.startsWith("/"))
94              {
95                  throw new IllegalArgumentException();
96              }
97              // create the corresponding service and publish it in the context of
98              // the contributor bundle.
99              try
100             {
101                 JettyBootstrapActivator.registerWebapplication(bundle,warFolderRelativePath,contextPath);
102             }
103             catch (Throwable e)
104             {
105                 // TODO Auto-generated catch block
106                 e.printStackTrace();
107             }
108         }
109         else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null)
110         {
111             String contextFileRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
112             if (contextFileRelativePath == null)
113             {
114                 // nothing to register here.
115                 return;
116             }
117             // support for multiple webapps in the same bundle:
118             String[] pathes = contextFileRelativePath.split(",;");
119             for (String path : pathes)
120             {
121                 try
122                 {
123                     JettyBootstrapActivator.registerContext(bundle,path.trim());
124                 }
125                 catch (Throwable e)
126                 {
127                     // TODO Auto-generated catch block
128                     e.printStackTrace();
129                 }
130             }
131         }
132         else
133         {
134             // support for OSGi-RFC66; disclaimer, no access to the actual
135             // (draft) of the spec: just a couple of posts on the
136             // world-wide-web.
137             URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
138             if (rfc66Webxml == null)
139             {
140                 return;// no webapp in here
141             }
142             // this is risky: should we make sure that there is no classes and
143             // jars directly available
144             // at the root of the of the bundle: otherwise they are accessible
145             // through the browser. we should enforce that the whole classpath
146             // is
147             // pointing to files and folders inside WEB-INF. We should
148             // filter-out
149             // META-INF too
150             String rfc66ContextPath = getWebContextPath(bundle,dic);
151             try
152             {
153                 JettyBootstrapActivator.registerWebapplication(bundle,".",rfc66ContextPath);
154             }
155             catch (Throwable e)
156             {
157                 // TODO Auto-generated catch block
158                 e.printStackTrace();
159             }
160         }
161     }
162 
163     private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic)
164     {
165         String rfc66ContextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
166         if (rfc66ContextPath == null)
167         {
168             // extract from the last token of the bundle's location:
169             // (really ?
170             // could consider processing the symbolic name as an alternative
171             // the location will often reflect the version.
172             // maybe this is relevant when the file is a war)
173             String location = bundle.getLocation();
174             String toks[] = location.replace('\\','/').split("/");
175             rfc66ContextPath = toks[toks.length - 1];
176             // remove .jar, .war etc:
177             int lastDot = rfc66ContextPath.lastIndexOf('.');
178             if (lastDot != -1)
179             {
180                 rfc66ContextPath = rfc66ContextPath.substring(0,lastDot);
181             }
182         }
183         if (!rfc66ContextPath.startsWith("/"))
184         {
185             rfc66ContextPath = "/" + rfc66ContextPath;
186         }
187         return rfc66ContextPath;
188     }
189 
190     private void unregister(Bundle bundle)
191     {
192         // nothing to do: when the bundle is stopped, each one of its service
193         // reference is also stopped and that is what we use to stop the
194         // corresponding
195         // webapps registered in that bundle.
196     }
197 
198 }