From e6c733c4d9d9116216c0a0105b770267918a12f9 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Sun, 29 Aug 2010 18:32:20 +0000 Subject: Sandbox to experiment with different ways to embed the runtime. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@990620 13f79535-47bb-0310-9956-ffa450edef68 --- .../sca/binding/ejb/corba/ClassLoadingUtil.java | 365 ++++++++++ .../binding/ejb/corba/DynamicStubClassLoader.java | 150 ++++ .../sca/binding/ejb/corba/Java2IDLUtil.java | 811 +++++++++++++++++++++ .../binding/ejb/corba/ObjectInputStreamExt.java | 61 ++ .../binding/ejb/corba/StubMethodInterceptor.java | 154 ++++ .../sca/binding/ejb/corba/UtilInitializer.java | 69 ++ 6 files changed, 1610 insertions(+) create mode 100644 sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ClassLoadingUtil.java create mode 100644 sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/DynamicStubClassLoader.java create mode 100644 sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/Java2IDLUtil.java create mode 100644 sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ObjectInputStreamExt.java create mode 100644 sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/StubMethodInterceptor.java create mode 100644 sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/UtilInitializer.java (limited to 'sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba') diff --git a/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ClassLoadingUtil.java b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ClassLoadingUtil.java new file mode 100644 index 0000000000..c65868c23b --- /dev/null +++ b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ClassLoadingUtil.java @@ -0,0 +1,365 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.ejb.corba; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * Utility class for loading classes by a variety of name variations. + *

+ * Supported names types are: + *

+ * 1) Fully qualified class name (e.g., "java.lang.String", "org.apache.geronimo.kernel.ClassLoading" + * 2) Method signature encoding ("Ljava.lang.String;", "J", "I", etc.) + * 3) Primitive type names ("int", "boolean", etc.) + * 4) Method array signature strings ("[I", "[Ljava.lang.String") + * 5) Arrays using Java code format ("int[]", "java.lang.String[][]") + *

+ * The classes are loaded using the provided class loader. For the basic types, the primitive + * reflection types are returned. + * + * @version $Rev$ $Date$ + */ +public class ClassLoadingUtil { + + /** + * Table for mapping primitive class names/signatures to the implementing + * class object + */ + private static final HashMap PRIMITIVE_CLASS_MAP = new HashMap(); + + /** + * Table for mapping primitive classes back to their name signature type, which + * allows a reverse mapping to be performed from a class object into a resolvable + * signature. + */ + private static final HashMap CLASS_TO_SIGNATURE_MAP = new HashMap(); + + + /** + * Setup the primitives map. We make any entry for each primitive class using both the + * human readable name and the method signature shorthand type. + */ + static { + PRIMITIVE_CLASS_MAP.put("boolean", boolean.class); + PRIMITIVE_CLASS_MAP.put("Z", boolean.class); + PRIMITIVE_CLASS_MAP.put("byte", byte.class); + PRIMITIVE_CLASS_MAP.put("B", byte.class); + PRIMITIVE_CLASS_MAP.put("char", char.class); + PRIMITIVE_CLASS_MAP.put("C", char.class); + PRIMITIVE_CLASS_MAP.put("short", short.class); + PRIMITIVE_CLASS_MAP.put("S", short.class); + PRIMITIVE_CLASS_MAP.put("int", int.class); + PRIMITIVE_CLASS_MAP.put("I", int.class); + PRIMITIVE_CLASS_MAP.put("long", long.class); + PRIMITIVE_CLASS_MAP.put("J", long.class); + PRIMITIVE_CLASS_MAP.put("float", float.class); + PRIMITIVE_CLASS_MAP.put("F", float.class); + PRIMITIVE_CLASS_MAP.put("double", double.class); + PRIMITIVE_CLASS_MAP.put("D", double.class); + PRIMITIVE_CLASS_MAP.put("void", void.class); + PRIMITIVE_CLASS_MAP.put("V", void.class); + + // Now build a reverse mapping table. The table above has a many-to-one mapping for + // class names. To do the reverse, we need to pick just one. As long as the + // returned name supports "round tripping" of the requests, this will work fine. + + CLASS_TO_SIGNATURE_MAP.put(boolean.class, "Z"); + CLASS_TO_SIGNATURE_MAP.put(byte.class, "B"); + CLASS_TO_SIGNATURE_MAP.put(char.class, "C"); + CLASS_TO_SIGNATURE_MAP.put(short.class, "S"); + CLASS_TO_SIGNATURE_MAP.put(int.class, "I"); + CLASS_TO_SIGNATURE_MAP.put(long.class, "J"); + CLASS_TO_SIGNATURE_MAP.put(float.class, "F"); + CLASS_TO_SIGNATURE_MAP.put(double.class, "D"); + CLASS_TO_SIGNATURE_MAP.put(void.class, "V"); + } + + + /** + * Load a class that matches the requested name, using the provided class loader context. + *

+ * The class name may be a standard class name, the name of a primitive type Java + * reflection class (e.g., "boolean" or "int"), or a type in method type signature + * encoding. Array classes in either encoding form are also processed. + * + * @param className The name of the required class. + * @param classLoader The class loader used to resolve the class object. + * @return The Class object resolved from "className". + * @throws ClassNotFoundException When unable to resolve the class object. + * @throws IllegalArgumentException If either argument is null. + */ + public static Class loadClass(String className, ClassLoader classLoader) throws ClassNotFoundException { + + // the tests require IllegalArgumentExceptions for null values on either of these. + if (className == null) { + throw new IllegalArgumentException("className is null"); + } + + if (classLoader == null) { + throw new IllegalArgumentException("classLoader is null"); + } + // The easiest case is a proper class name. We just have the class loader resolve this. + // If the class loader throws a ClassNotFoundException, then we need to check each of the + // special name encodings we support. + try { + return classLoader.loadClass(className); + } catch (ClassNotFoundException ignore) { + // if not found, continue on to the other name forms. + } + + + // The second easiest version to resolve is a direct map to a primitive type name + // or method signature. Check our name-to-class map for one of those. + Class resolvedClass = (Class) PRIMITIVE_CLASS_MAP.get(className); + if (resolvedClass != null) { + return resolvedClass; + } + + // Class names in method signature have the format "Lfully.resolved.name;", + // so if it ends in a semicolon and begins with an "L", this must be in + // this format. Have the class loader try to load this. There are no other + // options if this fails, so just allow the class loader to throw the + // ClassNotFoundException. + if (className.endsWith(";") && className.startsWith("L")) { + // pick out the name portion + String typeName = className.substring(1, className.length() - 1); + // and delegate the loading to the class loader. + return classLoader.loadClass(typeName); + } + + // All we have left now are the array types. Method signature array types + // have a series of leading "[" characters to specify the number of dimensions. + // The other array type we handle uses trailing "[]" for the dimensions, just + // like the Java language syntax. + + // first check for the signature form ([[[[type). + if (className.charAt(0) == '[') { + // we have at least one array marker, now count how many leading '['s we have + // to get the dimension count. + int count = 0; + int nameLen = className.length(); + + while (count < nameLen && className.charAt(count) == '[') { + count++; + } + + // pull of the name subtype, which is everything after the last '[' + String arrayTypeName = className.substring(count, className.length()); + // resolve the type using a recursive call, which will load any of the primitive signature + // types as well as class names. + Class arrayType = loadClass(arrayTypeName, classLoader); + + // Resolving array types require a little more work. The array classes are + // created dynamically when the first instance of a given dimension and type is + // created. We need to create one using reflection to do this. + return getArrayClass(arrayType, count); + } + + + // ok, last chance. Now check for an array specification in Java language + // syntax. This will be a type name followed by pairs of "[]" to indicate + // the number of dimensions. + if (className.endsWith("[]")) { + // get the base component class name and the arrayDimensions + int count = 0; + int position = className.length(); + + while (position > 1 && className.substring(position - 2, position).equals("[]")) { + // count this dimension + count++; + // and step back the probe position. + position -= 2; + } + + // position now points at the location of the last successful test. This makes it + // easy to pick off the class name. + + String typeName = className.substring(0, position); + + // load the base type, again, doing this recursively + Class arrayType = loadClass(typeName, classLoader); + // and turn this into the class object + return getArrayClass(arrayType, count); + } + + throw new ClassNotFoundException("Could not load class " + className + " from unknown classloader; " + classLoader); + } + + + /** + * Map a class object back to a class name. The returned class object + * must be "round trippable", which means + *

+ * type == ClassLoading.loadClass(ClassLoading.getClassName(type), classLoader) + *

+ * must be true. To ensure this, the class name is always returned in + * method signature format. + * + * @param type The class object we convert into name form. + * @return A string representation of the class name, in method signature + * format. + */ + public static String getClassName(Class type) { + StringBuffer name = new StringBuffer(); + + // we test these in reverse order from the resolution steps, + // first handling arrays, then primitive types, and finally + // "normal" class objects. + + // First handle arrays. If a class is an array, the type is + // element stored at that level. So, for a 2-dimensional array + // of ints, the top-level type will be "[I". We need to loop + // down the hierarchy until we hit a non-array type. + while (type.isArray()) { + // add another array indicator at the front of the name, + // and continue with the next type. + name.append('['); + type = type.getComponentType(); + } + + // we're down to the base type. If this is a primitive, then + // we poke in the single-character type specifier. + if (type.isPrimitive()) { + name.append((String) CLASS_TO_SIGNATURE_MAP.get(type)); + } + // a "normal" class. This gets expressing using the "Lmy.class.name;" syntax. + else { + name.append('L'); + name.append(type.getName()); + name.append(';'); + } + return name.toString(); + } + + private static Class getArrayClass(Class type, int dimension) { + // Array.newInstance() requires an array of the requested number of dimensions + // that gives the size for each dimension. We just request 0 in each of the + // dimensions, which is not unlike a black hole singularity. + int[] dimensions = new int[dimension]; + // create an instance and return the associated class object. + return Array.newInstance(type, dimensions).getClass(); + } + + public static Set getAllTypes(Class type) { + Set allTypes = new LinkedHashSet(); + allTypes.add(type); + allTypes.addAll(getAllSuperClasses(type)); + allTypes.addAll(getAllInterfaces(type)); + return allTypes; + } + + private static Set getAllSuperClasses(Class clazz) { + Set allSuperClasses = new LinkedHashSet(); + for (Class superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) { + allSuperClasses.add(superClass); + } + return allSuperClasses; + } + + private static Set getAllInterfaces(Class clazz) { + Set allInterfaces = new LinkedHashSet(); + LinkedList stack = new LinkedList(); + stack.addAll(Arrays.asList(clazz.getInterfaces())); + while (!stack.isEmpty()) { + Class intf = (Class) stack.removeFirst(); + if (!allInterfaces.contains(intf)) { + allInterfaces.add(intf); + stack.addAll(Arrays.asList(intf.getInterfaces())); + } + } + return allInterfaces; + } + + public static Set reduceInterfaces(Set source) { + Class[] classes = (Class[]) source.toArray(new Class[source.size()]); + classes = reduceInterfaces(classes); + return new LinkedHashSet(Arrays.asList(classes)); + } + + /** + * If there are multiple interfaces, and some of them extend each other, + * eliminate the superclass in favor of the subclasses that extend them. + * + * If one of the entries is a class (not an interface), make sure it's + * the first one in the array. If more than one of the entries is a + * class, throws an IllegalArgumentException + * + * @param source the original list of interfaces + * @return the equal or smaller list of interfaces + */ + public static Class[] reduceInterfaces(Class[] source) { + // use a copy of the source array + source = (Class[]) source.clone(); + + for (int leftIndex = 0; leftIndex < source.length-1; leftIndex++) { + Class left = source[leftIndex]; + if(left == null) { + continue; + } + + for (int rightIndex = leftIndex +1; rightIndex < source.length; rightIndex++) { + Class right = source[rightIndex]; + if(right == null) { + continue; + } + + if(left == right || right.isAssignableFrom(left)) { + // right is the same as class or a sub class of left + source[rightIndex] = null; + } else if(left.isAssignableFrom(right)) { + // left is the same as class or a sub class of right + source[leftIndex] = null; + + // the left has been eliminated; move on to the next left + break; + } + } + } + + Class clazz = null; + for (int i = 0; i < source.length; i++) { + if (source[i] != null && !source[i].isInterface()) { + if (clazz != null) { + throw new IllegalArgumentException("Source contains two classes which are not subclasses of each other: " + clazz.getName() + ", " + source[i].getName()); + } + clazz = source[i]; + source[i] = null; + } + } + + List list = new ArrayList(source.length); + if (clazz != null) list.add(clazz); + for (int i = 0; i < source.length; i++) { + if(source[i] != null) { + list.add(source[i]); + } + } + return (Class[]) list.toArray(new Class[list.size()]); + } +} + diff --git a/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/DynamicStubClassLoader.java b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/DynamicStubClassLoader.java new file mode 100644 index 0000000000..82554a1c8f --- /dev/null +++ b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/DynamicStubClassLoader.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.ejb.corba; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import javax.rmi.CORBA.Stub; + +import net.sf.cglib.core.NamingPolicy; +import net.sf.cglib.core.Predicate; +import net.sf.cglib.proxy.Callback; +import net.sf.cglib.proxy.CallbackFilter; +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.FixedValue; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.NoOp; + +/** + * @version $Revision$ $Date$ + */ +public class DynamicStubClassLoader extends ClassLoader { + private static final String PACKAGE_PREFIX = "org.omg.stub."; + + @Override + public synchronized Class loadClass(final String name) throws ClassNotFoundException { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + // check if the stub already exists first + try { + return classLoader.loadClass(name); + } catch (ClassNotFoundException e) { + } + + // if this is not a class from the org.omg.stub name space don't attempt to generate + if (!name.startsWith(PACKAGE_PREFIX)) { + throw new ClassNotFoundException("Could not load class: " + name); + } + + // load the interfaces class we are attempting to create a stub for + Class iface = loadStubInterfaceClass(name, classLoader); + + // create the stub builder + try { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(Stub.class); + enhancer.setInterfaces(new Class[] {iface}); + enhancer.setCallbackFilter(FILTER); + enhancer.setCallbackTypes(new Class[] {NoOp.class, MethodInterceptor.class, FixedValue.class}); + enhancer.setUseFactory(false); + enhancer.setClassLoader(classLoader); + enhancer.setNamingPolicy(new NamingPolicy() { + public String getClassName(String s, String s1, Object o, Predicate predicate) { + return name; + } + }); + + // generate the class + Class result = enhancer.createClass(); + assert result != null; + + StubMethodInterceptor interceptor = new StubMethodInterceptor(iface); + Ids ids = new Ids(iface); + Enhancer.registerStaticCallbacks(result, new Callback[] {NoOp.INSTANCE, interceptor, ids}); + + return result; + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } + } + + private Class loadStubInterfaceClass(String name, ClassLoader classLoader) throws ClassNotFoundException { + try { + int begin = name.lastIndexOf('.') + 1; + String iPackage = name.substring(13, begin); + String iName = iPackage + name.substring(begin + 1, name.length() - 5); + + return classLoader.loadClass(iName); + } catch (ClassNotFoundException e) { + // don't log exceptions from CosNaming because it attempts to load every + // class bound into the name server + + //FIXME this variable is never read, can we remove the + // whole block of code?? + //boolean shouldLog = true; + StackTraceElement[] stackTrace = e.getStackTrace(); + for (int i = 0; i < stackTrace.length; i++) { + StackTraceElement stackTraceElement = stackTrace[i]; + if (stackTraceElement.getClassName().equals("org.omg.CosNaming.NamingContextExtPOA") && stackTraceElement + .getMethodName().equals("_invoke")) { + //shouldLog = false; + break; + } + } + + throw new ClassNotFoundException("Unable to generate stub", e); + } + } + + private static final CallbackFilter FILTER = new CallbackFilter() { + public int accept(Method method) { + // we don't intercept non-public methods like finalize + if (!Modifier.isPublic(method.getModifiers())) { + return 0; + } + + if (method.getReturnType().equals(String[].class) && method.getParameterTypes().length == 0 + && method.getName().equals("_ids")) { + return 2; + } + + if (Modifier.isAbstract(method.getModifiers())) { + return 1; + } + + return 0; + } + }; + + private static final class Ids implements FixedValue { + private final String[] typeIds; + + public Ids(Class type) { + typeIds = Java2IDLUtil.createCorbaIds(type); + } + + public Object loadObject() throws Exception { + return typeIds; + } + } + +} diff --git a/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/Java2IDLUtil.java b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/Java2IDLUtil.java new file mode 100644 index 0000000000..de8090c45b --- /dev/null +++ b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/Java2IDLUtil.java @@ -0,0 +1,811 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.ejb.corba; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.UnexpectedException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.ejb.spi.HandleDelegate; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.rmi.PortableRemoteObject; + +import org.omg.CORBA.ORB; +import org.omg.CORBA.UserException; +import org.omg.CORBA.portable.IDLEntity; +import org.omg.CORBA.portable.ResponseHandler; +import org.omg.CORBA.portable.UnknownException; +import org.omg.CORBA_2_3.portable.InputStream; +import org.omg.CORBA_2_3.portable.OutputStream; +import org.omg.IOP.Codec; +import org.omg.IOP.CodecFactory; +import org.omg.IOP.ENCODING_CDR_ENCAPS; +import org.omg.IOP.Encoding; + +/** + * Various utility functions. + *

+ * Note: #getORB() and #getCodec() rely on UtilInitializer to initialize the ORB and codec. + * + * @version $Rev$ $Date$ + * @see UtilInitializer + */ +public final class Java2IDLUtil { + private static ORB orb; + private static Codec codec; + private static HandleDelegate handleDelegate; + + public static ORB getORB() { + assert orb != null; + return orb; + } + + + + public static void setORB(ORB orb) throws UserException { + if (Java2IDLUtil.orb == null) { + Java2IDLUtil.orb = orb; + CodecFactory factory = (CodecFactory) Java2IDLUtil.orb.resolve_initial_references("CodecFactory"); + codec = factory.create_codec(new Encoding(ENCODING_CDR_ENCAPS.value, (byte) 1, (byte) 2)); + } + } + + public static Codec getCodec() { + assert codec != null; + return codec; + } + + public static HandleDelegate getHandleDelegate() throws NamingException { + if (handleDelegate == null) { + InitialContext ic = new InitialContext(); + handleDelegate = (HandleDelegate) ic.lookup("java:comp/HandleDelegate"); + } + return handleDelegate; + } + + private static final Pattern SCOPED_NAME_EXTRACTION_PATTERN = Pattern.compile("(\\\\\\\\)|(\\\\@)|(@)|(\\z)"); + + /** + * See csiv2 Specification 16.2.5 par. 63-64. We extract the username if any and un-escape any + * escaped \ and @ characters. + * + * @param scopedNameBytes + * @return + * @throws UnsupportedEncodingException + */ + public static String extractUserNameFromScopedName(byte[] scopedNameBytes) throws UnsupportedEncodingException { + String scopedUserName = new String(scopedNameBytes, "UTF8"); + return extractUserNameFromScopedName(scopedUserName); + } + + public static String extractUserNameFromScopedName(String scopedUserName) { + Matcher m = SCOPED_NAME_EXTRACTION_PATTERN.matcher(scopedUserName); + StringBuffer buf = new StringBuffer(); + while (m.find()) { + m.appendReplacement(buf, ""); + if (m.group(1) != null) { + buf.append('\\'); + } else if (m.group(2) != null) { + buf.append("@"); + } else if (m.group(3) != null) { + break; + } + } + return buf.toString(); + } + + private static final Pattern SCOPED_NAME_ESCAPE_PATTERN = Pattern.compile("(\\\\)|(@)"); + + public static String buildScopedUserName(String user, String domain) { + StringBuffer buf = new StringBuffer(); + if (user != null) { + escape(user, buf); + } + if (domain != null) { + buf.append('@'); + escape(domain, buf); + } + return buf.toString(); + } + + private static void escape(String s, StringBuffer buf) { + Matcher m = SCOPED_NAME_ESCAPE_PATTERN.matcher(s); + while (m.find()) { + m.appendReplacement(buf, ""); + if (m.group(1) != null) { + buf.append("\\\\"); + } else if (m.group(2) != null) { + buf.append("\\@"); + } + } + m.appendTail(buf); + } + + + public static String byteToString(byte[] data) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < data.length; i++) { + buffer.append(HEXCHAR[(data[i] >>> 4) & 0x0F]); + buffer.append(HEXCHAR[(data[i]) & 0x0F]); + } + return buffer.toString(); + + } + + private static final char[] HEXCHAR = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + public static void writeObject(Class type, Object object, OutputStream out) { + if (type == Void.TYPE) { + // do nothing for a void + } else if (type == Boolean.TYPE) { + out.write_boolean(((Boolean) object).booleanValue()); + } else if (type == Byte.TYPE) { + out.write_octet(((Byte) object).byteValue()); + } else if (type == Character.TYPE) { + out.write_wchar(((Character) object).charValue()); + } else if (type == Double.TYPE) { + out.write_double(((Double) object).doubleValue()); + } else if (type == Float.TYPE) { + out.write_float(((Float) object).floatValue()); + } else if (type == Integer.TYPE) { + out.write_long(((Integer) object).intValue()); + } else if (type == Long.TYPE) { + out.write_longlong(((Long) object).longValue()); + } else if (type == Short.TYPE) { + out.write_short(((Short) object).shortValue()); + } else { + // object types must be written in the context of the CORBA application server + // which properly write replaces our objects for CORBA + // ApplicationServer oldApplicationServer = ServerFederation.getApplicationServer(); + try { + // ServerFederation.setApplicationServer(corbaApplicationServer); + + // todo check if + // copy the result to force replacement + // CORBA does not call writeReplace on remote proxies + // + // HOWEVER, if this is an array, then we don't want to do the replacement + // because we can end up with a replacement element that's not compatible with the + // original array type, which results in an ArrayStoreException. Fortunately, + // the Yoko RMI support appears to be able to sort this out for us correctly. + if (object instanceof Serializable && !object.getClass().isArray()) { + try { + object = copyObj(Thread.currentThread().getContextClassLoader(), object); + } catch (Exception e) { + throw new UnknownException(e); + } + } + + if (type == Object.class || type == Serializable.class) { + javax.rmi.CORBA.Util.writeAny(out, object); + } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)) { + out.write_Object((org.omg.CORBA.Object) object); + } else if (Remote.class.isAssignableFrom(type)) { + javax.rmi.CORBA.Util.writeRemoteObject(out, object); + } else if (type.isInterface() && Serializable.class.isAssignableFrom(type)) { + javax.rmi.CORBA.Util.writeAbstractObject(out, object); + } else { + out.write_value((Serializable) object, type); + } + } finally { + // ServerFederation.setApplicationServer(oldApplicationServer); + } + } + } + + private static Object copyObj(ClassLoader classLoader, Object object) throws IOException, ClassNotFoundException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(object); + oos.flush(); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStreamExt ois = new ObjectInputStreamExt(bais, classLoader); + return ois.readObject(); + } + + public static Object readObject(Class type, InputStream in) { + if (type == Void.TYPE) { + return null; + } else if (type == Boolean.TYPE) { + return new Boolean(in.read_boolean()); + } else if (type == Byte.TYPE) { + return new Byte(in.read_octet()); + } else if (type == Character.TYPE) { + return new Character(in.read_wchar()); + } else if (type == Double.TYPE) { + return new Double(in.read_double()); + } else if (type == Float.TYPE) { + return new Float(in.read_float()); + } else if (type == Integer.TYPE) { + return new Integer(in.read_long()); + } else if (type == Long.TYPE) { + return new Long(in.read_longlong()); + } else if (type == Short.TYPE) { + return new Short(in.read_short()); + } else if (type == Object.class || type == Serializable.class) { + return javax.rmi.CORBA.Util.readAny(in); + } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)) { + return in.read_Object(type); + } else if (Remote.class.isAssignableFrom(type)) { + return PortableRemoteObject.narrow(in.read_Object(), type); + } else if (type.isInterface() && Serializable.class.isAssignableFrom(type)) { + return in.read_abstract_interface(); + } else { + return in.read_value(type); + } + } + + public static void throwException(Method method, InputStream in) throws Throwable { + // read the exception id + final String id = in.read_string(); + + // get the class name from the id + if (!id.startsWith("IDL:")) { + return; + } + + Class[] exceptionTypes = method.getExceptionTypes(); + for (int i = 0; i < exceptionTypes.length; i++) { + Class exceptionType = exceptionTypes[i]; + + String exceptionId = getExceptionId(exceptionType); + if (id.equals(exceptionId)) { + throw (Throwable) in.read_value(exceptionType); + } + } + throw new UnexpectedException(id); + } + + public static OutputStream writeUserException(Method method, ResponseHandler reply, Exception exception) throws Exception { + if (exception instanceof RuntimeException || exception instanceof RemoteException) { + throw exception; + } + + Class[] exceptionTypes = method.getExceptionTypes(); + for (int i = 0; i < exceptionTypes.length; i++) { + Class exceptionType = exceptionTypes[i]; + if (!exceptionType.isInstance(exception)) { + continue; + } + + OutputStream out = (OutputStream) reply.createExceptionReply(); + String exceptionId = getExceptionId(exceptionType); + out.write_string(exceptionId); + out.write_value(exception); + return out; + } + throw exception; + } + + public static String getExceptionId(Class exceptionType) { + String exceptionName = exceptionType.getName().replace('.', '/'); + if (exceptionName.endsWith("Exception")) { + exceptionName = exceptionName.substring(0, exceptionName.length() - "Exception".length()); + } + exceptionName += "Ex"; + String exceptionId = "IDL:" + exceptionName + ":1.0"; + return exceptionId; + } + + public static String[] createCorbaIds(Class type) { + List ids = new LinkedList(); + for (Iterator iterator = getAllInterfaces(type).iterator(); iterator.hasNext();) { + Class superInterface = (Class) iterator.next(); + if (Remote.class.isAssignableFrom(superInterface) && superInterface != Remote.class) { + ids.add("RMI:" + superInterface.getName() + ":0000000000000000"); + } + } + return (String[]) ids.toArray(new String[ids.size()]); + } + + private static Set getAllInterfaces(Class intfClass) { + Set allInterfaces = new LinkedHashSet(); + + LinkedList stack = new LinkedList(); + stack.addFirst(intfClass); + + while (!stack.isEmpty()) { + Class intf = (Class) stack.removeFirst(); + allInterfaces.add(intf); + stack.addAll(0, Arrays.asList(intf.getInterfaces())); + } + + return allInterfaces; + } + + public static Map mapMethodToOperation(Class intfClass) { + return iiopMap(intfClass, false); + } + + public static Map mapOperationToMethod(Class intfClass) { + return iiopMap(intfClass, true); + } + + private static Map iiopMap(Class intfClass, boolean operationToMethod) { + Method[] methods = getAllMethods(intfClass); + + // find every valid getter + HashMap getterByMethod = new HashMap(methods.length); + HashMap getterByName = new HashMap(methods.length); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + String methodName = method.getName(); + + // no arguments allowed + if (method.getParameterTypes().length != 0) { + continue; + } + + // must start with get or is + String verb; + if (methodName.startsWith("get") && methodName.length() > 3 && method.getReturnType() != void.class) { + verb = "get"; + } else if (methodName.startsWith("is") && methodName.length() > 2 && method.getReturnType() == boolean.class) { + verb = "is"; + } else { + continue; + } + + // must only throw Remote or Runtime Exceptions + boolean exceptionsValid = true; + Class[] exceptionTypes = method.getExceptionTypes(); + for (int j = 0; j < exceptionTypes.length; j++) { + Class exceptionType = exceptionTypes[j]; + if (!RemoteException.class.isAssignableFrom(exceptionType) && + !RuntimeException.class.isAssignableFrom(exceptionType) && + !Error.class.isAssignableFrom(exceptionType)) { + exceptionsValid = false; + break; + } + } + if (!exceptionsValid) { + continue; + } + + String propertyName; + if (methodName.length() > verb.length() + 1 && Character.isUpperCase(methodName.charAt(verb.length() + 1))) { + propertyName = methodName.substring(verb.length()); + } else { + propertyName = Character.toLowerCase(methodName.charAt(verb.length())) + methodName.substring(verb.length() + 1); + } + getterByMethod.put(method, propertyName); + getterByName.put(propertyName, method); + } + + HashMap setterByMethod = new HashMap(methods.length); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + String methodName = method.getName(); + + // must have exactally one arg + if (method.getParameterTypes().length != 1) { + continue; + } + + // must return non void + if (method.getReturnType() != void.class) { + continue; + } + + // must start with set + if (!methodName.startsWith("set") || methodName.length() <= 3) { + continue; + } + + // must only throw Remote or Runtime Exceptions + boolean exceptionsValid = true; + Class[] exceptionTypes = method.getExceptionTypes(); + for (int j = 0; j < exceptionTypes.length; j++) { + Class exceptionType = exceptionTypes[j]; + if (!RemoteException.class.isAssignableFrom(exceptionType) && + !RuntimeException.class.isAssignableFrom(exceptionType) && + !Error.class.isAssignableFrom(exceptionType)) { + exceptionsValid = false; + break; + } + } + if (!exceptionsValid) { + continue; + } + + String propertyName; + if (methodName.length() > 4 && Character.isUpperCase(methodName.charAt(4))) { + propertyName = methodName.substring(3); + } else { + propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4); + } + + // must have a matching getter + Method getter = (Method) getterByName.get(propertyName); + if (getter == null) { + continue; + } + + // setter property must match getter return value + if (!method.getParameterTypes()[0].equals(getter.getReturnType())) { + continue; + } + setterByMethod.put(method, propertyName); + } + + // index the methods by name... used to determine which methods are overloaded + HashMap overloadedMethods = new HashMap(methods.length); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + if (getterByMethod.containsKey(method) || setterByMethod.containsKey(method)) { + continue; + } + String methodName = method.getName(); + List methodList = (List) overloadedMethods.get(methodName); + if (methodList == null) { + methodList = new LinkedList(); + overloadedMethods.put(methodName, methodList); + } + methodList.add(method); + } + + // index the methods by lower case name... used to determine which methods differ only by case + HashMap caseCollisionMethods = new HashMap(methods.length); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + if (getterByMethod.containsKey(method) || setterByMethod.containsKey(method)) { + continue; + } + String lowerCaseMethodName = method.getName().toLowerCase(); + Set methodSet = (Set) caseCollisionMethods.get(lowerCaseMethodName); + if (methodSet == null) { + methodSet = new HashSet(); + caseCollisionMethods.put(lowerCaseMethodName, methodSet); + } + methodSet.add(method.getName()); + } + + String className = getClassName(intfClass); + Map iiopMap = new HashMap(methods.length); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + String iiopName = (String) getterByMethod.get(method); + if (iiopName != null) { + // if we have a leading underscore prepend with J + if (iiopName.charAt(0) == '_') { + iiopName = "J_get_" + iiopName.substring(1); + } else { + iiopName = "_get_" + iiopName; + } + } else { + iiopName = (String) setterByMethod.get(method); + if (iiopName != null) { + // if we have a leading underscore prepend with J + if (iiopName.charAt(0) == '_') { + iiopName = "J_set_" + iiopName.substring(1); + } else { + iiopName = "_set_" + iiopName; + } + } else { + iiopName = method.getName(); + + // if we have a leading underscore prepend with J + if (iiopName.charAt(0) == '_') { + iiopName = "J" + iiopName; + } + } + } + + // if this name only differs by case add the case index to the end + Set caseCollisions = (Set) caseCollisionMethods.get(method.getName().toLowerCase()); + if (caseCollisions != null && caseCollisions.size() > 1) { + iiopName += upperCaseIndexString(iiopName); + } + + // if this is an overloaded method append the parameter string + List overloads = (List) overloadedMethods.get(method.getName()); + if (overloads != null && overloads.size() > 1) { + iiopName += buildOverloadParameterString(method.getParameterTypes()); + } + + // if we have a leading underscore prepend with J + iiopName = replace(iiopName, '$', "U0024"); + + // if we have matched a keyword prepend with an underscore + if (keywords.contains(iiopName.toLowerCase())) { + iiopName = "_" + iiopName; + } + + // if the name is the same as the class name, append an underscore + if (iiopName.equalsIgnoreCase(className)) { + iiopName += "_"; + } + + if (operationToMethod) { + iiopMap.put(iiopName, method); + } else { + iiopMap.put(method, iiopName); + } + } + + return iiopMap; + } + + private static Method[] getAllMethods(Class intfClass) { + LinkedList methods = new LinkedList(); + for (Iterator iterator = getAllInterfaces(intfClass).iterator(); iterator.hasNext();) { + Class intf = (Class) iterator.next(); + methods.addAll(Arrays.asList(intf.getDeclaredMethods())); + } + + return (Method[]) methods.toArray(new Method[methods.size()]); + } + + /** + * Return the a string containing an underscore '_' index of each uppercase character in the IIOP name. + * + * This is used for distinction of names that only differ by case, since CORBA does not support case sensitive names. + */ + private static String upperCaseIndexString(String iiopName) { + StringBuffer stringBuffer = new StringBuffer(); + for (int i = 0; i < iiopName.length(); i++) { + char c = iiopName.charAt(i); + if (Character.isUpperCase(c)) { + stringBuffer.append('_').append(i); + } + } + return stringBuffer.toString(); + } + + /** + * Replaces any occurances of the specified "oldChar" with the new string. + * + * This is used to replace occurances if '$' in CORBA names since '$' is a special character + */ + private static String replace(String source, char oldChar, String newString) { + StringBuffer stringBuffer = new StringBuffer(source.length()); + for (int i = 0; i < source.length(); i++) { + char c = source.charAt(i); + if (c == oldChar) { + stringBuffer.append(newString); + } else { + stringBuffer.append(c); + } + } + return stringBuffer.toString(); + } + + /** + * Return the a string containing a double underscore '__' list of parameter types encoded using the Java to IDL rules. + * + * This is used for distinction of methods that only differ by parameter lists. + */ + private static String buildOverloadParameterString(Class[] parameterTypes) { + String name = ""; + if (parameterTypes.length ==0) { + name += "__"; + } else { + for (int i = 0; i < parameterTypes.length; i++) { + Class parameterType = parameterTypes[i]; + name += buildOverloadParameterString(parameterType); + } + } + return name.replace('.', '_'); + } + + /** + * Returns a single parameter type encoded using the Java to IDL rules. + */ + private static String buildOverloadParameterString(Class parameterType) { + String name = "_"; + + int arrayDimensions = 0; + while (parameterType.isArray()) { + arrayDimensions++; + parameterType = parameterType.getComponentType(); + } + + // arrays start with org_omg_boxedRMI_ + if (arrayDimensions > 0) { + name += "_org_omg_boxedRMI"; + } + + // IDLEntity types must be prefixed with org_omg_boxedIDL_ + if (IDLEntity.class.isAssignableFrom(parameterType)) { + name += "_org_omg_boxedIDL"; + } + + // add package... some types have special mappings in corba + String packageName = (String) specialTypePackages.get(parameterType.getName()); + if (packageName == null) { + packageName = getPackageName(parameterType.getName()); + } + if (packageName.length() > 0) { + name += "_" + packageName; + } + + // arrays now contain a dimension indicator + if (arrayDimensions > 0) { + name += "_" + "seq" + arrayDimensions; + } + + // add the class name + String className = (String) specialTypeNames.get(parameterType.getName()); + if (className == null) { + className = buildClassName(parameterType); + } + name += "_" + className; + + return name; + } + + /** + * Returns a string containing an encoded class name. + */ + private static String buildClassName(Class type) { + if (type.isArray()) { + throw new IllegalArgumentException("type is an array: " + type); + } + + // get the classname + String typeName = type.getName(); + int endIndex = typeName.lastIndexOf('.'); + if (endIndex < 0) { + return typeName; + } + StringBuffer className = new StringBuffer(typeName.substring(endIndex + 1)); + + // for innerclasses replace the $ separator with two underscores + // we can't just blindly replace all $ characters since class names can contain the $ character + if (type.getDeclaringClass() != null) { + String declaringClassName = getClassName(type.getDeclaringClass()); + assert className.toString().startsWith(declaringClassName + "$"); + className.replace(declaringClassName.length(), declaringClassName.length() + 1, "__"); + } + + // if we have a leading underscore prepend with J + if (className.charAt(0) == '_') { + className.insert(0, "J"); + } + return className.toString(); + } + + private static String getClassName(Class type) { + if (type.isArray()) { + throw new IllegalArgumentException("type is an array: " + type); + } + + // get the classname + String typeName = type.getName(); + int endIndex = typeName.lastIndexOf('.'); + if (endIndex < 0) { + return typeName; + } + return typeName.substring(endIndex + 1); + } + + private static String getPackageName(String interfaceName) { + int endIndex = interfaceName.lastIndexOf('.'); + if (endIndex < 0) { + return ""; + } + return interfaceName.substring(0, endIndex); + } + + private static final Map specialTypeNames; + private static final Map specialTypePackages; + private static final Set keywords; + + static { + specialTypeNames = new HashMap(); + specialTypeNames.put("boolean", "boolean"); + specialTypeNames.put("char", "wchar"); + specialTypeNames.put("byte", "octet"); + specialTypeNames.put("short", "short"); + specialTypeNames.put("int", "long"); + specialTypeNames.put("long", "long_long"); + specialTypeNames.put("float", "float"); + specialTypeNames.put("double", "double"); + specialTypeNames.put("java.lang.Class", "ClassDesc"); + specialTypeNames.put("java.lang.String", "WStringValue"); + specialTypeNames.put("org.omg.CORBA.Object", "Object"); + + specialTypePackages = new HashMap(); + specialTypePackages.put("boolean", ""); + specialTypePackages.put("char", ""); + specialTypePackages.put("byte", ""); + specialTypePackages.put("short", ""); + specialTypePackages.put("int", ""); + specialTypePackages.put("long", ""); + specialTypePackages.put("float", ""); + specialTypePackages.put("double", ""); + specialTypePackages.put("java.lang.Class", "javax.rmi.CORBA"); + specialTypePackages.put("java.lang.String", "CORBA"); + specialTypePackages.put("org.omg.CORBA.Object", ""); + + keywords = new HashSet(); + keywords.add("abstract"); + keywords.add("any"); + keywords.add("attribute"); + keywords.add("boolean"); + keywords.add("case"); + keywords.add("char"); + keywords.add("const"); + keywords.add("context"); + keywords.add("custom"); + keywords.add("default"); + keywords.add("double"); + keywords.add("enum"); + keywords.add("exception"); + keywords.add("factory"); + keywords.add("false"); + keywords.add("fixed"); + keywords.add("float"); + keywords.add("in"); + keywords.add("inout"); + keywords.add("interface"); + keywords.add("long"); + keywords.add("module"); + keywords.add("native"); + keywords.add("object"); + keywords.add("octet"); + keywords.add("oneway"); + keywords.add("out"); + keywords.add("private"); + keywords.add("public"); + keywords.add("raises"); + keywords.add("readonly"); + keywords.add("sequence"); + keywords.add("short"); + keywords.add("string"); + keywords.add("struct"); + keywords.add("supports"); + keywords.add("switch"); + keywords.add("true"); + keywords.add("truncatable"); + keywords.add("typedef"); + keywords.add("union"); + keywords.add("unsigned"); + keywords.add("valuebase"); + keywords.add("valuetype"); + keywords.add("void"); + keywords.add("wchar"); + keywords.add("wstring"); + } + +} diff --git a/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ObjectInputStreamExt.java b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ObjectInputStreamExt.java new file mode 100644 index 0000000000..1be4a98a48 --- /dev/null +++ b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/ObjectInputStreamExt.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.ejb.corba; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.lang.reflect.Proxy; + +/** + * @version $Rev$ $Date$ + */ +public class ObjectInputStreamExt extends ObjectInputStream { + + private ClassLoader classloader; + + public ObjectInputStreamExt(InputStream in, ClassLoader loader) throws IOException { + super(in); + this.classloader = loader; + } + + @Override + protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { + return ClassLoadingUtil.loadClass(classDesc.getName(), classloader); + } + + @Override + protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { + Class[] cinterfaces = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) + cinterfaces[i] = classloader.loadClass(interfaces[i]); + + try { + return Proxy.getProxyClass(classloader, cinterfaces); + } catch (IllegalArgumentException e) { + throw new ClassNotFoundException(null, e); + } + } + + ClassLoader getClassloader() { + return classloader; + } + +} diff --git a/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/StubMethodInterceptor.java b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/StubMethodInterceptor.java new file mode 100644 index 0000000000..3ff0ed552a --- /dev/null +++ b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/StubMethodInterceptor.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.ejb.corba; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; + +import javax.ejb.EJBObject; +import javax.rmi.CORBA.Stub; +import javax.rmi.CORBA.Util; + +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +import org.omg.CORBA.SystemException; +import org.omg.CORBA.portable.ApplicationException; +import org.omg.CORBA.portable.RemarshalException; +import org.omg.CORBA.portable.ServantObject; +import org.omg.CORBA_2_3.portable.InputStream; +import org.omg.CORBA_2_3.portable.OutputStream; + +/** + * @version $Revision$ $Date$ + */ +public class StubMethodInterceptor implements MethodInterceptor { + private static final Method ISIDENTICAL; + + static { + try { + ISIDENTICAL = EJBObject.class.getMethod("isIdentical", new Class[]{EJBObject.class}); + } catch (NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } + } + + private final Class type; + private final Map operations; + + public StubMethodInterceptor(Class type) { + this.type = type; + this.operations = Collections.unmodifiableMap(org.apache.tuscany.sca.binding.ejb.corba.Java2IDLUtil.mapMethodToOperation(type)); + } + + public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { + Stub stub = (Stub) proxy; + + // handle is identical in stub to avoid unnecessary round trip + if (method.equals(ISIDENTICAL)) { + org.omg.CORBA.Object otherObject = (org.omg.CORBA.Object) args[0]; + return new Boolean(stub._is_equivalent(otherObject)); + } + + // get the operation name object + String operationName = (String) operations.get(method); + if (operationName == null) { + throw new IllegalStateException("Unknown method: " + method); + } + + while (true) { + // if this is a stub to a remote object we invoke over the wire + if (!Util.isLocal(stub)) { + + InputStream in = null; + try { + // create the request output stream + OutputStream out = (OutputStream) stub._request(operationName, true); + + // write the arguments + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + Class parameterType = parameterTypes[i]; + org.apache.tuscany.sca.binding.ejb.corba.Java2IDLUtil.writeObject(parameterType, args[i], out); + } + + // send the invocation + in = (InputStream) stub._invoke(out); + + // read the result + Object result = org.apache.tuscany.sca.binding.ejb.corba.Java2IDLUtil.readObject(method.getReturnType(), in); + return result; + } catch (RemarshalException exception) { + continue; + } catch (ApplicationException exception) { + org.apache.tuscany.sca.binding.ejb.corba.Java2IDLUtil.throwException(method, (InputStream) exception.getInputStream()); + } catch (SystemException e) { + throw Util.mapSystemException(e); + } finally { + stub._releaseReply(in); + } + } else { + // get the servant + ServantObject servantObject = stub._servant_preinvoke(operationName, type); + if (servantObject == null) { + continue; + } + + try { + // copy the arguments + Object[] argsCopy = Util.copyObjects(args, stub._orb()); + + // invoke the servant + Object result = null; + try { + result = method.invoke(servantObject.servant, argsCopy); + } catch (InvocationTargetException e) { + if (e.getCause() != null) { + throw e.getCause(); + } + throw e; + } + + // copy the result + result = Util.copyObject(result, stub._orb()); + + return result; + } catch (Throwable throwable) { + // copy the exception + Throwable throwableCopy = (Throwable) Util.copyObject(throwable, stub._orb()); + + // if it is one of my exception rethrow it + Class[] exceptionTypes = method.getExceptionTypes(); + for (int i = 0; i < exceptionTypes.length; i++) { + Class exceptionType = exceptionTypes[i]; + if (exceptionType.isInstance(throwableCopy)) { + throw throwableCopy; + } + } + + throw Util.wrapException(throwableCopy); + } finally { + stub._servant_postinvoke(servantObject); + } + } + } + } +} diff --git a/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/UtilInitializer.java b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/UtilInitializer.java new file mode 100644 index 0000000000..f9aa9ffa22 --- /dev/null +++ b/sandbox/sebastien/java/embed/modules/binding-ejb-runtime/src/main/java/org/apache/tuscany/sca/binding/ejb/corba/UtilInitializer.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.ejb.corba; + +import org.omg.CORBA.LocalObject; +import org.omg.PortableInterceptor.ORBInitInfo; +import org.omg.PortableInterceptor.ORBInitializer; + +/** + * The sole purpose of this initializer is to register a non-singleton ORB + * with the class Util. + * + * @version $Revision$ $Date$ + * @see Java2IDLUtil + */ +public class UtilInitializer extends LocalObject implements ORBInitializer { + + private static final long serialVersionUID = 4901857563505370955L; + + /** + * Called during ORB initialization. If it is expected that initial + * services registered by an interceptor will be used by other + * interceptors, then those initial services shall be registered at + * this point via calls to + * ORBInitInfo.register_initial_reference. + * + * @param info provides initialization attributes and operations by + * which Interceptors can be registered. + */ + public void pre_init(ORBInitInfo info) { + } + + /** + * Called during ORB initialization. If a service must resolve initial + * references as part of its initialization, it can assume that all + * initial references will be available at this point. + *

+ * Calling the post_init operations is not the final + * task of ORB initialization. The final task, following the + * post_init calls, is attaching the lists of registered + * interceptors to the ORB. Therefore, the ORB does not contain the + * interceptors during calls to post_init. If an + * ORB-mediated call is made from within post_init, no + * request interceptors will be invoked on that call. + * Likewise, if an operation is performed which causes an IOR to be + * created, no IOR interceptors will be invoked. + * + * @param info provides initialization attributes and + * operations by which Interceptors can be registered. + */ + public void post_init(ORBInitInfo info) { + } +} -- cgit v1.2.3