1 /*
   2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.awt.datatransfer;
  27 
  28 import java.awt.datatransfer.DataFlavor;
  29 import java.awt.datatransfer.Transferable;
  30 import java.awt.datatransfer.UnsupportedFlavorException;
  31 import java.io.ByteArrayInputStream;
  32 import java.io.ByteArrayOutputStream;
  33 import java.io.InputStream;
  34 import java.io.IOException;
  35 import java.io.ObjectInputStream;
  36 import java.io.ObjectOutputStream;
  37 import java.io.ObjectStreamClass;
  38 import java.io.OutputStream;
  39 import java.lang.reflect.Modifier;
  40 import java.lang.reflect.Proxy;
  41 import java.security.AccessController;
  42 import java.security.PrivilegedAction;
  43 import java.util.HashMap;
  44 import java.util.HashSet;
  45 import java.util.Map;
  46 import java.util.Set;
  47 
  48 
  49 /**
  50  * Proxies for another Transferable so that Serializable objects are never
  51  * returned directly by DnD or the Clipboard. Instead, a new instance of the
  52  * object is returned.
  53  *
  54  * @author Lawrence P.G. Cable
  55  * @author David Mendenhall
  56  *
  57  * @since 1.4
  58  */
  59 public class TransferableProxy implements Transferable {
  60     public TransferableProxy(Transferable t, boolean local) {
  61         transferable = t;
  62         isLocal = local;
  63     }
  64     public DataFlavor[] getTransferDataFlavors() {
  65         return transferable.getTransferDataFlavors();
  66     }
  67     public boolean isDataFlavorSupported(DataFlavor flavor) {
  68         return transferable.isDataFlavorSupported(flavor);
  69     }
  70     public Object getTransferData(DataFlavor df)
  71         throws UnsupportedFlavorException, IOException
  72     {
  73         Object data = transferable.getTransferData(df);
  74 
  75         // If the data is a Serializable object, then create a new instance
  76         // before returning it. This insulates applications sharing DnD and
  77         // Clipboard data from each other.
  78         if (data != null && isLocal && df.isFlavorSerializedObjectType()) {
  79             ByteArrayOutputStream baos = new ByteArrayOutputStream();
  80 
  81             ClassLoaderObjectOutputStream oos =
  82                 new ClassLoaderObjectOutputStream(baos);
  83             oos.writeObject(data);
  84 
  85             ByteArrayInputStream bais =
  86                 new ByteArrayInputStream(baos.toByteArray());
  87 
  88             try {
  89                 ClassLoaderObjectInputStream ois =
  90                     new ClassLoaderObjectInputStream(bais,
  91                                                      oos.getClassLoaderMap());
  92                 data = ois.readObject();
  93             } catch (ClassNotFoundException cnfe) {
  94                 throw (IOException)new IOException().initCause(cnfe);
  95             }
  96         }
  97 
  98         return data;
  99     }
 100 
 101     protected final Transferable transferable;
 102     protected final boolean isLocal;
 103 }
 104 
 105 final class ClassLoaderObjectOutputStream extends ObjectOutputStream {
 106     private final Map<Set<String>, ClassLoader> map =
 107         new HashMap<Set<String>, ClassLoader>();
 108 
 109     ClassLoaderObjectOutputStream(OutputStream os) throws IOException {
 110         super(os);
 111     }
 112 
 113     protected void annotateClass(final Class<?> cl) throws IOException {
 114         ClassLoader classLoader = AccessController.doPrivileged(
 115             new PrivilegedAction<ClassLoader>() {
 116                 public ClassLoader run() {
 117                     return cl.getClassLoader();
 118                 }
 119             });
 120 
 121         Set<String> s = new HashSet<String>(1);
 122         s.add(cl.getName());
 123 
 124         map.put(s, classLoader);
 125     }
 126     protected void annotateProxyClass(final Class<?> cl) throws IOException {
 127         ClassLoader classLoader = AccessController.doPrivileged(
 128             new PrivilegedAction<ClassLoader>() {
 129                 public ClassLoader run() {
 130                     return cl.getClassLoader();
 131                 }
 132             });
 133 
 134         Class<?>[] interfaces = cl.getInterfaces();
 135         Set<String> s = new HashSet<String>(interfaces.length);
 136         for (int i = 0; i < interfaces.length; i++) {
 137             s.add(interfaces[i].getName());
 138         }
 139 
 140         map.put(s, classLoader);
 141     }
 142 
 143     Map<Set<String>, ClassLoader> getClassLoaderMap() {
 144         return new HashMap<>(map);
 145     }
 146 }
 147 
 148 final class ClassLoaderObjectInputStream extends ObjectInputStream {
 149     private final Map<Set<String>, ClassLoader> map;
 150 
 151     ClassLoaderObjectInputStream(InputStream is,
 152                                  Map<Set<String>, ClassLoader> map)
 153       throws IOException {
 154         super(is);
 155         if (map == null) {
 156             throw new NullPointerException("Null map");
 157         }
 158         this.map = map;
 159     }
 160 
 161     protected Class<?> resolveClass(ObjectStreamClass classDesc)
 162       throws IOException, ClassNotFoundException {
 163         String className = classDesc.getName();
 164 
 165         Set<String> s = new HashSet<String>(1);
 166         s.add(className);
 167 
 168         ClassLoader classLoader = map.get(s);
 169         if (classLoader != null) {
 170             return Class.forName(className, false, classLoader);
 171         } else {
 172             return super.resolveClass(classDesc);
 173         }
 174     }
 175 
 176     protected Class<?> resolveProxyClass(String[] interfaces)
 177       throws IOException, ClassNotFoundException {
 178 
 179         Set<String> s = new HashSet<String>(interfaces.length);
 180         for (int i = 0; i < interfaces.length; i++) {
 181             s.add(interfaces[i]);
 182         }
 183 
 184         ClassLoader classLoader = map.get(s);
 185         if (classLoader == null) {
 186             return super.resolveProxyClass(interfaces);
 187         }
 188 
 189         // The code below is mostly copied from the superclass.
 190         ClassLoader nonPublicLoader = null;
 191         boolean hasNonPublicInterface = false;
 192 
 193         // define proxy in class loader of non-public interface(s), if any
 194         Class<?>[] classObjs = new Class<?>[interfaces.length];
 195         for (int i = 0; i < interfaces.length; i++) {
 196             Class<?> cl = Class.forName(interfaces[i], false, classLoader);
 197             if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
 198                 if (hasNonPublicInterface) {
 199                     if (nonPublicLoader != cl.getClassLoader()) {
 200                         throw new IllegalAccessError(
 201                             "conflicting non-public interface class loaders");
 202                     }
 203                 } else {
 204                     nonPublicLoader = cl.getClassLoader();
 205                     hasNonPublicInterface = true;
 206                 }
 207             }
 208             classObjs[i] = cl;
 209         }
 210         try {
 211             return Proxy.getProxyClass(hasNonPublicInterface ?
 212                                        nonPublicLoader : classLoader,
 213                                        classObjs);
 214         } catch (IllegalArgumentException e) {
 215             throw new ClassNotFoundException(null, e);
 216         }
 217     }
 218 }