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 }