Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: ce3c141ae5ae04dec2a628b050e7de8153d6a14c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//
//  ========================================================================
//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.osgi.boot;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.regex.Pattern;

import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;



/**
 * OSGiWebInfConfiguration
 *
 * Handle adding resources found in bundle fragments, and add them into the 
 */
public class OSGiWebInfConfiguration extends WebInfConfiguration
{
    private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
    
    
    public static final String CONTAINER_BUNDLE_PATTERN = "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern";
    
    /* ------------------------------------------------------------ */
    /** 
     * Check to see if there have been any bundle symbolic names added of bundles that should be
     * regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
     * This can be defined in:
     * <ol>
     *  <li>SystemProperty SYS_PROP_TLD_BUNDLES</li>
     *  <li>DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN</li>
     *  </ol>
     *  
     *  We also allow individual bundles to specify particular bundles that might include TLDs via the Require-Tlds
     *  MANIFEST.MF header. 
     *  
     * @see org.eclipse.jetty.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
     */
    @Override
    public void preConfigure(final WebAppContext context) throws Exception
    {
        super.preConfigure(context);
        
        //Check to see if there have been any bundle symbolic names added of bundles that should be
        //regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
        //This can be defined in:
        // 1. SystemProperty SYS_PROP_TLD_BUNDLES
        // 2. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN
        String tmp = (String)context.getAttribute(CONTAINER_BUNDLE_PATTERN);
        Pattern pattern = (tmp==null?null:Pattern.compile(tmp));
        List<String> names = new ArrayList<String>();
        tmp = System.getProperty("org.eclipse.jetty.osgi.tldbundles");
        if (tmp != null)
        {
            StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false);
            while (tokenizer.hasMoreTokens())
                names.add(tokenizer.nextToken());
        }

        HashSet<Resource> matchingResources = new HashSet<Resource>();
        if ( !names.isEmpty() || pattern != null)
        {
            Bundle[] bundles = FrameworkUtil.getBundle(OSGiWebInfConfiguration.class).getBundleContext().getBundles();
           
            for (Bundle bundle : bundles)
            {
                if (pattern != null)
                {
                    // if bundle symbolic name matches the pattern
                    if (pattern.matcher(bundle.getSymbolicName()).matches())
                    {
                        //get the file location of the jar and put it into the list of container jars that will be scanned for stuff (including tlds)
                        matchingResources.addAll(getBundleAsResource(bundle));
                    }
                }               
                if (names != null)
                {
                    //if there is an explicit bundle name, then check if it matches
                    if (names.contains(bundle.getSymbolicName()))
                        matchingResources.addAll(getBundleAsResource(bundle));
                }
            }
        }
        
        for (Resource r:matchingResources)
        {
            context.getMetaData().addContainerResource(r);
        }
    }
    
    
    /* ------------------------------------------------------------ */
    /** 
     * Consider the fragment bundles associated with the bundle of the webapp being deployed.
     * 
     * 
     * @see org.eclipse.jetty.webapp.WebInfConfiguration#findJars(org.eclipse.jetty.webapp.WebAppContext)
     */
    @Override
    protected List<Resource> findJars (WebAppContext context) 
    throws Exception
    {
        List<Resource> mergedResources = new ArrayList<Resource>();
        //get jars from WEB-INF/lib if there are any
        List<Resource> webInfJars = super.findJars(context);
        if (webInfJars != null)
            mergedResources.addAll(webInfJars);
        
        //add fragment jars as if in WEB-INF/lib of the associated webapp
        Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
        for (Bundle frag : fragments)
        {
            File fragFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag);
            mergedResources.add(Resource.newResource(fragFile.toURI()));  
        }
        
        return mergedResources;
    }
    
    /* ------------------------------------------------------------ */
    /** 
     * Allow fragments to supply some resources that are added to the baseResource of the webapp.
     * 
     * The resources can be either prepended or appended to the baseResource.
     * 
     * @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
     */
    @Override
    public void configure(WebAppContext context) throws Exception
    {
        TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
        TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
             
        Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
        if (bundle != null)
        {
            //TODO anything we need to do to improve PackageAdminServiceTracker?
            Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(bundle);
            if (fragments != null && fragments.length != 0)
            {
                // sorted extra resource base found in the fragments.
                // the resources are either overriding the resourcebase found in the
                // web-bundle
                // or appended.
                // amongst each resource we sort them according to the alphabetical
                // order
                // of the name of the internal folder and the symbolic name of the
                // fragment.
                // this is useful to make sure that the lookup path of those
                // resource base defined by fragments is always the same.
                // This natural order could be abused to define the order in which
                // the base resources are
                // looked up.
                for (Bundle frag : fragments)
                {
                    String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
                    String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
                    if (fragFolder != null)
                    {
                        URL fragUrl = frag.getEntry(fragFolder);
                        if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder
                                                                                  + " inside "
                                                                                  + " the fragment '"
                                                                                  + frag.getSymbolicName()
                                                                                  + "'"); }
                        fragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(fragUrl);
                        String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
                        appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
                    }
                    if (patchFragFolder != null)
                    {
                        URL patchFragUrl = frag.getEntry(patchFragFolder);
                        if (patchFragUrl == null)
                        { 
                            throw new IllegalArgumentException("Unable to locate " + patchFragUrl
                                                               + " inside fragment '"+frag.getSymbolicName()+ "'"); 
                        }
                        patchFragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(patchFragUrl);
                        String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
                        patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
                    }
                }
                if (!appendedResourcesPath.isEmpty())
                    context.setAttribute(WebInfConfiguration.RESOURCE_DIRS, new HashSet<Resource>(appendedResourcesPath.values()));
            }
        }
        
        super.configure(context);

        // place the patch resources at the beginning of the contexts's resource base
        if (!patchResourcesPath.isEmpty())
        {
            Resource[] resources = new Resource[1+patchResourcesPath.size()];
            ResourceCollection mergedResources = new ResourceCollection (patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]));
            System.arraycopy(patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]), 0, resources, 0, patchResourcesPath.size());
            resources[resources.length-1] = context.getBaseResource();
            context.setBaseResource(new ResourceCollection(resources));
        }
    }

    
    /* ------------------------------------------------------------ */
    /**
    * Resolves the bundle. Usually that would be a single URL per bundle. But we do some more work if there are jars
    * embedded in the bundle.
    */
    private  List<Resource> getBundleAsResource(Bundle bundle)
    throws Exception
    {
        List<Resource> resources = new ArrayList<Resource>();

        File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
        if (file.isDirectory())
        {
            for (File f : file.listFiles())
            {
                if (f.getName().endsWith(".jar") && f.isFile())
                {
                    resources.add(Resource.newResource(f));
                }
                else if (f.isDirectory() && f.getName().equals("lib"))
                {
                    for (File f2 : file.listFiles())
                    {
                        if (f2.getName().endsWith(".jar") && f2.isFile())
                        {
                            resources.add(Resource.newResource(f));
                        }
                    }
                }
            }
            resources.add(Resource.newResource(file)); //TODO really???
        }
        else
        {
            resources.add(Resource.newResource(file));
        }
        
        return resources;
    }
}

Back to the top