/* * ALMA - Atacama Large Millimiter Array (c) European Southern Observatory, 2004 * Copyright by ESO (in the framework of the ALMA collaboration), All rights * reserved * * This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * */ package alma.acs.nc; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import org.omg.CORBA.Any; import org.omg.CORBA.TCKind; import org.omg.CORBA.TypeCode; import org.omg.CORBA.TypeCodePackage.BadKind; import org.omg.CORBA.portable.IDLEntity; import alma.ACS.booleanSeqHelper; import alma.ACS.doubleSeqHelper; import alma.ACS.floatSeqHelper; import alma.ACS.longSeqHelper; import alma.ACS.stringSeqHelper; import alma.ACS.uLongLongSeqHelper; import alma.ACS.uLongSeqHelper; import alma.ACSErrTypeCommon.wrappers.AcsJBadParameterEx; import alma.ACSErrTypeCommon.wrappers.AcsJUnexpectedExceptionEx; import alma.ACSErrTypeJavaNative.wrappers.AcsJJavaAnyEx; import alma.acs.container.ContainerServicesBase; import alma.acs.exceptions.AcsJException; /** * Intended to be used as an aide to developers working with CORBA anys. If * there's some method you think should be added to this class to ease * developers' lives, please send this suggestion to the alma-sw-common@nrao.edu * or acs-discuss@nrao.edu mailing lists. * * @author dfugate * @version $Id$ */ class AnyAide { /** reference to the container services */ private final ContainerServicesBase m_containerServices; /** our own logger */ private final Logger m_logger; /** * Standard constructor. * * @param cs * Container services reference of the component. */ public AnyAide(ContainerServicesBase cs) { // save a local reference m_containerServices = cs; // just copy the reference m_logger = cs.getLogger(); } /** * Moved here from method arrayToCorbaAny (deleted in ACS 9.0). */ protected Any internalArrayToCorbaAny(Object objs) throws AcsJException { // class object for the array Class cl = objs.getClass(); if (!cl.isArray()) { Throwable cause = new Throwable("Object of type " + cl.getName() + " is not an array."); throw new AcsJJavaAnyEx(cause); } // class object for the array elements Class objClass = cl.getComponentType(); int length = Array.getLength(objs); // doubleSeq if (objClass.equals(double.class)) { double[] values = new double[length]; System.arraycopy(objs, 0, values, 0, length); return doubleArrayToCorbaAny(values); } // longSeq else if (objClass.equals(int.class)) { int[] values = new int[length]; System.arraycopy(objs, 0, values, 0, length); return intArrayToCorbaAny(values); } // stringSeq else if (objClass.equals(String.class)) { String[] values = new String[length]; System.arraycopy(objs, 0, values, 0, length); return stringArrayToCorbaAny(values); } // floatSeq else if (objClass.equals(float.class)) { float[] values = new float[length]; System.arraycopy(objs, 0, values, 0, length); return floatArrayToCorbaAny(values); } else { // if we do not know what it is, there's not much we can // do. Throwable cause = new Throwable(cl.getName() + " not supported!"); throw new AcsJJavaAnyEx(cause); } } public Any doubleArrayToCorbaAny(double[] doubles) { Any retVal = m_containerServices.getAdvancedContainerServices().getAny(); doubleSeqHelper.insert(retVal, doubles); return retVal; } public Any floatArrayToCorbaAny(float[] floats) { Any retVal = m_containerServices.getAdvancedContainerServices().getAny(); floatSeqHelper.insert(retVal, floats); return retVal; } public Any intArrayToCorbaAny(int[] ints) { Any retVal = m_containerServices.getAdvancedContainerServices().getAny(); longSeqHelper.insert(retVal, ints); return retVal; } public Any stringArrayToCorbaAny(String[] strings) { Any retVal = m_containerServices.getAdvancedContainerServices().getAny(); stringSeqHelper.insert(retVal, strings); return retVal; } /** * Converts a generic Java object to a CORBA Any. May fail. * * @param obj * Object to be converted to a CORBA any * @return A CORBA any with obj's data embedded within it. * @throws AcsJException * Thrown if there's some problem converting the object to an * any. TODO: make sure this works with enumerations. */ public Any objectToCorbaAny(Object obj) throws AcsJException { if (obj != null && obj.getClass().isArray()) { return internalArrayToCorbaAny(obj); } Any retVal = m_containerServices.getAdvancedContainerServices().getAny(); // null case if (obj == null) { retVal.insert_Object(null); } // check against string else if (obj instanceof String) { retVal.insert_string((String) obj); } // check against double else if (obj instanceof Double) { double value = ((Double) obj).doubleValue(); retVal.insert_double(value); } // check against long - CORBA long long and unsigned long long else if (obj instanceof Long) { long value = ((Long) obj).longValue(); retVal.insert_longlong(value); } // check against integer - CORBA long or unsigned long else if (obj instanceof Integer) { int value = ((Integer) obj).intValue(); retVal.insert_long(value); } // check against float else if (obj instanceof Float) { float value = ((Float) obj).floatValue(); retVal.insert_float(value); } else if (obj instanceof IDLEntity) { // as a last ditch attempt, we assume the object // is some sort of complex IDL struct/union/etc // and that this method will work. return complexObjectToCorbaAny((IDLEntity) obj); } else { Throwable cause = new Throwable("Bad arg of type " + obj.getClass().getName()); throw new AcsJBadParameterEx(cause); } return retVal; } /** * Converts a complex CORBA-based object to a CORBA any. * * @param obj * A complex CORBA-based object such as a user-defined IDL struct. * @return A CORBA any containing obj. * @throws AcsJException * if any problem occurs with the conversion. */ public Any complexObjectToCorbaAny(IDLEntity obj) throws AcsJException { if (obj == null) { Throwable cause = new Throwable("Method arg 'obj' was null"); throw new AcsJBadParameterEx(cause); } Any retVal = m_containerServices.getAdvancedContainerServices().getAny(); // ------ Class structHelperClass = null; // first double-check that the Java Object they are attempting to // actually looks like a CORBA type. try { // This is the CORBA helper class which is capable of inserting/extracting data from CORBA Anys. structHelperClass = Class.forName(obj.getClass().getName() + "Helper"); } catch (Exception e) { // If what's above fails...then the developer has specified a native Java // class which has nothing to do with CORBA. String msg = "The non-CORBA class '" + obj.getClass().getName() + "' cannot be converted to a CORBA Any."; Throwable cause = new Throwable(msg); m_logger.warning(msg); throw new alma.ACSErrTypeCommon.wrappers.AcsJTypeNotFoundEx(cause); } try { // get at the static insert method defined for all IDL structures and sequences. // TODO: Cache it in a struct - method map, perhaps using weak references. Method insert = structHelperClass.getMethod("insert", new Class[] { Any.class, obj.getClass() }); // arguments to insert method are just the newly created Any and the // IDL struct instance passed to this method. Object[] args = { retVal, obj }; insert.invoke(null, args); return retVal; } catch (NoSuchMethodException e) { // we got a Helper class, but it seems to be not the CORBA-generated kind Throwable cause = new Throwable("Class '" + structHelperClass.getName() + "' associated with the given object of type '" + obj.getClass().getName() + "' is incompatiable with CORBA: " + e.getMessage()); throw new AcsJBadParameterEx(cause); } catch (java.lang.reflect.InvocationTargetException e) { Throwable realEx = e.getCause(); String reason = "Failed to insert the given CORBA object into a CORBA Any: the helper class insert method threw an exception."; m_logger.log(Level.FINE, reason, realEx); Throwable cause = new Throwable(reason + realEx.getMessage()); throw new alma.ACSErrTypeJavaNative.wrappers.AcsJJavaLangEx(cause); // todo: NC-specific exception type } catch (Throwable thr) { String reason = "Failed to insert the given CORBA object into a CORBA Any."; m_logger.log(Level.FINE, reason, thr); Throwable cause = new Throwable(reason + thr.getMessage()); throw new AcsJUnexpectedExceptionEx(cause); } } /** * Method which attempts to (and under normal circumstances should succeed) * convert a CORBA any object to the corresponding Java object. For simple * CORBA types such as long, this method will extract the long and embed it * within a java.lang.Long object. In the event of failure, a null object is * returned. * <p> * Sequences / arrays are only supported for string, float, double, long and boolean * when using the typedefs from acscommon.idl such as "typedef sequence <float> floatSeq" * * @param any * A CORBA any containing some sort of CORBA object * @return the CORBA any converted into the corresponding Java type, or <code>null</code> if it failed. */ public Object corbaAnyToObject(Any any) { // @TODO check any==null // initialize the return value Object returnValue = null; // get the CORBA typecode enum. // we need this to deal with the simple types org.omg.CORBA.TCKind anyKind = any.type().kind(); // within this switch block, returnValue is set // to be some real (native) Java object rather // than a CORBA any type. at this time, ACS only // support "BACI Value types" defined within // $ACSROOT/include/baciValue.h (the "Type" enum). switch (anyKind.value()) { case org.omg.CORBA.TCKind._tk_null: // this case is quite simple. A null CORBA reference is null in Java as well returnValue = null; break; case org.omg.CORBA.TCKind._tk_string: // simple type in which we have an extract method returnValue = any.extract_string(); break; case org.omg.CORBA.TCKind._tk_double: // simple type in which we have an extract method returnValue = new Double(any.extract_double()); break; case org.omg.CORBA.TCKind._tk_long: // simple type in which we have an extract method returnValue = new Integer(any.extract_long()); break; case org.omg.CORBA.TCKind._tk_alias: String id = null; try { id = any.type().id(); } catch (BadKind ex) { // should never happen for a tk_alias } switch (id) { case "IDL:alma/ACS/longSeq:1.0": returnValue = longSeqHelper.extract(any); break; case "IDL:alma/ACS/uLongSeq:1.0": returnValue = uLongSeqHelper.extract(any); break; case "IDL:alma/ACS/uLongLongSeq:1.0": returnValue = uLongLongSeqHelper.extract(any); break; case "IDL:alma/ACS/floatSeq:1.0": returnValue = floatSeqHelper.extract(any); break; case "IDL:alma/ACS/doubleSeq:1.0": returnValue = doubleSeqHelper.extract(any); break; case "IDL:alma/ACS/stringSeq:1.0": returnValue = stringSeqHelper.extract(any); break; case "IDL:alma/ACS/booleanSeq:1.0": returnValue = booleanSeqHelper.extract(any); break; default: // who knows if there could be "IDL:alma/ACS/patternSeq:1.0" etc m_logger.severe("Got an unexpected tk_alias with id=" + id); } break; case org.omg.CORBA.TCKind._tk_ulong: // simple type in which we have an extract method returnValue = new Integer(any.extract_ulong()); break; case org.omg.CORBA.TCKind._tk_longlong: // simple type in which we have an extract method returnValue = new Long(any.extract_longlong()); break; case org.omg.CORBA.TCKind._tk_ulonglong: // simple type in which we have an extract method returnValue = new Long(any.extract_ulonglong()); break; case org.omg.CORBA.TCKind._tk_float: // simple type in which we have an extract method returnValue = new Float(any.extract_float()); break; // not yet ported for jacorb 3.4, where any.type().toString() has changed. Let's see if we need it. // case org.omg.CORBA.TCKind._tk_enum: // // very special case. at the moment, // // we just support enumerations defined within // // the uppermost IDL module // try { // String localHelperName = anyType + "Helper"; // localHelperName = localHelperName.replaceAll("::", "."); // Class localHelper = Class.forName(localHelperName); // // // Extract method of helper class // // Need access to this to convert an Any to the Java language // // type. // Method extract = localHelper.getMethod("extract", new Class[] { Any.class }); // Object[] args = { any }; // returnValue = extract.invoke(null, args); // } catch (Exception ex) { // m_logger.log(Level.SEVERE, "Failed to extract enum!", ex); // } // break; // pretty bad if we get this far! default: m_logger.severe("Could not extract an any of type " + any.type().toString()); break; } return returnValue; } /** * Extracts from a Corba Any the embedded user-defined event data. * The returned data can be either * <ol> * <li>a class implementing <code>IDLEntity</code> if an IDL-defined struct was sent, or * <li>an array of IDL-defined structs * </ol> * Other non-IDL defined classes or primitive types are not allowed as event data * (not totally sure but it seems like that, HSO 2006-12). * * @param any CORBA Any containing a complex, user-defined object within it * @return the CORBA Any parameter converted to an object of the * corresponding Java type, or <code>null</code> if the conversion failed. */ public Object complexAnyToObject(Any any) { // initialize the return value Object retValue = null; Class localHelper = null; // Create the IDL struct helper class // With Java Anys, we can extract the name of the underlying object // instance and from that all that needs to be done is to concatenate "Helper" // to get. String qualHelperClassName = null; try { org.omg.CORBA.TCKind kind = any.type().kind(); if (kind.equals(org.omg.CORBA.TCKind.tk_sequence)) { // the event data is a sequence instead of a single value or struct. Need to get the underlying type org.omg.CORBA.TypeCode sequenceType = any.type().content_type(); // @TODO check if the following applies also for sequences of primitive types, // or if there is a rule that we always must have structs as event data // (which is implied by always calling complexAnyToObject in push_structured_event) // Derive the Java package from the id. // First assume that the type is not defined nested inside an interface qualHelperClassName = corbaStructToJavaClass(sequenceType, false) + "SeqHelper"; try { localHelper = Class.forName(qualHelperClassName); } catch (ClassNotFoundException ex) { // it could be that we are dealing with a sequence of nested structs qualHelperClassName = corbaStructToJavaClass(sequenceType, true) + "SeqHelper"; localHelper = Class.forName(qualHelperClassName); } } else { // First assume that the type is not defined nested inside an interface qualHelperClassName = corbaStructToJavaClass(any.type(), false) + "Helper"; try { localHelper = Class.forName(qualHelperClassName); } catch(ClassNotFoundException ex) { // it could be that we are dealing with a nested struct qualHelperClassName = corbaStructToJavaClass(any.type(), true) + "Helper"; localHelper = Class.forName(qualHelperClassName); } } // Extract method of helper class // Need access to this to convert an Any to the Java language type. // TODO: Cache it in a struct - method map, perhaps using weak references. Method extract = localHelper.getMethod("extract", new Class[] { Any.class }); Object[] args = { any }; retValue = extract.invoke(null, args); } catch (ClassNotFoundException e) { // should never happen... String msg = "Failed to extract the event struct data from a CORBA Any because the helper class '" + qualHelperClassName + "' does not exist."; m_logger.log(Level.WARNING, msg, e); } catch (NoSuchMethodException e) { // should never happen... String msg = "Failed to process an any because the helper class '" + qualHelperClassName + "' does not provide the 'extract' method."; m_logger.log(Level.WARNING, msg, e); } // catch (ClassCastException e) { // // should never happen... // String msg = "Failed to process an any because the contained data does not seem to come from an IDL struct."; // m_logger.log(Level.WARNING, msg, e); // } catch (Throwable thr) { // IllegalAccessException, InvocationTargetException, TypeCodePackage.BadKind or any other throwable // should never happen... String msg = "Failed to process an any because of unexpected problem."; m_logger.log(Level.WARNING, msg, thr); } return retValue; } /** * Derives the qualified Java class name for an IDL-defined struct from the Corba ID of that struct. * See also jacorb-specific method "TypeCode#idlTypeName" * * @param isNestedStruct if true, "Package" will be inserted according to * <i>"IDL to Java LanguageMapping Specification" version 1.2: 1.17 Mapping for Certain Nested Types</i> apply. */ protected String corbaStructToJavaClass(TypeCode tc, boolean isNestedStruct) throws IllegalArgumentException { String qualName = null; if (tc.kind() == TCKind.tk_struct) { String prefix = "IDL:"; String suffix = ":1.0"; try { String id = tc.id(); if (!id.startsWith(prefix) || !id.endsWith(suffix)) { throw new IllegalArgumentException("Struct ID is expected to start with 'IDL:' and end with ':1.0'"); } String qualNameWithSlashes = id.substring(prefix.length(), id.length() - suffix.length()); qualName = qualNameWithSlashes.replace('/', '.'); } catch (BadKind ex) { // should never happen since we call it only for tk_struct throw new IllegalArgumentException(ex); } if (isNestedStruct) { int lastDotIndex = qualName.lastIndexOf('.'); if (lastDotIndex > 0) { String className = qualName.substring(lastDotIndex + 1); String jPackage = qualName.substring(0, lastDotIndex); jPackage += "Package."; // defined in IDL-Java mapping spec qualName = jPackage + className; } } return qualName; } else { throw new IllegalArgumentException("Expected TypeCode for a struct, but got TypeCode #" + tc.kind().value()); } } }