/* * JBoss, Home of Professional Open Source. * Copyright 2008, 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.iiop.rmi.marshal.strategy; import java.lang.reflect.Method; import java.rmi.RemoteException; import org.omg.CORBA.UserException; import org.omg.CORBA.portable.IDLEntity; import org.omg.CORBA.portable.UnknownException; import org.omg.CORBA_2_3.portable.InputStream; import org.omg.CORBA_2_3.portable.OutputStream; import org.jboss.iiop.rmi.ExceptionAnalysis; import org.jboss.iiop.rmi.RMIIIOPViolationException; import org.jboss.iiop.rmi.marshal.CDRStream; import org.jboss.iiop.rmi.marshal.CDRStreamReader; import org.jboss.iiop.rmi.marshal.CDRStreamWriter; /** * A <code>SkeletonStrategy</code> for a given method knows how to * unmarshalthe sequence of method parameters from a CDR input stream, how to * marshal into a CDR output stream the return value of the method, and how to * marshal into a CDR output stream any exception thrown by the method. * * @author <a href="mailto:reverbel@ime.usp.br">Francisco Reverbel</a> * @version $Revision: 81018 $ */ public class SkeletonStrategy { /** * Each <code>CDRStreamReader</code> in the array unmarshals a method * parameter. */ private CDRStreamReader[] paramReaders; /** * A <code>Method</code> instance. */ private Method m; /** * Each <code>ExceptionWriter</code> in the array knows how to marshal * an exception that may be thrown be the method. The array is sorted so * that no writer for a derived exception appears after the base * exception writer. */ private ExceptionWriter[] excepWriters; /** * A <code>CDRStreamWriter</code> that marshals the return value of the * method. */ private CDRStreamWriter retvalWriter; // Public ----------------------------------------------------------------- /* * Constructs a <code>SkeletonStrategy</code> for a given method. */ public SkeletonStrategy(Method m) { // Keep the method this.m = m; // Initialize paramReaders Class[] paramTypes = m.getParameterTypes(); int len = paramTypes.length; paramReaders = new CDRStreamReader[len]; for (int i = 0; i < len; i++) { paramReaders[i] = CDRStream.readerFor(paramTypes[i]); } // Initialize excepWriters Class[] excepTypes = m.getExceptionTypes(); len = excepTypes.length; int n = 0; for (int i = 0; i < len; i++) { if (!RemoteException.class.isAssignableFrom(excepTypes[i])) { n++; } } excepWriters = new ExceptionWriter[n]; int j = 0; for (int i = 0; i < len; i++) { if (!RemoteException.class.isAssignableFrom(excepTypes[i])) { excepWriters[j++] = new ExceptionWriter(excepTypes[i]); } } ExceptionWriter.arraysort(excepWriters); // Initialize retvalWriter retvalWriter = CDRStream.writerFor(m.getReturnType()); } /** * Unmarshals the sequence of method parameters from an input stream. * * @param in a CDR input stream * @return an object array with the parameters. */ public Object[] readParams(InputStream in) { int len = paramReaders.length; Object[] params = new Object[len]; for (int i = 0; i < len; i++ ) { params[i] = paramReaders[i].read(in); } return params; } /** * Returns this <code>SkeletonStrategy</code>'s method. */ public Method getMethod() { return m; } /** * Returns true if this <code>SkeletonStrategy</code>'s method * is non void. */ public boolean isNonVoid() { return (retvalWriter != null); } /** * Marshals into an output stream the return value of the method. * * @param out a CDR output stream * @param retVal the value to be written. */ public void writeRetval(OutputStream out, Object retVal) { retvalWriter.write(out, retVal); } /** * Marshals into an output stream an exception thrown by the method. * * @param out a CDR output stream * @param e the exception to be written. */ public void writeException(OutputStream out, Exception e) { int len = excepWriters.length; for (int i = 0; i < len; i++) { if (excepWriters[i].getExceptionClass().isInstance(e)) { excepWriters[i].write(out, e); return; } } throw new UnknownException(e); } // Static inner class (private) -------------------------------------------- /** * An <code>ExceptionWriter</code> knows how to write exceptions of a given * class to a CDR output stream. */ private static class ExceptionWriter implements CDRStreamWriter { /** * The exception class. */ private Class clz; /* * If the exception class corresponds to an IDL-defined exception, this * field contains the write method of the associated helper class. * A null value indicates that the exception class does not correspond * to an IDL-defined exception. */ private java.lang.reflect.Method writeMethod = null; /** * The CORBA repository id of the exception class. (This field is used * if the exception class does not correspond to an IDL-defined * exception. An IDL-generated helper class provides the repository id * of an IDL-defined exception.) */ private String reposId; /** * Constructs an <code>ExceptionWriter</code> for a given exception * class. */ ExceptionWriter(Class clz) { this.clz = clz; if (IDLEntity.class.isAssignableFrom(clz) && UserException.class.isAssignableFrom(clz)) { // This ExceptionWriter corresponds to an IDL-defined exception String helperClassName = clz.getName() + "Helper"; try { Class helperClass = clz.getClassLoader().loadClass(helperClassName); Class[] paramTypes = { org.omg.CORBA.portable.OutputStream.class, clz }; writeMethod = helperClass.getMethod("write", paramTypes); } catch (ClassNotFoundException e) { throw new RuntimeException("Error loading class " + helperClassName + ": " + e); } catch (NoSuchMethodException e) { throw new RuntimeException("No write method in helper class " + helperClassName + ": " + e); } } else { // This ExceptionWriter does not correspond to an IDL-defined // exception try { this.reposId = ExceptionAnalysis.getExceptionAnalysis(clz) .getExceptionRepositoryId(); } catch (RMIIIOPViolationException e) { throw new RuntimeException("Cannot obtain " + "exception repository id for " + clz.getName() + ":\n" + e); } } } /** * Gets the exception <code>Class</code>. */ Class getExceptionClass() { return clz; } /** * Writes an exception to a CDR output stream. */ public void write(OutputStream out, Object excep) { if (writeMethod != null) { try { writeMethod.invoke(null, new Object[] { out, excep }); } catch (IllegalAccessException e) { throw new RuntimeException("Internal error: " + e); } catch (java.lang.reflect.InvocationTargetException e) { throw new RuntimeException("Exception marshaling IDLEntity: " + e.getTargetException()); } } else { out.write_string(reposId); out.write_value((Exception)excep, clz); } } /** * Sorts an <code>ExceptionWriter</code> array so that no derived * exception strategy appears after a base exception strategy. */ static void arraysort(ExceptionWriter[] a) { int len = a.length; for (int i = 0; i < len - 1; i++) { for (int j = i + 1; j < len; j++) { if (a[i].clz.isAssignableFrom(a[j].clz)) { ExceptionWriter tmp = a[i]; a[i] = a[j]; a[j] = tmp; } } } } } // end of inner class ExceptionWriter }