View Javadoc
1   //******************************************************************************
2   //
3   // File:    BackendClassLoader.java
4   // Package: edu.rit.pj.cluster
5   // Unit:    Class edu.rit.pj.cluster.BackendClassLoader
6   //
7   // This Java source file is copyright (C) 2012 by Alan Kaminsky. All rights
8   // reserved. For further information, contact the author, Alan Kaminsky, at
9   // ark@cs.rit.edu.
10  //
11  // This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
12  // software; you can redistribute it and/or modify it under the terms of the GNU
13  // General Public License as published by the Free Software Foundation; either
14  // version 3 of the License, or (at your option) any later version.
15  //
16  // PJ is distributed in the hope that it will be useful, but WITHOUT ANY
17  // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18  // A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19  //
20  // Linking this library statically or dynamically with other modules is making a
21  // combined work based on this library. Thus, the terms and conditions of the GNU
22  // General Public License cover the whole combination.
23  //
24  // As a special exception, the copyright holders of this library give you
25  // permission to link this library with independent modules to produce an
26  // executable, regardless of the license terms of these independent modules, and
27  // to copy and distribute the resulting executable under terms of your choice,
28  // provided that you also meet, for each linked independent module, the terms
29  // and conditions of the license of that module. An independent module is a module
30  // which is not derived from or based on this library. If you modify this library,
31  // you may extend this exception to your version of the library, but you are not
32  // obligated to do so. If you do not wish to do so, delete this exception
33  // statement from your version.
34  //
35  // A copy of the GNU General Public License is provided in the file gpl.txt. You
36  // may also obtain a copy of the GNU General Public License on the World Wide
37  // Web at http://www.gnu.org/licenses/gpl.html.
38  //
39  //******************************************************************************
40  package edu.rit.pj.cluster;
41  
42  import java.io.BufferedOutputStream;
43  import java.io.File;
44  import java.io.FileOutputStream;
45  import java.io.IOException;
46  import java.io.OutputStream;
47  import java.net.URL;
48  import java.util.Collections;
49  import java.util.HashMap;
50  import java.util.Map;
51  
52  /**
53   * Class BackendClassLoader provides a class loader for a job backend process in
54   * the PJ cluster middleware. If a backend class loader is requested to load a
55   * class and the parent class loaders cannot do so, the backend class loader
56   * sends a request for the class file to the job frontend process, waits for the
57   * job frontend process to send the class file, and loads the class.
58   *
59   * @author Alan Kaminsky
60   * @version 15-Jun-2012
61   */
62  public class BackendClassLoader
63          extends ClassLoader {
64  
65  // Hidden data members.
66      private JobBackendRef myJobBackend;
67      private JobFrontendRef myJobFrontend;
68      private ResourceCache myCache;
69  
70      // Map from resource name to resource URL for non-class-file resources.
71      private Map<String, URL> myResourceURLMap
72              = Collections.synchronizedMap(new HashMap<String, URL>());
73  
74  // Exported constructors.
75      /**
76       * Construct a new backend class loader. The parent class loader is the one
77       * returned by <code>ClassLoader.getSystemClassLoader()</code>. Class files will
78       * be requested from <code>theJobFrontend</code>. Class files will be stored in
79       * <code>theCache</code>.
80       *
81       * @param theJobBackend Reference to job backend.
82       * @param theJobFrontend Reference to job frontend.
83       * @param theCache Resource cache.
84       */
85      public BackendClassLoader(JobBackendRef theJobBackend,
86              JobFrontendRef theJobFrontend,
87              ResourceCache theCache) {
88          super();
89          myJobBackend = theJobBackend;
90          myJobFrontend = theJobFrontend;
91          myCache = theCache;
92      }
93  
94      /**
95       * Construct a new backend class loader. The parent class loader is
96       * <code>parent</code>. Class files will be requested from
97       * <code>theJobFrontend</code>. Class files will be stored in <code>theCache</code>.
98       *
99       * @param parent Parent class loader.
100      * @param theJobBackend Reference to job backend.
101      * @param theJobFrontend Reference to job frontend.
102      * @param theCache Resource cache.
103      */
104     public BackendClassLoader(ClassLoader parent,
105             JobBackendRef theJobBackend,
106             JobFrontendRef theJobFrontend,
107             ResourceCache theCache) {
108         super(parent);
109         myJobBackend = theJobBackend;
110         myJobFrontend = theJobFrontend;
111         myCache = theCache;
112     }
113 
114 // Hidden operations.
115     /**
116      * {@inheritDoc}
117      *
118      * Find the class with the given name.
119      * @exception ClassNotFoundException Thrown if the class could not be found.
120      */
121     protected Class<?> findClass(String className)
122             throws ClassNotFoundException {
123         try {
124             // Convert class name to resource name.
125             String resourceName = className.replace('.', '/') + ".class";
126 
127             // If the resource is not in the cache, ask the Job Frontend for it.
128             if (!myCache.contains(resourceName)) {
129                 myJobFrontend.requestResource(myJobBackend, resourceName);
130             }
131 
132             // Wait until the resource shows up in the cache.
133             byte[] content = myCache.get(resourceName);
134             if (content == null) {
135                 throw new ClassNotFoundException("Class " + className + " not found");
136             }
137 
138             // Load the class.
139             return defineClass(className, content, 0, content.length);
140         } catch (IOException exc) {
141             throw new ClassNotFoundException("Class " + className + " not found due to I/O error",
142                     exc);
143         } catch (InterruptedException exc) {
144             throw new ClassNotFoundException("Class " + className + " not found because thread interrupted",
145                     exc);
146         }
147     }
148 
149     /**
150      * {@inheritDoc}
151      *
152      * Find the resource with the given name.
153      */
154     protected URL findResource(String name) {
155         try {
156             URL url = myResourceURLMap.get(name);
157 
158             if (url == null) {
159                 // If the resource is not in the cache, ask the Job Frontend for
160                 // it.
161                 if (!myCache.contains(name)) {
162                     myJobFrontend.requestResource(myJobBackend, name);
163                 }
164 
165                 // Wait until the resource shows up in the cache.
166                 byte[] content = myCache.get(name);
167                 if (content == null) {
168                     return null;
169                 }
170 
171                 // Store the resource contents in a temporary file, which will
172                 // be deleted when the JVM exits.
173                 String fname = new File(name).getName();
174                 int i = fname.lastIndexOf('.');
175                 String fprefix = i == -1 ? fname : fname.substring(0, i);
176                 String fsuffix = i == -1 ? null : fname.substring(i);
177                 File file = File.createTempFile(fprefix + "_tmp", fsuffix);
178                 OutputStream out
179                         = new BufferedOutputStream(new FileOutputStream(file));
180                 out.write(content);
181                 out.close();
182                 file.deleteOnExit();
183 
184                 // Map resource name to temporary file URL.
185                 url = file.toURI().toURL();
186                 myResourceURLMap.put(name, url);
187             }
188 
189             return url;
190         } catch (IOException exc) {
191             return null;
192         } catch (InterruptedException exc) {
193             return null;
194         }
195     }
196 
197 }