/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.jmx.connector.invoker.serializablepolicy; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.Serializable; import java.rmi.MarshalledObject; import java.util.ArrayList; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.modelmbean.ModelMBeanAttributeInfo; import javax.management.modelmbean.ModelMBeanConstructorInfo; import javax.management.modelmbean.ModelMBeanInfo; import javax.management.modelmbean.ModelMBeanInfoSupport; import javax.management.modelmbean.ModelMBeanNotificationInfo; import javax.management.modelmbean.ModelMBeanOperationInfo; import org.jboss.invocation.MarshalledInvocation; import org.jboss.jmx.connector.invoker.SerializablePolicy; import org.jboss.logging.Logger; /** * A policy that strips non serializable information from ModelMBeanInfo and MBeanInfo. * Only the getMBeanInfo and getAttributes methods are stripped, if the getAttribute method * is called the default NotSerializableException or ClassNotFoundException will be thrown * depending on the attribute types. * * @author Magesh Kumar B * @version $Revision: $ */ public class StripNonSerializableInfoPolicy implements SerializablePolicy { private static Logger log = Logger.getLogger(StripModelMBeanInfoPolicy.class); public Object filter(MarshalledInvocation input, Object result) throws Throwable { if ("getMBeanInfo".equals(input.getMethod().getName()) && (result instanceof ModelMBeanInfo)) { result = translateTo(result); } else if (result instanceof AttributeList) { AttributeList list = (AttributeList)result; Attribute attr = null; String className = null; int listCount = list.size(); // Remove all non-serializable objects for (int i = 0; i < listCount; i++) { attr = (Attribute)list.get(i); className = attr.getValue()==null?null:attr.getValue().getClass().getName(); if (log.isDebugEnabled()) { log.debug("Attribute Name:"+attr.getName() + ", Class:"+className); } if (attr.getName().toUpperCase().indexOf("INSTANCE") >= 0 || (className != null && className.indexOf("org.jboss.resource.adapter.jms.JmsManagedConnectionFactory") >= 0 )) { //TODO maybe this is not required at all // It is an instance reference, no point in transfering the reference when it may not be serialized // or the class may not be found e.g., org.jboss.resource.adapter.jms.JmsManagedConnectionFactory // and attribute name 'McfInstance' list.set(i, new Attribute(attr.getName(),attr.getValue().toString())); } else if (!isSerializableObject(attr.getValue())) { // Mark all non serializable objects as 'Not Serializable' list.set(i, new Attribute(attr.getName(),"Not Serializable")); } } } return result; } private Object translateTo(Object result) { ArrayList list = new ArrayList(); MBeanInfo info = (MBeanInfo)result; MBeanAttributeInfo[] attrs = info.getAttributes(); String className = null; MBeanAttributeInfo copy = null; ModelMBeanAttributeInfo copy2 = null; for (int i = 0; i < attrs.length; i++) { className = attrs[i].getType(); try { //TODO: Should the Stats be serializable ??? if ((isPrimitive(className)) || (isSerializable(getClass().getClassLoader().loadClass(className))) || (isJSR77Stats(getClass().getClassLoader().loadClass(className)))) { if (attrs[i] instanceof ModelMBeanAttributeInfo) { copy2 = new ModelMBeanAttributeInfo( attrs[i].getName(), attrs[i].getType(), attrs[i].getDescription(), attrs[i].isReadable(), attrs[i].isWritable(), attrs[i].isIs()); list.add(copy2); } else { copy = new MBeanAttributeInfo( attrs[i].getName(), attrs[i].getType(), attrs[i].getDescription(), attrs[i].isReadable(), attrs[i].isWritable(), attrs[i].isIs()); list.add(copy); } } } catch(ClassNotFoundException cnfe) { if (log.isDebugEnabled()) { log.debug("we consider this a non-serializable Class", cnfe); } } } if (list.size() < attrs.length) { if (info instanceof ModelMBeanInfoSupport) { ModelMBeanInfoSupport modelInfo = (ModelMBeanInfoSupport)info; result = new ModelMBeanInfoSupport(modelInfo.getClassName(), modelInfo.getDescription(), (ModelMBeanAttributeInfo[])list.toArray(new ModelMBeanAttributeInfo[list.size()]), (ModelMBeanConstructorInfo[])modelInfo.getConstructors(), (ModelMBeanOperationInfo[])modelInfo.getOperations(), (ModelMBeanNotificationInfo[])modelInfo.getNotifications()); } else { result = new MBeanInfo(info.getClassName(), info.getDescription(), (MBeanAttributeInfo[])list.toArray(new MBeanAttributeInfo[list.size()]), (MBeanConstructorInfo[])info.getConstructors(), (MBeanOperationInfo[])info.getOperations(), (MBeanNotificationInfo[])info.getNotifications()); } } return result; } private boolean isJSR77Stats(Class clazz) { boolean result = false; try { Class stats = getClass().getClassLoader().loadClass("javax.management.j2ee.statistics.Stats"); result = stats.isAssignableFrom(clazz); } catch (ClassNotFoundException e) { //ignore exception if (log.isDebugEnabled()) { log.debug("Ignored class not found exception",e); } } return result; } private boolean isSerializable(Class clazz) { return (Serializable.class.isAssignableFrom(clazz)); } private boolean isPrimitive(String className) { String[] primitives = new String[] {"int", "double", "long", "boolean", "byte", "short"}; boolean result = false; for (int i=0; i < primitives.length; i++) { if (className.indexOf(primitives[i]) != -1) { result = true; break; } } return result; } /** * Check if a given Object and its references are Serializable * * @param obj * @return boolean */ private boolean isSerializableObject(Object obj) { boolean result = false; if (obj == null) { result = true; } else if (obj instanceof Serializable) { ByteArrayOutputStream baos = null; ObjectOutputStream tstOOS = null; try { baos = new ByteArrayOutputStream(); tstOOS = new ObjectOutputStream(baos); // Validate that the object's references are serializable baos.reset(); tstOOS.writeObject(obj); // Try marshalling ourselves before sending Object ois = new MarshalledObject(obj).get(); result = true; } catch (Exception ignore) { //ignore exception if (log.isDebugEnabled()) { log.debug("Ignored exception", ignore); } } finally { try { baos.close(); tstOOS.close(); } catch (Exception e) { //ignore exception if (log.isDebugEnabled()) { log.debug("Ignored stream close exception", e); } } } } return result; } }