| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.qpid.client.util; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectStreamClass; |
| import java.lang.reflect.Proxy; |
| import java.util.HashMap; |
| |
| |
| /** |
| * <code>ClassLoadingAwareObjectInputStream</code> is an Extention of Object input stream to be used |
| * to de-serialize JMS Object Messages. |
| * |
| * <p>This was introduced to resolve the class loading issues which can happen when we use the client |
| * libraries in a complex class loading Environment.</p> |
| */ |
| public class ClassLoadingAwareObjectInputStream extends ObjectInputStream |
| { |
| /** <p>Class loader instance which loaded this class. |
| * It will be used to load classes when we failed to load classes from dynamic class loading</p> */ |
| private static final ClassLoader _ON_FAULT_CLASS_LOADER = |
| ClassLoadingAwareObjectInputStream.class.getClassLoader(); |
| |
| /** <p>Maps primitive type names to corresponding class objects.</p> */ |
| private static final HashMap<String, Class> _primitives = new HashMap<String, Class>(8, 1.0F); |
| private final TrustedClassFilter _securityFilter; |
| |
| /** |
| * Security Filter used to filter classes that the application deems to be insecure, this filter |
| * is not applied to the class instances for the primitive types. |
| */ |
| public interface TrustedClassFilter |
| { |
| boolean isTrusted(Class<?> clazz); |
| } |
| |
| public ClassLoadingAwareObjectInputStream(InputStream in, TrustedClassFilter filter) throws IOException |
| { |
| super(in); |
| _securityFilter = filter; |
| } |
| |
| @Override |
| protected Class resolveClass(ObjectStreamClass classDesc) |
| throws IOException, ClassNotFoundException |
| { |
| |
| // Here we use TTCL as our primary class loader to load the classes |
| ClassLoader cl = Thread.currentThread().getContextClassLoader(); |
| Class<?> clazz = load(classDesc.getName(), cl); |
| return checkSecurity(clazz); |
| } |
| |
| @Override |
| protected Class resolveProxyClass(String[] interfaces) |
| throws IOException, ClassNotFoundException |
| { |
| // Here we use TTCL as our primary class loader to load the classes |
| ClassLoader cl = Thread.currentThread().getContextClassLoader(); |
| |
| Class[] cinterfaces = new Class[interfaces.length]; |
| for (int i = 0; i < interfaces.length; i++) |
| { |
| cinterfaces[i] = load(interfaces[i], cl); |
| } |
| |
| |
| Class<?> clazz = null; |
| Throwable failureCause = null; |
| |
| try |
| { |
| clazz = Proxy.getProxyClass(cl, cinterfaces); |
| } |
| catch (IllegalArgumentException e) |
| { |
| failureCause = e; |
| |
| try |
| { |
| clazz = Proxy.getProxyClass(_ON_FAULT_CLASS_LOADER, cinterfaces); |
| } |
| catch (IllegalArgumentException e2) |
| { |
| } |
| } |
| |
| if (clazz != null) |
| { |
| return checkSecurity(clazz); |
| } |
| |
| throw new ClassNotFoundException("Failed find class.", failureCause); |
| } |
| |
| private Class<?> checkSecurity(Class<?> clazz) throws ClassNotFoundException |
| { |
| if (!clazz.isPrimitive() && _securityFilter != null) |
| { |
| if (!_securityFilter.isTrusted(clazz)) |
| { |
| throw new ClassNotFoundException("Forbidden " + clazz + "! " + |
| "This class is not trusted to be deserialized from ObjectMessage payloads."); |
| } |
| } |
| |
| return clazz; |
| } |
| |
| /** |
| * <p> |
| * Method we used to load class that are needed to de-serialize the objects. </p> |
| * <p> |
| * Here we first look up for the objects from the given class loader and if its not there |
| * we will be using the class loader of this class. |
| * </p> |
| * @param className Class name to lookup |
| * @param cl primary class loader which we 1st use to lookup |
| * @return Class instance we are looking for |
| * @throws ClassNotFoundException if both primary and secondary lockup's failed. |
| */ |
| private Class load(String className, ClassLoader cl) |
| throws ClassNotFoundException |
| { |
| try |
| { |
| return Class.forName(className, false, cl); |
| } |
| catch (ClassNotFoundException e) |
| { |
| final Class clazz = _primitives.get(className); |
| |
| if (clazz != null) |
| { |
| return clazz; |
| } |
| else |
| { |
| return Class.forName(className, false, _ON_FAULT_CLASS_LOADER); |
| } |
| } |
| } |
| |
| static |
| { |
| _primitives.put("boolean", boolean.class); |
| _primitives.put("byte", byte.class); |
| _primitives.put("char", char.class); |
| _primitives.put("short", short.class); |
| _primitives.put("int", int.class); |
| _primitives.put("long", long.class); |
| _primitives.put("float", float.class); |
| _primitives.put("double", double.class); |
| _primitives.put("void", void.class); |
| } |
| } |