View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2011 Mort Bay Consulting Pty. Ltd.
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.jsp;
14  
15  import java.io.IOException;
16  import java.net.URL;
17  import java.util.Collection;
18  import java.util.Enumeration;
19  import java.util.LinkedHashSet;
20  
21  import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
22  import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
23  import org.eclipse.jetty.util.log.Log;
24  import org.eclipse.jetty.util.log.Logger;
25  import org.eclipse.jetty.util.resource.Resource;
26  import org.eclipse.jetty.webapp.TagLibConfiguration;
27  import org.eclipse.jetty.webapp.WebAppContext;
28  import org.osgi.framework.Bundle;
29  import org.osgi.framework.BundleReference;
30  import org.osgi.service.packageadmin.PackageAdmin;
31  import org.osgi.util.tracker.ServiceTracker;
32  
33  /**
34   * <p>
35   * Replacement for {@link TagLibConfiguration} for the OSGi integration.
36   * </p>
37   * <p>
38   * In the case of a WAB, tlds can be located in OSGi bundles that are dependencies
39   * of the WAB.
40   * It is expected that each WAB lists the symbolic-names of the bundles that contain
41   * tld files. The list is defined as the value of the header 'Require-TldBundle'
42   * </p>
43   * <p>
44   * Discussions about this are logged in https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971
45   * </p>
46   */
47  public class TagLibOSGiConfiguration extends TagLibConfiguration
48  {
49      private static final Logger LOG = Log.getLogger(TagLibOSGiConfiguration.class);
50  
51  	private ServiceTracker packageAdminServiceTracker = null;
52  	
53  	/**
54  	 * Override the preConfigure; locates the bundles that contain
55  	 * tld files according to the value of the manifest header Require-TldBundle.
56  	 * <p>
57  	 * Set or add to the property TldProcessor.TLDResources the list of located jars
58  	 * so that the super class will scan those.
59  	 * </p>
60  	 */
61      public void preConfigure(WebAppContext context) throws Exception
62      {
63      	String requireTldBundle = (String)context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
64      	if (requireTldBundle != null)
65      	{
66      		Collection<Resource> resources = getRequireTldBundleAsJettyResources(context, requireTldBundle);
67      		if (resources != null && !resources.isEmpty())
68      		{
69      			Collection<Resource> previouslySet = (Collection<Resource>)
70      				context.getAttribute(TagLibConfiguration.TLD_RESOURCES);
71      			if (previouslySet != null)
72      			{
73      				resources.addAll(previouslySet);
74      			}
75      			context.setAttribute(TagLibConfiguration.TLD_RESOURCES, resources);
76      		}
77      	}
78      	super.preConfigure(context);
79  
80      }
81  
82      /**
83       * @param requireTldBundle The comma separated list of bundles' symbolic names
84       * that contain tld for this osgi webapp.
85       * @return The collection of jars or folders that match those bundles.
86       */
87      private Collection<Resource> getRequireTldBundleAsJettyResources(
88      		WebAppContext context, String requireTldBundle)
89      {
90      	Bundle bundle = (Bundle)
91      		context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
92      	PackageAdmin packAdmin = getBundleAdmin();
93      	String[] symbNames = requireTldBundle.split(", ");
94      	Collection<Resource> tlds = new LinkedHashSet<Resource>();
95      	for (String symbName : symbNames)
96      	{
97      		Bundle[] bs = packAdmin.getBundles(symbName, null);
98      		if (bs == null || bs.length == 0)
99      		{
100     			throw new IllegalArgumentException("Unable to locate the bundle '"
101     					+ symbName + "' specified in the "
102     					+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
103     					+ " of the manifest of "
104     					+ bundle.getSymbolicName());
105     		}
106     		//take the first one as it is the most recent version?
107 			Enumeration<URL> en = bs[0].findEntries("META-INF", "*.tld", false);
108 			boolean atLeastOneTldFound = false;
109 			while (en.hasMoreElements())
110 			{
111 				atLeastOneTldFound = true;
112 				URL oriUrl = en.nextElement();
113 				URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl);
114 				Resource tldResource;
115 				try
116 				{
117 					tldResource = Resource.newResource(url);
118 				}
119 				catch (IOException e)
120 				{
121 					throw new IllegalArgumentException("Unable to locate the "
122 							+ "tld resource in '"
123 	    					+ url.toString()
124 	    					+ "' in the bundle '" + bs[0].getSymbolicName()
125 	    					+ "' while registering the "
126 	    					+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
127 	    					+ " of the manifest of "
128 	    					+ bundle.getSymbolicName(), e);
129 				}
130 				tlds.add(tldResource);
131 			}
132 			if (!atLeastOneTldFound)
133 			{
134 				LOG.warn("No '/META-INF/*.tld' resources were found "
135     					+ " in the bundle '" + bs[0].getSymbolicName()
136     					+ "' while registering the "
137     					+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
138     					+ " of the manifest of "
139     					+ bundle.getSymbolicName());
140 			}
141     	}
142     	return tlds;
143     }
144     
145 	private PackageAdmin getBundleAdmin()
146 	{
147 		if (packageAdminServiceTracker == null)
148 		{
149 			Bundle bootBundle = ((BundleReference)OSGiWebappConstants.class.getClassLoader()).getBundle();
150 			packageAdminServiceTracker = new ServiceTracker(bootBundle.getBundleContext(),
151 					PackageAdmin.class.getName(), null);
152 			packageAdminServiceTracker.open();
153 		}
154 		return (PackageAdmin) packageAdminServiceTracker.getService();
155 	}
156 	    
157 }