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 }