source: patches/NetKernel/trunk/src/kernel/com/ten60/netkernel/util/DynamicURLClassLoader.java @ 2029

Last change on this file since 2029 was 2029, checked in by mwindhouwer, 12 years ago

Initial import of all the *cats, i.e., ISOcat, RELcat and SCHEMAcat.

File size: 10.1 KB
Line 
1/******************************************************************************
2 * (c) Copyright 2002,2003, 1060 Research Ltd
3 *
4 * This Software is licensed to You, the licensee, for use under the terms of
5 * the 1060 Public License v1.0. Please read and agree to the 1060 Public
6 * License v1.0 [www.1060research.com/license] before using or redistributing
7 * this software.
8 *
9 * In summary the 1060 Public license has the following conditions.
10 * A. You may use the Software free of charge provided you agree to the terms
11 * laid out in the 1060 Public License v1.0
12 * B. You are only permitted to use the Software with components or applications
13 * that provide you with OSI Certified Open Source Code [www.opensource.org], or
14 * for which licensing has been approved by 1060 Research Limited.
15 * You may write your own software for execution by this Software provided any
16 * distribution of your software with this Software complies with terms set out
17 * in section 2 of the 1060 Public License v1.0
18 * C. You may redistribute the Software provided you comply with the terms of
19 * the 1060 Public License v1.0 and that no warranty is implied or given.
20 * D. If you find you are unable to comply with this license you may seek to
21 * obtain an alternative license from 1060 Research Limited by contacting
22 * license@1060research.com or by visiting www.1060research.com
23 *
24 * NO WARRANTY:  THIS SOFTWARE IS NOT COVERED BY ANY WARRANTY. SEE 1060 PUBLIC
25 * LICENSE V1.0 FOR DETAILS
26 *
27 * THIS COPYRIGHT NOTICE IS *NOT* THE 1060 PUBLIC LICENSE v1.0. PLEASE READ
28 * THE DISTRIBUTED 1060_Public_License.txt OR www.1060research.com/license
29 *
30 * File:          $RCSfile: DynamicURLClassLoader.java,v $
31 * Version:       $Name:  $ $Revision: 1.16 $
32 * Last Modified: $Date: 2008/03/26 10:28:57 $
33 *
34 * CHANGELOG
35 * [18/8/2009 maw] Applied patch from tab to createClassFromInputStream() which
36 *                 should fix a concurrent class loading bug.
37 *****************************************************************************/
38package com.ten60.netkernel.util;
39import java.util.*;
40import java.net.*;
41import java.util.jar.*;
42import java.io.*;
43
44/**
45 * DynamicURLClassLoader locates class and resources from a list of URLs before resorting to
46 * parent classloader.
47 * @author  tab
48 */
49public class DynamicURLClassLoader extends ClassLoader
50{
51        private List mURLs;
52       
53        private static byte[] mBuffer1 = new byte[40000]; // hopefully big enough for the largest class
54        private static Thread mBuffer1User = null;
55        private static int mBuffer1Count=0;
56       
57        private static byte[] mBuffer2 = new byte[40000]; // hopefully big enough for the largest class
58        private static Thread mBuffer2User = null;
59        private static int mBuffer2Count=0;
60       
61        private static Integer mBufferSync = new Integer(0);
62       
63        private static Enumeration sEmptyEnum;
64        static
65        {       sEmptyEnum = new Enumeration()
66                {
67                        public boolean hasMoreElements()
68                        {       return false;
69                        }
70                       
71                        public Object nextElement()
72                        {       return null;
73                        }
74                };
75        }
76       
77        private static class ClassPathElement
78        {       public JarFile mJarFile;
79                public File mDirectory;
80                public String mBaseURL;
81                public ClassPathElement(JarFile aJar, String aBaseURL)
82                {       mJarFile = aJar;
83                        mBaseURL = aBaseURL;
84                }
85                public ClassPathElement(File aDir, String aBaseURL)
86                {       mDirectory = aDir;
87                        mBaseURL = aBaseURL;
88                }
89        }
90       
91        public DynamicURLClassLoader(List aURLs)
92        {       mURLs =  parseURLs(aURLs);
93        }
94       
95        public void cleanup()
96        {       mURLs=null;
97        }
98       
99        private List parseURLs(List aURLs)
100        {       List result = new ArrayList(aURLs.size());
101                for (Iterator i=aURLs.iterator(); i.hasNext(); )
102                {       String url = (String)i.next();
103                        String message=null;
104                        try
105                        {       if (url.startsWith("file:") && url.endsWith("/"))
106                                {       URI uri = URI.create(url);
107                                        File f = new File(uri);
108                                        if (f.exists())
109                                        {       ClassPathElement cpe = new ClassPathElement(f,url);
110                                                result.add(cpe);
111                                        }
112                                        else
113                                        {       message = "file doesn't exist";
114                                        }
115                                }
116                                else if (url.startsWith("jar:file:") && url.endsWith("!/"))
117                                {       String fileURI = url.substring(4,url.length()-2);
118                                        File f = new File(URI.create(fileURI));
119                                        JarFile jf = new JarFile(f);
120                                        ClassPathElement cpe = new ClassPathElement(jf,url);
121                                        result.add(cpe);
122                                }
123                                else
124                                {       message = "this URL isn't valid";
125                                }
126                        } catch (Exception e)
127                        {       message = "Unhandled exception "+e.getClass().getName()+": "+e.getMessage();
128                        }
129                        if (message!=null)
130                        {       System.out.println("DynamicURLClassLoader failed to parse "+url+": "+message);
131                        }
132                       
133                }
134                return result;
135        }
136       
137        public Class loadClass(String aName, boolean aResolve) throws ClassNotFoundException
138        {       Class c= loadClass(aName);
139                if (aResolve)
140                {       resolveClass(c);
141                }
142                return c;
143        }       
144       
145        public Class loadClass(String aName) throws ClassNotFoundException
146        {       Class result = innerLocalLoadClass(aName);
147                if (result==null)
148                {       throw new ClassNotFoundException(aName);
149                }
150                return result;
151        }
152       
153        protected Class innerLocalLoadClass(String aName)
154        {       Class result = findLoadedClass(aName);
155                if (result==null)
156                {       result = innerLoadClass(aName);
157                }
158                return result;
159        }
160       
161        public URL getResource(String aName)
162        {       try
163                {       boolean found=false;
164                        for (Iterator i = mURLs.iterator(); i.hasNext(); )
165                        {       ClassPathElement cpe = (ClassPathElement)i.next();
166                                if (cpe.mJarFile!=null)
167                                {       JarEntry je = cpe.mJarFile.getJarEntry(aName);
168                                        found=(je!=null);
169                                }
170                                else
171                                {       File f = new File(cpe.mDirectory, aName);
172                                        found=f.exists();
173                                }
174                                if (found)
175                                {       StringBuffer sb = new StringBuffer(aName.length()+cpe.mBaseURL.length());
176                                        sb.append(cpe.mBaseURL);
177                                        Utils.appendUnreservedURIChar(sb, aName);
178                                        return new URL(sb.toString());
179                                }
180                                       
181                        }
182                } catch (MalformedURLException e)
183                {       // just return null
184                }
185                return null;
186        }
187       
188        /*Cannot override in JDK 1.4.2
189        public Enumeration getResources(String aName)
190        {       URL resource = getResource(aName);
191                if (resource!=null)
192                {       Vector v=new Vector(1);
193                        v.add(resource);
194                        return v.elements();
195                }
196                else
197                {       return sEmptyEnum;
198                }
199        }
200        */
201       
202    /** create or return a buffer that is big enough. This buffer is designed
203         * to be reused as much as possible to avoid memory thrashing but avoids
204         * a possible deadlock scenario when two threads are loading in parallel
205         * (very rare)
206     * @param aLength the minimum size of buffer acceptable
207     * @return the buffer
208     */
209    private static byte[] getBuffer(int aLength)
210        {       byte[] result;
211                synchronized(mBufferSync)
212                {       Thread current = Thread.currentThread();
213                        if (mBuffer1User!=null && mBuffer1User!=current)
214                        {       if (mBuffer2User!=null && mBuffer2User!=current)
215                                {       result = new byte[aLength];
216                                }
217                                else
218                                {       if (mBuffer2.length<aLength)
219                                        {   mBuffer2 = new byte[aLength];
220                                        }
221                                        result=mBuffer2;
222                                        mBuffer2User = current;
223                                        mBuffer2Count++;                                       
224                                }
225                        }
226                        else
227                        {       if (mBuffer1.length<aLength)
228                                {   mBuffer1 = new byte[aLength];
229                                }
230                                result=mBuffer1;
231                                mBuffer1User = current;
232                                mBuffer1Count++;
233                        }
234                }
235                return result;
236    }
237       
238        /** release buffer for reuse
239         */
240        private static void releaseBuffer(byte[] aBuffer)
241        {       synchronized(mBufferSync)
242                {       if (aBuffer==mBuffer1)
243                        {       if (--mBuffer1Count==0)
244                                {       mBuffer1User=null;
245                                }
246                        }
247                        else if (aBuffer==mBuffer2)
248                        {       if (--mBuffer2Count==0)
249                                {       mBuffer2User=null;
250                                }
251                        }
252                }
253        }
254       
255        private Class innerLoadClass(String aName)
256        {       StringBuffer sb=new StringBuffer(aName.length()+6);
257                sb.append(aName.replace('.','/'));
258                sb.append(".class");
259                String resource = sb.toString();
260                Class result = null;
261                try
262                {       for (Iterator i = mURLs.iterator(); i.hasNext(); )
263                        {       ClassPathElement cpe = (ClassPathElement)i.next();
264                                if (cpe.mJarFile!=null)
265                                {       JarEntry je = cpe.mJarFile.getJarEntry(resource);
266                                        if (je!=null)
267                                        {       result = createClassFromInputStream(aName,cpe.mJarFile.getInputStream(je),(int)je.getSize());
268                                                break;
269                                        }
270                                }
271                                else
272                                {       File f = new File(cpe.mDirectory, resource);
273                                        if (f.exists())
274                                        {       result = createClassFromInputStream(aName,new FileInputStream(f), (int)f.length());
275                                                break;
276                                        }
277                                }
278                        }
279                } catch (IOException e)
280                {       /* just return null */
281                }
282                return result;
283        }
284/*
285        protected Class createClassFromInputStream(String aName,InputStream aStream, int aLength) throws IOException
286        {       Class result;
287                int j=0;
288                byte[] buffer = getBuffer(aLength);
289                try
290                {       while (j<aLength)
291                        {   int r = aStream.read(buffer,j, aLength-j);
292                                j+=r;
293                        }
294                        aStream.close();
295                        result = defineClass(aName,buffer,0,aLength);
296                       
297                        int i = aName.lastIndexOf('.');
298                        if (i != -1)
299                        {       String pkgname = aName.substring(0, i);
300                                Package pkg = getPackage(pkgname);
301                                if (pkg==null)
302                                {       definePackage(pkgname, null, null, null, null, null, null, null );
303                                }
304                        }
305
306                        return result;
307                }
308                finally
309                {       releaseBuffer(buffer);
310                }
311               
312        }
313*/
314   protected Class createClassFromInputStream(String aName,InputStream aStream, int aLength) throws IOException
315   {    Class result;
316       int j=0;
317       byte[] buffer = getBuffer(aLength);
318       try
319       {    while (j<aLength)
320           {   int r = aStream.read(buffer,j, aLength-j);
321               j+=r;
322           }
323           aStream.close();
324           result = defineClass(aName,buffer,0,aLength);
325                     int i = aName.lastIndexOf('.');
326           if (i != -1)
327           {    String pkgname = aName.substring(0, i);
328               Package pkg = getPackage(pkgname);
329               if (pkg==null)
330               {    definePackage(pkgname, null, null, null, null, null, null, null );
331               }
332           }
333
334           return result;
335       }
336       catch (LinkageError e)
337       {    if (e.getMessage().indexOf("duplicate")>=0)
338           {    //this can happen if two threads race to load the class
339               return this.findLoadedClass(aName);
340           }
341           else
342           {    throw e;
343           }
344       }
345       finally
346       {    releaseBuffer(buffer);
347       }
348   }
349
350       
351        /** append XML representation of classloader structure
352         */
353        public void appendXML(Writer aWriter) throws IOException
354        {       aWriter.write("<DynamicURLClassLoader>");
355                for (Iterator i=mURLs.iterator(); i.hasNext(); )
356                {       ClassPathElement cpe = (ClassPathElement)i.next();
357                        XMLUtils.write(aWriter, "url", XMLUtils.escape(cpe.mBaseURL));
358                }
359                aWriter.write("</DynamicURLClassLoader>");
360        }
361}
Note: See TracBrowser for help on using the repository browser.