001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.util; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.ObjectInputStream; 022import java.io.ObjectStreamClass; 023import java.lang.reflect.Proxy; 024import java.util.Arrays; 025import java.util.Collection; 026import java.util.Map; 027 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031public class ClassLoadingAwareObjectInputStream extends ObjectInputStream { 032 033 private static final Logger LOG = LoggerFactory.getLogger(ClassLoadingAwareObjectInputStream.class); 034 private static final ClassLoader FALLBACK_CLASS_LOADER = 035 ClassLoadingAwareObjectInputStream.class.getClassLoader(); 036 037 public static final String[] serializablePackages; 038 039 private final ClassLoader inLoader; 040 041 static { 042 serializablePackages = System.getProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", 043 "java.lang,java.util,org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper").split(","); 044 } 045 046 public ClassLoadingAwareObjectInputStream(InputStream in) throws IOException { 047 super(in); 048 inLoader = in.getClass().getClassLoader(); 049 } 050 051 protected Class<?> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { 052 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 053 Class clazz = load(classDesc.getName(), cl, inLoader); 054 checkSecurity(clazz); 055 return clazz; 056 } 057 058 protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { 059 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 060 Class[] cinterfaces = new Class[interfaces.length]; 061 for (int i = 0; i < interfaces.length; i++) { 062 cinterfaces[i] = load(interfaces[i], cl); 063 } 064 065 Class clazz = null; 066 try { 067 clazz = Proxy.getProxyClass(cl, cinterfaces); 068 } catch (IllegalArgumentException e) { 069 try { 070 clazz = Proxy.getProxyClass(inLoader, cinterfaces); 071 } catch (IllegalArgumentException e1) { 072 // ignore 073 } 074 try { 075 clazz = Proxy.getProxyClass(FALLBACK_CLASS_LOADER, cinterfaces); 076 } catch (IllegalArgumentException e2) { 077 // ignore 078 } 079 } 080 081 if (clazz != null) { 082 checkSecurity(clazz); 083 return clazz; 084 } else { 085 throw new ClassNotFoundException(null); 086 } 087 } 088 089 public static boolean isAllAllowed() { 090 return serializablePackages.length == 1 && serializablePackages[0].equals("*"); 091 } 092 093 private void checkSecurity(Class clazz) throws ClassNotFoundException { 094 if (!clazz.isPrimitive()) { 095 if (clazz.getPackage() != null && !isAllAllowed()) { 096 boolean found = false; 097 for (String packageName : serializablePackages) { 098 if (clazz.getPackage().getName().equals(packageName) || clazz.getPackage().getName().startsWith(packageName + ".")) { 099 found = true; 100 break; 101 } 102 } 103 104 if (!found) { 105 throw new ClassNotFoundException("Forbidden " + clazz + "! This class is not allowed to be serialized. Add package with 'org.apache.activemq.SERIALIZABLE_PACKAGES' system property."); 106 } 107 } 108 } 109 } 110 111 private Class<?> load(String className, ClassLoader... cl) throws ClassNotFoundException { 112 // check for simple types first 113 final Class<?> clazz = loadSimpleType(className); 114 if (clazz != null) { 115 LOG.trace("Loaded class: {} as simple type -> ", className, clazz); 116 return clazz; 117 } 118 119 // try the different class loaders 120 for (ClassLoader loader : cl) { 121 LOG.trace("Attempting to load class: {} using classloader: {}", className, cl); 122 try { 123 Class<?> answer = Class.forName(className, false, loader); 124 if (LOG.isTraceEnabled()) { 125 LOG.trace("Loaded class: {} using classloader: {} -> ", new Object[]{className, cl, answer}); 126 } 127 return answer; 128 } catch (ClassNotFoundException e) { 129 LOG.trace("Class not found: {} using classloader: {}", className, cl); 130 // ignore 131 } 132 } 133 134 // and then the fallback class loader 135 return Class.forName(className, false, FALLBACK_CLASS_LOADER); 136 } 137 138 /** 139 * Load a simple type 140 * 141 * @param name the name of the class to load 142 * @return the class or <tt>null</tt> if it could not be loaded 143 */ 144 public static Class<?> loadSimpleType(String name) { 145 // code from ObjectHelper.loadSimpleType in Apache Camel 146 147 // special for byte[] or Object[] as its common to use 148 if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) { 149 return byte[].class; 150 } else if ("java.lang.Byte[]".equals(name) || "Byte[]".equals(name)) { 151 return Byte[].class; 152 } else if ("java.lang.Object[]".equals(name) || "Object[]".equals(name)) { 153 return Object[].class; 154 } else if ("java.lang.String[]".equals(name) || "String[]".equals(name)) { 155 return String[].class; 156 // and these is common as well 157 } else if ("java.lang.String".equals(name) || "String".equals(name)) { 158 return String.class; 159 } else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name)) { 160 return Boolean.class; 161 } else if ("boolean".equals(name)) { 162 return boolean.class; 163 } else if ("java.lang.Integer".equals(name) || "Integer".equals(name)) { 164 return Integer.class; 165 } else if ("int".equals(name)) { 166 return int.class; 167 } else if ("java.lang.Long".equals(name) || "Long".equals(name)) { 168 return Long.class; 169 } else if ("long".equals(name)) { 170 return long.class; 171 } else if ("java.lang.Short".equals(name) || "Short".equals(name)) { 172 return Short.class; 173 } else if ("short".equals(name)) { 174 return short.class; 175 } else if ("java.lang.Byte".equals(name) || "Byte".equals(name)) { 176 return Byte.class; 177 } else if ("byte".equals(name)) { 178 return byte.class; 179 } else if ("java.lang.Float".equals(name) || "Float".equals(name)) { 180 return Float.class; 181 } else if ("float".equals(name)) { 182 return float.class; 183 } else if ("java.lang.Double".equals(name) || "Double".equals(name)) { 184 return Double.class; 185 } else if ("double".equals(name)) { 186 return double.class; 187 } 188 189 return null; 190 } 191 192}