/* * 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. *
* Sequences / arrays are only supported for string, float, double, long and boolean
* when using the typedefs from acscommon.idl such as "typedef sequence null
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
*
*
* 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 IDLEntity
if an IDL-defined struct was sent, or
* null
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
* "IDL to Java LanguageMapping Specification" version 1.2: 1.17 Mapping for Certain Nested Types 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());
}
}
}