~ubuntu-branches/ubuntu/trusty/ehcache/trusty

« back to all changes in this revision

Viewing changes to src/main/java/net/sf/ehcache/management/ResourceClassLoader.java

  • Committer: Package Import Robot
  • Author(s): Emmanuel Bourg
  • Date: 2013-05-06 14:53:07 UTC
  • mfrom: (1.1.7) (2.1.8 sid)
  • Revision ID: package-import@ubuntu.com-20130506145307-v5bhw5yu70re00l3
Tags: 2.6.7-1
* Team upload.
* New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 *  Copyright Terracotta, Inc.
 
3
 *
 
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
 
5
 *  you may not use this file except in compliance with the License.
 
6
 *  You may obtain a copy of the License at
 
7
 *
 
8
 *      http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 *  Unless required by applicable law or agreed to in writing, software
 
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
 
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 *  See the License for the specific language governing permissions and
 
14
 *  limitations under the License.
 
15
 */
 
16
package net.sf.ehcache.management;
 
17
 
 
18
import java.io.ByteArrayOutputStream;
 
19
import java.io.File;
 
20
import java.io.IOException;
 
21
import java.io.InputStream;
 
22
import java.lang.reflect.InvocationTargetException;
 
23
import java.lang.reflect.Method;
 
24
import java.net.URL;
 
25
import java.net.URLConnection;
 
26
import java.util.ArrayList;
 
27
import java.util.Collections;
 
28
import java.util.Enumeration;
 
29
import java.util.List;
 
30
import java.util.jar.Attributes;
 
31
import java.util.jar.Manifest;
 
32
import java.util.jar.Attributes.Name;
 
33
 
 
34
import net.sf.ehcache.util.MergedEnumeration;
 
35
 
 
36
import org.slf4j.Logger;
 
37
import org.slf4j.LoggerFactory;
 
38
 
 
39
/**
 
40
 * ResourceClassLoader can load classes nested in a subdirectory of a jar
 
41
 * Example :
 
42
 * ehcache.jar!/net/sf/ehcache/CacheManager is in the "normal" classpath and will be loaded by any typical classloader
 
43
 * ehcache.jar!/subdirectory/net/sf/ehcache/CacheManager can only be loaded by the ResourceClassLoader, with prefix "subdirectory"
 
44
 *
 
45
 * @author Anthony Dahanne
 
46
 *
 
47
 */
 
48
public class ResourceClassLoader extends ClassLoader {
 
49
 
 
50
    private static final int BUFFER_SIZE = 1024;
 
51
    private static final Logger LOG = LoggerFactory.getLogger(ResourceClassLoader.class);
 
52
    private final String prefix;
 
53
    private final String implementationVersion;
 
54
 
 
55
    /**
 
56
     * Given a parent classloader and the prefix to apply to the lookup path
 
57
     * Creates a ResourceClassLoader able to load classes from resources prefixed with "prefix"
 
58
     *
 
59
     * @param prefix
 
60
     * @param parent
 
61
     * @throws IOException
 
62
     */
 
63
    public ResourceClassLoader(String prefix, ClassLoader parent) {
 
64
        super(parent);
 
65
        this.prefix = prefix;
 
66
        String temporaryImplementationVersion = null;
 
67
        InputStream in = null;
 
68
        // looking up the version of our jar, from the Manifest in the private package (prefix)
 
69
        try {
 
70
            URL manifestResource = getParent().getResource(prefix + "/META-INF/MANIFEST.MF");
 
71
            in = manifestResource.openStream();
 
72
            Manifest man = new Manifest(in);
 
73
            Attributes attributes = man.getMainAttributes();
 
74
            temporaryImplementationVersion = attributes.getValue(Name.IMPLEMENTATION_VERSION);
 
75
        } catch (Exception e) {
 
76
            LOG.debug("Could not read the Manifest", e);
 
77
        } finally {
 
78
            try {
 
79
                if (in != null) {
 
80
                    in.close();
 
81
                }
 
82
              } catch (Exception e) {
 
83
                  /* Ignore */
 
84
              }
 
85
        }
 
86
        this.implementationVersion = temporaryImplementationVersion;
 
87
    }
 
88
 
 
89
    @Override
 
90
    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
 
91
        // changing the order of delegation to prefer the resourceClassLoader over its parents
 
92
        Class c = findLoadedClass(name);
 
93
        if (c == null) {
 
94
            try {
 
95
                c = findClass(name);
 
96
            } catch (ClassNotFoundException e) {
 
97
                c = super.loadClass(name, resolve);
 
98
            }
 
99
        }
 
100
        if (resolve) {
 
101
            resolveClass(c);
 
102
        }
 
103
        return c;
 
104
    }
 
105
 
 
106
    @Override
 
107
    public URL getResource(String name) {
 
108
        URL url;
 
109
        url = findResource(name);
 
110
        if (url == null) {
 
111
            return super.getResource(name);
 
112
        }
 
113
        return url;
 
114
    }
 
115
 
 
116
    @Override
 
117
    protected URL findResource(String name) {
 
118
        URL resource = getParent().getResource(prefix + "/" + name);
 
119
        return resource;
 
120
    }
 
121
 
 
122
    @Override
 
123
    /**
 
124
     * very similar to what OracleJDK classloader does,
 
125
     * except the first resources (more important) are the ones found with our ResourceClassLoader
 
126
     */
 
127
    public Enumeration<URL> getResources(String resourceName) throws IOException {
 
128
        Enumeration[] tmp = new Enumeration[2];
 
129
        tmp[0] = findResources(resourceName);
 
130
        tmp[1] = getParent().getResources(resourceName);
 
131
        return new MergedEnumeration<URL>(tmp[0], tmp[1]);
 
132
    };
 
133
 
 
134
 
 
135
    @Override
 
136
    protected Enumeration<URL> findResources(String name) throws IOException {
 
137
        Enumeration<URL> resources = getParent().getResources(prefix + "/" + name);
 
138
        // DEV-8100 add support for Jboss AS, translating vfs URLs
 
139
        List<URL> urls = new ArrayList<URL>();
 
140
        while (resources.hasMoreElements()) {
 
141
            URL elementToAdd;
 
142
            URL nextElement = resources.nextElement();
 
143
            if (nextElement.toExternalForm().startsWith("vfs")) {
 
144
                elementToAdd = translateFromVFSToPhysicalURL(nextElement);
 
145
            } else {
 
146
                elementToAdd = nextElement;
 
147
            }
 
148
            urls.add(elementToAdd);
 
149
        }
 
150
        return Collections.enumeration(urls);
 
151
    }
 
152
 
 
153
    @Override
 
154
    protected Class<?> findClass(String className) throws ClassNotFoundException {
 
155
 
 
156
 
 
157
        String classRealName = prefix + "/" + className.replace('.', '/') + ".class";
 
158
        URL classResource = getParent().getResource(classRealName);
 
159
 
 
160
        if (classResource != null) {
 
161
            // classresource ok, let's define its package too
 
162
            int index = className.lastIndexOf('.');
 
163
            if (index != -1) {
 
164
                String pkgname = className.substring(0, index);
 
165
                if (getPackage(pkgname) == null) {
 
166
                    definePackage(pkgname, null, null, null, null, implementationVersion, null, null);
 
167
                }
 
168
            }
 
169
            InputStream in = null;
 
170
            try {
 
171
                byte[] array = new byte[BUFFER_SIZE];
 
172
                in = classResource.openStream();
 
173
                ByteArrayOutputStream out = new ByteArrayOutputStream(array.length);
 
174
                int length = in.read(array);
 
175
                while (length > 0) {
 
176
                    out.write(array, 0, length);
 
177
                    length = in.read(array);
 
178
                }
 
179
                Class<?> defineClass = defineClass(className, out.toByteArray(), 0, out.size());
 
180
                return defineClass;
 
181
            } catch (IOException e) {
 
182
                LOG.warn("Impossible to open " + classRealName + " for loading", e);
 
183
            } finally {
 
184
                try {
 
185
                    if (in != null) {
 
186
                        in.close();
 
187
                    }
 
188
                  } catch (Exception e) {
 
189
                      /* Ignore */
 
190
                  }
 
191
            }
 
192
        }
 
193
        throw new ClassNotFoundException(className);
 
194
 
 
195
    }
 
196
 
 
197
    /**
 
198
     *  DEV-8100 add support for Jboss AS
 
199
     *  jersey does not understand Jboss VFS URLs , so we use Jboss VFS classes to translate those URLs to file: URLs
 
200
     */
 
201
    private URL translateFromVFSToPhysicalURL(URL vfsUrl) throws IOException {
 
202
        URL physicalUrl = null;
 
203
        URLConnection vfsURLConnection = vfsUrl.openConnection();
 
204
        Object vfsVirtualFile = vfsURLConnection.getContent();
 
205
        try {
 
206
            Class vfsUtilsClass = Class.forName("org.jboss.vfs.VFSUtils");
 
207
            Class virtualFileClass = Class.forName("org.jboss.vfs.VirtualFile");
 
208
            Method getPathName = virtualFileClass.getDeclaredMethod("getPathName", new Class[0]);
 
209
            Method getPhysicalURL = vfsUtilsClass.getDeclaredMethod("getPhysicalURL", virtualFileClass);
 
210
            Method recursiveCopy = vfsUtilsClass.getDeclaredMethod("recursiveCopy", virtualFileClass, File.class);
 
211
            String pathName = (String) getPathName.invoke(vfsVirtualFile, (Object[]) null);
 
212
            physicalUrl = (URL) getPhysicalURL.invoke(null, vfsVirtualFile);
 
213
            File physicalURLAsFile = new File(physicalUrl.getFile());
 
214
            // https://issues.jboss.org/browse/JBAS-8786
 
215
            if (physicalURLAsFile.isDirectory() && physicalURLAsFile.list().length == 0) {
 
216
                // jboss does not unpack the libs in WEB-INF/lib, we have to unpack them (partially) ourselves
 
217
                unpackVfsResourceToPhysicalURLLocation(physicalUrl, vfsVirtualFile, recursiveCopy);
 
218
            }
 
219
        } catch (ClassNotFoundException e) {
 
220
            // jboss-5 and below doesn't have this class, so just return the vfsUrl,
 
221
            // in case the library loading this resource knows how to handle it
 
222
          physicalUrl = vfsUrl;
 
223
        } catch (NoSuchMethodException e) {
 
224
            throw new RuntimeException(e);
 
225
        } catch (InvocationTargetException e) {
 
226
            throw new RuntimeException(e.getCause());
 
227
        } catch (IllegalAccessException e) {
 
228
            throw new RuntimeException(e);
 
229
        } catch (IllegalArgumentException e) {
 
230
            throw new RuntimeException(e);
 
231
        }
 
232
        return physicalUrl;
 
233
    }
 
234
 
 
235
    private void unpackVfsResourceToPhysicalURLLocation(URL physicalUrl, Object vfsVirtualFile, Method recursiveCopy)
 
236
            throws IllegalAccessException, InvocationTargetException {
 
237
        String physicalPath = physicalUrl.getFile() + "/../";
 
238
        recursiveCopy.invoke(null, vfsVirtualFile, new File(physicalPath));
 
239
    }
 
240
 
 
241
}