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 }