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 {
TransferableProxy(Transferable t, boolean local)60     public TransferableProxy(Transferable t, boolean local) {
61         transferable = t;
62         isLocal = local;
63     }
getTransferDataFlavors()64     public DataFlavor[] getTransferDataFlavors() {
65         return transferable.getTransferDataFlavors();
66     }
isDataFlavorSupported(DataFlavor flavor)67     public boolean isDataFlavorSupported(DataFlavor flavor) {
68         return transferable.isDataFlavorSupported(flavor);
69     }
getTransferData(DataFlavor df)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 
ClassLoaderObjectOutputStream(OutputStream os)109     ClassLoaderObjectOutputStream(OutputStream os) throws IOException {
110         super(os);
111     }
112 
annotateClass(final Class<?> cl)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     }
annotateProxyClass(final Class<?> cl)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 
getClassLoaderMap()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 
ClassLoaderObjectInputStream(InputStream is, Map<Set<String>, ClassLoader> map)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 
resolveClass(ObjectStreamClass classDesc)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 
resolveProxyClass(String[] interfaces)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             @SuppressWarnings("deprecation")
212             Class<?> proxyClass = Proxy.getProxyClass(hasNonPublicInterface ?
213                                                           nonPublicLoader : classLoader,
214                                                       classObjs);
215             return proxyClass;
216         } catch (IllegalArgumentException e) {
217             throw new ClassNotFoundException(null, e);
218         }
219     }
220 }
221