1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.agent; 28 29 import java.io.ByteArrayInputStream; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.net.URL; 33 import java.net.URLConnection; 34 import java.net.URLStreamHandler; 35 import java.security.CodeSigner; 36 import java.security.CodeSource; 37 import java.security.PermissionCollection; 38 import java.security.Permissions; 39 import java.security.ProtectionDomain; 40 import java.util.HashMap; 41 import java.util.MissingResourceException; 42 43 class AgentClassLoader2 extends InstantiationClassLoader { 44 45 private CodeSource cs = null; 46 private final HashMap<CodeSource, ProtectionDomain> pdcache = new HashMap<CodeSource, ProtectionDomain>(11); 47 private static volatile AgentClassLoader2 instance = null; 48 49 private AgentClassLoader2(Agent.Task parent, ClassLoader cl) { 50 super(cl); 51 this.parent = parent; 52 53 SecurityManager security = System.getSecurityManager(); 54 if (security != null) { 55 security.checkCreateClassLoader(); 56 } 57 58 ProtectionDomain pd = this.getClass().getProtectionDomain(); 59 60 if (pd != null) { 61 cs = this.getClass().getProtectionDomain().getCodeSource(); 62 synchronized (pdcache) { 63 pdcache.put(cs, pd); 64 } 65 } 66 } 67 68 private AgentClassLoader2(Agent.Task parent) { 69 this(parent, parent.getClass().getClassLoader()); 70 } 71 72 private ProtectionDomain getProtectionDomain(CodeSource cs) { 73 ProtectionDomain pd = null; 74 synchronized (pdcache) { 75 pd = pdcache.get(cs); 76 if (pd == null) { 77 PermissionCollection perms = new Permissions(); 78 pd = new ProtectionDomain(cs, perms, this, null); 79 pdcache.put(cs, pd); 80 } 81 } 82 return pd; 83 } 84 85 86 /* 87 * Returns shared instance of classloader for tests where it is required. 88 */ 89 public static AgentClassLoader2 getInstance(Agent.Task parent) { 90 if (instance == null) { 91 synchronized (AgentClassLoader2.class) { 92 if (instance == null) { 93 instance = new AgentClassLoader2(parent); 94 } 95 } 96 } 97 instance.parent = parent; 98 return instance; 99 } 100 101 public Class<?> loadClassLocal(String name) throws ClassNotFoundException { 102 Class<?> target = null; 103 System.out.println("FORCE REMOTE " + name); 104 try { 105 target = findClass(name); 106 } catch (ClassNotFoundException e) { 107 // not found remote, search locally 108 // this is reverse of normal CL operation 109 target = super.loadClass(name); 110 //target = findSystemClass(name); 111 } catch (NoClassDefFoundError e2) { 112 target = super.loadClass(name); 113 //target = findSystemClass(name); 114 } 115 116 return target; 117 } 118 119 120 @Override 121 public Class<?> findClass(String className) throws ClassNotFoundException { 122 if (className != null) { 123 int i = className.lastIndexOf('.'); 124 if (i > 0) { 125 String pkgName = className.substring(0, i); 126 if (getPackage(pkgName) == null) { 127 definePackage(pkgName, null, null, null, null, null, null, null); 128 } 129 } 130 AgentRemoteClassData classData = parent.getClassData(className); 131 ProtectionDomain pd = null; 132 if (classData != null && classData.getCodeSource() != null) { 133 try { 134 pd = getProtectionDomain(new CodeSource(new URL("file:" + classData.getCodeSource()), (CodeSigner[]) null)); 135 } catch (IOException e) { 136 // ProtectionDomain will be replaced 137 } 138 } 139 if (pd == null) { 140 pd = getProtectionDomain(cs); 141 } 142 143 return defineClass(className, classData.getByteData(), 0, classData.getByteData().length, pd); 144 145 } 146 throw new ClassNotFoundException(); 147 } 148 149 @Override 150 protected URL findResource(String name) { 151 URL u = null; 152 //URL u = super.findResource(name); 153 // create URLConnection + AgentURLStreamHandler 154 // must fail with null URL if data stream throws MissingResourceException 155 // not available locally, request across connection 156 if (parent == null || name == null) { 157 // sanity check 158 return null; 159 } 160 161 try { 162 byte[] bytes = parent.getResourceData(name); 163 164 if (bytes == null) { 165 u = null; 166 } 167 else { 168 // if byes[] is zero length, we expect the code to still work 169 u = new URL("file", "", -1, name, new AgentURLStreamHandler(bytes)); 170 } 171 } 172 catch (MissingResourceException e) { 173 u = null; 174 } 175 catch (IOException e) { 176 u = null; 177 } 178 179 return u; 180 } 181 182 /* 183 @Override 184 public synchronized InputStream getResourceAsStream(String resourceName) { 185 // check local classpath first 186 // the resource should already be absolute, if we've got here 187 // through getClass().getResourceAsStream() 188 InputStream in = getClass().getResourceAsStream(resourceName); 189 if (in == null) { 190 try { 191 // if not found here, try remote load from Agent Manager 192 byte[] data = parent.getResourceData(resourceName); 193 in = new ByteArrayInputStream(data); 194 } 195 catch (Exception e) { 196 // ignore 197 } 198 } 199 return in; 200 }*/ 201 202 private Agent.Task parent; 203 204 @Override 205 public ClassLoader newClassLoaderInstance(ClassLoader parentCL) throws InstantiationStateException { 206 if (instance == null || !instance.equals(this)) { 207 synchronized (AgentClassLoader2.class) { 208 if (instance == null || !instance.equals(this)) { 209 return new AgentClassLoader2(parent, parentCL); 210 } 211 } 212 } 213 throw new InstantiationStateException("Only one instance of the "+getClass().getName()+" class could exist"); 214 } 215 216 private class AgentURLStreamHandler extends URLStreamHandler { 217 AgentURLStreamHandler(byte[] bytes) { 218 super(); 219 this.bytes = bytes; 220 } 221 222 @Override 223 protected URLConnection openConnection(URL url) throws IOException { 224 return new AgentURLConnection(url, bytes); 225 } 226 227 private byte[] bytes; 228 } 229 230 private class AgentURLConnection extends URLConnection { 231 AgentURLConnection(URL url) { 232 // do not use this constructor for now, bytes are already available 233 super(url); 234 } 235 236 AgentURLConnection(URL url, byte[] bytes) { 237 super(url); 238 this.bytes = bytes; 239 } 240 241 @Override 242 public void connect() throws IOException { 243 // could check Agent.Task parent for connection status 244 // generally, ignore this call per the spec 245 if (bytes != null) { 246 connected = true; 247 } 248 else { 249 connected = false; 250 } 251 } 252 253 @Override 254 public InputStream getInputStream() throws IOException { 255 if (parent == null) { 256 throw new IOException("No parent agent to open connection with!"); 257 } 258 259 if (bytes == null) { 260 // should not occur 261 throw new IOException("No bytes available!!"); 262 } 263 264 return new ByteArrayInputStream(bytes); 265 } 266 267 268 @Override 269 public String getContentEncoding() { 270 return null; 271 } 272 273 @Override 274 public int getContentLength() { 275 if (bytes != null) { 276 return bytes.length; 277 } else { 278 // zero length, bytes.length should == 0, so all other cases are -1 279 return -1; 280 } 281 } 282 283 /* public long getContentLengthLong() { 284 EXISTS in Java 7 285 }*/ 286 287 288 @Override 289 public String getContentType() { 290 if (bytes == null) { 291 // sanity check 292 return null; 293 } 294 295 String type = null; 296 297 try { 298 type = guessContentTypeFromStream(getInputStream()); 299 } 300 catch (Exception e) { 301 // it's really IOException which is possible 302 type = null; 303 } 304 305 if (type == null) { 306 // assumption: getURL() path is the same as what cached bytes has content for 307 URL u = getURL(); 308 if (u != null) { 309 type = guessContentTypeFromName(url.getPath()); 310 } 311 } 312 313 return type; 314 } 315 316 @Override 317 public long getDate() { 318 return 0; // unknown 319 } 320 321 @Override 322 public long getExpiration() { 323 return 0; // unknown 324 } 325 326 @Override 327 public long getLastModified() { 328 return 0l; // unknown 329 } 330 331 private byte[] bytes; 332 } 333 }