0001: /**
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */package org.apache.geronimo.corba.util;
0017:
0018: import java.io.ByteArrayInputStream;
0019: import java.io.ByteArrayOutputStream;
0020: import java.io.IOException;
0021: import java.io.Serializable;
0022: import java.io.UnsupportedEncodingException;
0023: import java.io.ObjectOutputStream;
0024: import java.rmi.Remote;
0025: import java.rmi.UnexpectedException;
0026: import java.rmi.RemoteException;
0027: import java.lang.reflect.Method;
0028: import java.util.List;
0029: import java.util.LinkedList;
0030: import java.util.Iterator;
0031: import java.util.Map;
0032: import java.util.Set;
0033: import java.util.Arrays;
0034: import java.util.LinkedHashSet;
0035: import java.util.HashMap;
0036: import java.util.HashSet;
0037: import java.util.regex.Pattern;
0038: import java.util.regex.Matcher;
0039:
0040: import javax.ejb.spi.HandleDelegate;
0041: import javax.naming.InitialContext;
0042: import javax.naming.NamingException;
0043: import javax.rmi.PortableRemoteObject;
0044:
0045: import org.apache.geronimo.corba.ORBConfiguration;
0046: import org.apache.geronimo.crypto.asn1.DERInputStream;
0047: import org.apache.geronimo.crypto.asn1.DERObjectIdentifier;
0048: import org.apache.geronimo.crypto.asn1.DEROutputStream;
0049: import org.apache.geronimo.crypto.asn1.x509.GeneralName;
0050: import org.apache.geronimo.crypto.asn1.x509.X509Name;
0051: import org.omg.CORBA.Any;
0052: import org.omg.CORBA.ORB;
0053: import org.omg.CORBA.UserException;
0054: import org.omg.CORBA.portable.ResponseHandler;
0055: import org.omg.CORBA.portable.UnknownException;
0056: import org.omg.CORBA.portable.IDLEntity;
0057: import org.omg.GSSUP.GSSUPMechOID;
0058: import org.omg.GSSUP.InitialContextToken;
0059: import org.omg.GSSUP.InitialContextTokenHelper;
0060: import org.omg.IOP.Codec;
0061: import org.omg.IOP.CodecFactory;
0062: import org.omg.IOP.ENCODING_CDR_ENCAPS;
0063: import org.omg.IOP.Encoding;
0064: import org.omg.CORBA_2_3.portable.OutputStream;
0065: import org.omg.CORBA_2_3.portable.InputStream;
0066: import org.apache.commons.logging.Log;
0067: import org.apache.commons.logging.LogFactory;
0068: import org.apache.geronimo.corba.CorbaApplicationServer;
0069: import org.apache.openejb.ProxyInfo;
0070: import org.apache.openejb.spi.ApplicationServer;
0071: import org.apache.openejb.core.ServerFederation;
0072:
0073: /**
0074: * Various utility functions.
0075: * <p/>
0076: * Note: #getORB() and #getCodec() rely on UtilInitializer to initialze the ORB and codec.
0077: *
0078: * @version $Rev: 503493 $ $Date: 2007-02-04 13:47:55 -0800 (Sun, 04 Feb 2007) $
0079: * @see UtilInitializer
0080: */
0081: public final class Util {
0082: private static final Log log = LogFactory.getLog(Util.class);
0083: private static final byte ASN_TAG_NT_EXPORTED_NAME1 = 0x04;
0084: private static final byte ASN_TAG_NT_EXPORTED_NAME2 = 0x01;
0085: private static final byte ASN_TAG_OID = 0x06;
0086: private static final byte ASN_TAG_GSS = 0x60;
0087: private static ORB orb;
0088: private static Codec codec;
0089: private static HandleDelegate handleDelegate;
0090: private static CorbaApplicationServer corbaApplicationServer = new CorbaApplicationServer();
0091: private static HashMap<String, ORBConfiguration> configuredOrbs = new HashMap<String, ORBConfiguration>();
0092:
0093: public static ORB getORB() {
0094: assert orb != null;
0095: return orb;
0096: }
0097:
0098: public static void registerORB(String id, ORBConfiguration orb) {
0099: configuredOrbs.put(id, orb);
0100: }
0101:
0102: public static ORBConfiguration getRegisteredORB(String id) {
0103: return configuredOrbs.get(id);
0104: }
0105:
0106: public static void unregisterORB(String id) {
0107: configuredOrbs.remove(id);
0108: }
0109:
0110: public static void setORB(ORB orb) throws UserException {
0111: if (Util.orb == null) {
0112: Util.orb = orb;
0113: CodecFactory factory = (CodecFactory) Util.orb
0114: .resolve_initial_references("CodecFactory");
0115: codec = factory.create_codec(new Encoding(
0116: ENCODING_CDR_ENCAPS.value, (byte) 1, (byte) 2));
0117: }
0118: }
0119:
0120: public static Codec getCodec() {
0121: assert codec != null;
0122: return codec;
0123: }
0124:
0125: public static HandleDelegate getHandleDelegate()
0126: throws NamingException {
0127: if (handleDelegate == null) {
0128: InitialContext ic = new InitialContext();
0129: handleDelegate = (HandleDelegate) ic
0130: .lookup("java:comp/HandleDelegate");
0131: }
0132: return handleDelegate;
0133: }
0134:
0135: public static Object getEJBProxy(ProxyInfo info) {
0136: if (info.getInterfaceType().isHome()) {
0137: return corbaApplicationServer.getEJBHome(info);
0138: } else {
0139: return corbaApplicationServer.getEJBObject(info);
0140: }
0141: }
0142:
0143: public static byte[] encodeOID(String oid) throws IOException {
0144: oid = (oid.startsWith("oid:") ? oid.substring(4) : oid);
0145: return encodeOID(new DERObjectIdentifier(oid));
0146: }
0147:
0148: public static byte[] encodeOID(DERObjectIdentifier oid)
0149: throws IOException {
0150: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
0151: DEROutputStream dOut = new DEROutputStream(bOut);
0152:
0153: dOut.writeObject(oid);
0154:
0155: return bOut.toByteArray();
0156: }
0157:
0158: public static String decodeOID(byte[] oid) throws IOException {
0159: return decodeOIDDERObjectIdentifier(oid).getId();
0160: }
0161:
0162: public static DERObjectIdentifier decodeOIDDERObjectIdentifier(
0163: byte[] oid) throws IOException {
0164: ByteArrayInputStream bIn = new ByteArrayInputStream(oid);
0165: DERInputStream dIn = new DERInputStream(bIn);
0166:
0167: return (DERObjectIdentifier) dIn.readObject();
0168: }
0169:
0170: public static byte[] encodeGeneralName(String name)
0171: throws IOException {
0172: return encodeGeneralName(new X509Name(name));
0173: }
0174:
0175: public static byte[] encodeGeneralName(X509Name x509Name)
0176: throws IOException {
0177: return encodeGeneralName(new GeneralName(x509Name));
0178: }
0179:
0180: public static byte[] encodeGeneralName(GeneralName generalName)
0181: throws IOException {
0182: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
0183: DEROutputStream dOut = new DEROutputStream(bOut);
0184:
0185: dOut.writeObject(generalName);
0186:
0187: return bOut.toByteArray();
0188: }
0189:
0190: public static String decodeGeneralName(byte[] name)
0191: throws IOException {
0192: throw new java.lang.UnsupportedOperationException();
0193: }
0194:
0195: /**
0196: * This method encodes a name as if it was encoded using the GSS-API
0197: * gss_export_name() function call (see RFC 2743, page 84).
0198: * The oid to indicate names of this format is:<br/>
0199: * {1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes),
0200: * 4(gss-api-exported-name)}<br/>
0201: * The token has the following format:
0202: * <table>
0203: * <tr><td><b>Offset</b></td><td><b>Meaning</b></td><td><b>Value</b></td></tr>
0204: * <tr><td>0</td><td>token id</td><td>0x04</td></tr>
0205: * <tr><td>1</td><td>token id</td><td>0x01</td></tr>
0206: * <p/>
0207: * <tr><td>2</td><td>oid length</td><td>hi-byte (len/0xFF)</td></tr>
0208: * <tr><td>3</td><td>oid length</td><td>lo-byte (len%0xFF)</td></tr>
0209: * <p/>
0210: * <tr><td>4</td><td>oid</td><td>oid:1.3.6.1.5.6.4</td></tr>
0211: * <p/>
0212: * <tr><td>n+0</td><td>name length</td><td>len/0xFFFFFF</td></tr>
0213: * <tr><td>n+1</td><td>name length</td><td>(len%0xFFFFFF)/0xFFFF</td></tr>
0214: * <tr><td>n+2</td><td>name length</td><td>((len%0xFFFFFF)%0xFFFF)/0xFF</td></tr>
0215: * <tr><td>n+3</td><td>name length</td><td>((len%0xFFFFFF)%0xFFFF)%0xFF</td></tr>
0216: * <p/>
0217: * <tr><td>n+4</td><td>name</td><td>foo</td></tr>
0218: * </table>
0219: *
0220: * @param oid The oid of the mechanism this name is exported from.
0221: * @param name The name to be exported.
0222: * @return The byte array representing the exported name object.
0223: */
0224: public static byte[] encodeGSSExportName(String oid, String name) {
0225: try {
0226: byte[] oid_arr = encodeOID(oid);
0227: int oid_len = oid_arr.length;
0228: byte[] name_arr = name.getBytes("UTF-8");
0229: int name_len = name_arr.length;
0230:
0231: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0232: // token id at 0
0233: baos.write(ASN_TAG_NT_EXPORTED_NAME1);
0234: baos.write(ASN_TAG_NT_EXPORTED_NAME2);
0235:
0236: // write the two length bytes
0237: baos.write((byte) (oid_len & 0xFF00) >> 8);
0238: baos.write((byte) (oid_len & 0x00FF));
0239:
0240: // oid at 2
0241: baos.write(oid_arr);
0242:
0243: // name length at n
0244: baos.write((byte) (name_len & 0xFF000000) >> 24);
0245: baos.write((byte) (name_len & 0x00FF0000) >> 16);
0246: baos.write((byte) (name_len & 0x0000FF00) >> 8);
0247: baos.write((byte) (name_len & 0x000000FF));
0248:
0249: // name at n+4
0250: baos.write(name_arr);
0251: return baos.toByteArray();
0252: } catch (Exception ex) {
0253: // do nothing, return null
0254: }
0255: return null;
0256: }
0257:
0258: /**
0259: * This function reads a name from a byte array which was created
0260: * by the gssExportName() method.
0261: *
0262: * @param name_tok The GSS name token.
0263: * @return The name from the GSS name token.
0264: */
0265: public static String decodeGSSExportName(byte[] name_tok) {
0266: String result = null;
0267: if (name_tok != null) {
0268: ByteArrayInputStream bais = new ByteArrayInputStream(
0269: name_tok);
0270: try {
0271: // GSSToken tag 1 0x04
0272: int t1 = bais.read();
0273: if (t1 == ASN_TAG_NT_EXPORTED_NAME1) {
0274: // GSSToken tag 2 0x01
0275: int t2 = bais.read();
0276: if (t2 == ASN_TAG_NT_EXPORTED_NAME2) {
0277: // read the two length bytes
0278: int l = bais.read() << 8;
0279: l += bais.read();
0280:
0281: // read the oid
0282: byte[] oid_arr = new byte[l];
0283: bais.read(oid_arr, 0, l);
0284: String oid = decodeOID(oid_arr);
0285:
0286: if (oid.equals(GSSUPMechOID.value.substring(4))) {
0287: int l1 = bais.read();
0288: int l2 = bais.read();
0289: int l3 = bais.read();
0290: int l4 = bais.read();
0291:
0292: int name_len = (l1 << 24) + (l2 << 16)
0293: + (l3 << 8) + l4;
0294: byte[] name_arr = new byte[name_len];
0295: bais.read(name_arr, 0, name_len);
0296: result = new String(name_arr);
0297: } else {
0298: System.err
0299: .print("ASN1Utils.gssImportName: Unknown OID: "
0300: + oid
0301: + " ('"
0302: + Integer
0303: .toHexString(oid_arr[0])
0304: + "')");
0305: }
0306: }
0307: }
0308: } catch (Exception ex) {
0309: ex.printStackTrace();
0310: // do nothing, return null
0311: }
0312: }
0313: return result;
0314: }
0315:
0316: private static final Pattern SCOPED_NAME_EXTRACTION_PATTERN = Pattern
0317: .compile("(\\\\\\\\)|(\\\\@)|(@)|(\\z)");
0318:
0319: /**
0320: * See csiv2 spec 16.2.5 par. 63-64. We extract the username if any and un-escape any
0321: * escaped \ and @ characters.
0322: *
0323: * @param scopedNameBytes
0324: * @return
0325: * @throws UnsupportedEncodingException
0326: */
0327: public static String extractUserNameFromScopedName(
0328: byte[] scopedNameBytes) throws UnsupportedEncodingException {
0329: String scopedUserName = new String(scopedNameBytes, "UTF8");
0330: return extractUserNameFromScopedName(scopedUserName);
0331: }
0332:
0333: public static String extractUserNameFromScopedName(
0334: String scopedUserName) {
0335: Matcher m = SCOPED_NAME_EXTRACTION_PATTERN
0336: .matcher(scopedUserName);
0337: StringBuffer buf = new StringBuffer();
0338: while (m.find()) {
0339: m.appendReplacement(buf, "");
0340: if (m.group(1) != null) {
0341: buf.append('\\');
0342: } else if (m.group(2) != null) {
0343: buf.append("@");
0344: } else if (m.group(3) != null) {
0345: break;
0346: }
0347: }
0348: return buf.toString();
0349: }
0350:
0351: private static final Pattern SCOPED_NAME_ESCAPE_PATTERN = Pattern
0352: .compile("(\\\\)|(@)");
0353:
0354: public static String buildScopedUserName(String user, String domain) {
0355: StringBuffer buf = new StringBuffer();
0356: if (user != null) {
0357: escape(user, buf);
0358: }
0359: if (domain != null) {
0360: buf.append('@');
0361: escape(domain, buf);
0362: }
0363: return buf.toString();
0364: }
0365:
0366: private static void escape(String s, StringBuffer buf) {
0367: Matcher m = SCOPED_NAME_ESCAPE_PATTERN.matcher(s);
0368: while (m.find()) {
0369: m.appendReplacement(buf, "");
0370: if (m.group(1) != null) {
0371: buf.append("\\\\");
0372: } else if (m.group(2) != null) {
0373: buf.append("\\@");
0374: }
0375: }
0376: m.appendTail(buf);
0377: }
0378:
0379: /**
0380: * Encode a mechanism independent initial context token (GSSToken). Defined
0381: * in [IETF RFC 2743] Section 3.1, "Mechanism-Independent token Format" pp. 81-82.
0382: * <table>
0383: * <tr><td><b>Offset</b></td><td><b>Meaning</b></td></tr>
0384: * <tr><td>0</td><td>ASN1 tag</td></tr>
0385: * <tr><td>1</td><td>token length (<128)</td></tr>
0386: * <tr><td>2</td><td>mechanism oid</td></tr>
0387: * <tr><td>n</td><td>mechanism specific token (e.g. GSSUP::InitialContextToken)</td></tr>
0388: * </table>
0389: * Currently only one mechanism specific token is supported: GSS username password
0390: * (GSSUP::InitialContextToken).
0391: *
0392: * @param orb The orb to get an Any from.
0393: * @param codec The codec to do the encoding of the Any.
0394: * @param user The username.
0395: * @param pwd The password of the user.
0396: * @param target The target name.
0397: * @return The byte array of the ASN1 encoded GSSToken.
0398: */
0399: public static byte[] encodeGSSUPToken(ORB orb, Codec codec,
0400: String user, String pwd, String target) {
0401: byte[] result = null;
0402: try {
0403: // write the GSS ASN tag
0404: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0405: baos.write(ASN_TAG_GSS);
0406:
0407: // create and encode a GSSUP initial context token
0408: InitialContextToken init_token = new InitialContextToken();
0409: init_token.username = user.getBytes("UTF-8");
0410:
0411: init_token.password = pwd.getBytes("UTF-8");
0412:
0413: init_token.target_name = encodeGSSExportName(
0414: GSSUPMechOID.value.substring(4), target);
0415:
0416: Any a = orb.create_any();
0417: InitialContextTokenHelper.insert(a, init_token);
0418: byte[] init_ctx_token = codec.encode_value(a);
0419:
0420: // encode the mechanism oid
0421: byte[] oid_arr = encodeOID(GSSUPMechOID.value.substring(4));
0422:
0423: // write the length
0424: baos
0425: .write((byte) (oid_arr.length
0426: + init_ctx_token.length + 2));
0427:
0428: // write the mechanism oid
0429: baos.write(oid_arr);
0430:
0431: // write the
0432: baos.write(init_ctx_token);
0433:
0434: // get the bytes
0435: result = baos.toByteArray();
0436: } catch (Exception ex) {
0437: // do nothing, return null
0438: }
0439: return result;
0440: }
0441:
0442: /**
0443: * Decode an GSSUP InitialContextToken from a GSSToken.
0444: *
0445: * @param codec The codec to do the encoding of the Any.
0446: * @param gssup_tok The InitialContextToken struct to fill in the decoded values.
0447: * @return Return true when decoding was successful, false otherwise.
0448: */
0449: public static boolean decodeGSSUPToken(Codec codec,
0450: byte[] token_arr, InitialContextToken gssup_tok) {
0451: boolean result = false;
0452: if (gssup_tok != null) {
0453: ByteArrayInputStream bais = new ByteArrayInputStream(
0454: token_arr);
0455: try {
0456: // GSSToken tag
0457: int c = bais.read();
0458: if (c == ASN_TAG_GSS) {
0459: // GSSToken length
0460: int token_len = bais.read();
0461: // OID tag
0462: int oid_tag = bais.read();
0463: if (oid_tag == ASN_TAG_OID) {
0464: // OID length
0465: int oid_len = bais.read();
0466: byte[] oid_tmp_arr = new byte[oid_len];
0467: bais.read(oid_tmp_arr, 0, oid_len);
0468: byte[] oid_arr = new byte[oid_len + 2];
0469: oid_arr[0] = (byte) oid_tag;
0470: oid_arr[1] = (byte) oid_len;
0471: System.arraycopy(oid_tmp_arr, 0, oid_arr, 2,
0472: oid_len);
0473: String oid = decodeOID(oid_arr);
0474: if (oid.equals(GSSUPMechOID.value.substring(4))) {
0475: int len = token_len - oid_len;
0476: byte[] init_tok_arr = new byte[len];
0477: bais.read(init_tok_arr, 0, len);
0478: Any a = codec.decode_value(init_tok_arr,
0479: InitialContextTokenHelper.type());
0480: InitialContextToken token = InitialContextTokenHelper
0481: .extract(a);
0482: if (token != null) {
0483: gssup_tok.username = token.username;
0484: gssup_tok.password = token.password;
0485: gssup_tok.target_name = decodeGSSExportName(
0486: token.target_name).getBytes(
0487: "UTF-8");
0488:
0489: result = true;
0490: }
0491: }
0492: }
0493: }
0494: } catch (Exception ex) {
0495: // do nothing, return false
0496: }
0497: }
0498: return result;
0499: }
0500:
0501: public static String byteToString(byte[] data) {
0502: StringBuffer buffer = new StringBuffer();
0503: for (int i = 0; i < data.length; i++) {
0504: buffer.append(HEXCHAR[(data[i] >>> 4) & 0x0F]);
0505: buffer.append(HEXCHAR[(data[i]) & 0x0F]);
0506: }
0507: return buffer.toString();
0508:
0509: }
0510:
0511: private static final char[] HEXCHAR = { '0', '1', '2', '3', '4',
0512: '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
0513:
0514: public static void writeObject(Class type, Object object,
0515: OutputStream out) {
0516: if (type == Void.TYPE) {
0517: // do nothing for a void
0518: } else if (type == Boolean.TYPE) {
0519: out.write_boolean(((Boolean) object).booleanValue());
0520: } else if (type == Byte.TYPE) {
0521: out.write_octet(((Byte) object).byteValue());
0522: } else if (type == Character.TYPE) {
0523: out.write_wchar(((Character) object).charValue());
0524: } else if (type == Double.TYPE) {
0525: out.write_double(((Double) object).doubleValue());
0526: } else if (type == Float.TYPE) {
0527: out.write_float(((Float) object).floatValue());
0528: } else if (type == Integer.TYPE) {
0529: out.write_long(((Integer) object).intValue());
0530: } else if (type == Long.TYPE) {
0531: out.write_longlong(((Long) object).longValue());
0532: } else if (type == Short.TYPE) {
0533: out.write_short(((Short) object).shortValue());
0534: } else {
0535: // object types must bbe written in the context of the corba application server
0536: // which properly write replaces our objects for corba
0537: ApplicationServer oldApplicationServer = ServerFederation
0538: .getApplicationServer();
0539: try {
0540: ServerFederation
0541: .setApplicationServer(corbaApplicationServer);
0542:
0543: // todo check if
0544: // copy the result to force replacement
0545: // corba does not call writeReplace on remote proxies
0546: //
0547: // HOWEVER, if this is an array, then we don't want to do the replacement
0548: // because we can end up with a replacement element that's not compatible with the
0549: // original array type, which results in an ArrayStoreException. Fortunately,
0550: // the Yoko RMI support appears to be able to sort this out for us correctly.
0551: if (object instanceof Serializable
0552: && !object.getClass().isArray()) {
0553: try {
0554: object = copyObj(Thread.currentThread()
0555: .getContextClassLoader(), object);
0556: } catch (Exception e) {
0557: log.debug("Exception in result copy", e);
0558: throw new UnknownException(e);
0559: }
0560: }
0561:
0562: if (type == Object.class || type == Serializable.class) {
0563: javax.rmi.CORBA.Util.writeAny(out, object);
0564: } else if (org.omg.CORBA.Object.class
0565: .isAssignableFrom(type)) {
0566: out.write_Object((org.omg.CORBA.Object) object);
0567: } else if (Remote.class.isAssignableFrom(type)) {
0568: javax.rmi.CORBA.Util.writeRemoteObject(out, object);
0569: } else if (type.isInterface()
0570: && Serializable.class.isAssignableFrom(type)) {
0571: javax.rmi.CORBA.Util.writeAbstractObject(out,
0572: object);
0573: } else {
0574: out.write_value((Serializable) object, type);
0575: }
0576: } finally {
0577: ServerFederation
0578: .setApplicationServer(oldApplicationServer);
0579: }
0580: }
0581: }
0582:
0583: private static Object copyObj(ClassLoader classLoader, Object object)
0584: throws IOException, ClassNotFoundException {
0585: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0586: ObjectOutputStream oos = new ObjectOutputStream(baos);
0587: oos.writeObject(object);
0588: oos.flush();
0589: oos.close();
0590: ByteArrayInputStream bais = new ByteArrayInputStream(baos
0591: .toByteArray());
0592: ObjectInputStreamExt ois = new ObjectInputStreamExt(bais,
0593: classLoader);
0594: return ois.readObject();
0595: }
0596:
0597: public static Object readObject(Class type, InputStream in) {
0598: if (type == Void.TYPE) {
0599: return null;
0600: } else if (type == Boolean.TYPE) {
0601: return new Boolean(in.read_boolean());
0602: } else if (type == Byte.TYPE) {
0603: return new Byte(in.read_octet());
0604: } else if (type == Character.TYPE) {
0605: return new Character(in.read_wchar());
0606: } else if (type == Double.TYPE) {
0607: return new Double(in.read_double());
0608: } else if (type == Float.TYPE) {
0609: return new Float(in.read_float());
0610: } else if (type == Integer.TYPE) {
0611: return new Integer(in.read_long());
0612: } else if (type == Long.TYPE) {
0613: return new Long(in.read_longlong());
0614: } else if (type == Short.TYPE) {
0615: return new Short(in.read_short());
0616: } else if (type == Object.class || type == Serializable.class) {
0617: return javax.rmi.CORBA.Util.readAny(in);
0618: } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)) {
0619: return in.read_Object(type);
0620: } else if (Remote.class.isAssignableFrom(type)) {
0621: return PortableRemoteObject.narrow(in.read_Object(), type);
0622: } else if (type.isInterface()
0623: && Serializable.class.isAssignableFrom(type)) {
0624: return in.read_abstract_interface();
0625: } else {
0626: return in.read_value(type);
0627: }
0628: }
0629:
0630: public static void throwException(Method method, InputStream in)
0631: throws Throwable {
0632: // read the exception id
0633: final String id = in.read_string();
0634:
0635: // get the class name from the id
0636: if (!id.startsWith("IDL:")) {
0637: log.warn("Malformed exception id: " + id);
0638: return;
0639: }
0640:
0641: Class[] exceptionTypes = method.getExceptionTypes();
0642: for (int i = 0; i < exceptionTypes.length; i++) {
0643: Class exceptionType = exceptionTypes[i];
0644:
0645: String exceptionId = getExceptionId(exceptionType);
0646: if (id.equals(exceptionId)) {
0647: throw (Throwable) in.read_value(exceptionType);
0648: }
0649: }
0650: throw new UnexpectedException(id);
0651: }
0652:
0653: public static OutputStream writeUserException(Method method,
0654: ResponseHandler reply, Exception exception)
0655: throws Exception {
0656: if (exception instanceof RuntimeException
0657: || exception instanceof RemoteException) {
0658: throw exception;
0659: }
0660:
0661: Class[] exceptionTypes = method.getExceptionTypes();
0662: for (int i = 0; i < exceptionTypes.length; i++) {
0663: Class exceptionType = exceptionTypes[i];
0664: if (!exceptionType.isInstance(exception)) {
0665: continue;
0666: }
0667:
0668: OutputStream out = (OutputStream) reply
0669: .createExceptionReply();
0670: String exceptionId = getExceptionId(exceptionType);
0671: out.write_string(exceptionId);
0672: out.write_value(exception);
0673: return out;
0674: }
0675: throw exception;
0676: }
0677:
0678: private static String getExceptionId(Class exceptionType) {
0679: String exceptionName = exceptionType.getName()
0680: .replace('.', '/');
0681: if (exceptionName.endsWith("Exception")) {
0682: exceptionName = exceptionName.substring(0, exceptionName
0683: .length()
0684: - "Exception".length());
0685: }
0686: exceptionName += "Ex";
0687: String exceptionId = "IDL:" + exceptionName + ":1.0";
0688: return exceptionId;
0689: }
0690:
0691: public static String[] createCorbaIds(Class type) {
0692: List ids = new LinkedList();
0693: for (Iterator iterator = getAllInterfaces(type).iterator(); iterator
0694: .hasNext();) {
0695: Class super Interface = (Class) iterator.next();
0696: if (Remote.class.isAssignableFrom(super Interface)
0697: && super Interface != Remote.class) {
0698: ids.add("RMI:" + super Interface.getName()
0699: + ":0000000000000000");
0700: }
0701: }
0702: return (String[]) ids.toArray(new String[ids.size()]);
0703: }
0704:
0705: private static Set getAllInterfaces(Class intfClass) {
0706: Set allInterfaces = new LinkedHashSet();
0707:
0708: LinkedList stack = new LinkedList();
0709: stack.addFirst(intfClass);
0710:
0711: while (!stack.isEmpty()) {
0712: Class intf = (Class) stack.removeFirst();
0713: allInterfaces.add(intf);
0714: stack.addAll(0, Arrays.asList(intf.getInterfaces()));
0715: }
0716:
0717: return allInterfaces;
0718: }
0719:
0720: public static Map mapMethodToOperation(Class intfClass) {
0721: return iiopMap(intfClass, false);
0722: }
0723:
0724: public static Map mapOperationToMethod(Class intfClass) {
0725: return iiopMap(intfClass, true);
0726: }
0727:
0728: private static Map iiopMap(Class intfClass,
0729: boolean operationToMethod) {
0730: Method[] methods = getAllMethods(intfClass);
0731:
0732: // find every valid getter
0733: HashMap getterByMethod = new HashMap(methods.length);
0734: HashMap getterByName = new HashMap(methods.length);
0735: for (int i = 0; i < methods.length; i++) {
0736: Method method = methods[i];
0737: String methodName = method.getName();
0738:
0739: // no arguments allowed
0740: if (method.getParameterTypes().length != 0) {
0741: continue;
0742: }
0743:
0744: // must start with get or is
0745: String verb;
0746: if (methodName.startsWith("get") && methodName.length() > 3
0747: && method.getReturnType() != void.class) {
0748: verb = "get";
0749: } else if (methodName.startsWith("is")
0750: && methodName.length() > 2
0751: && method.getReturnType() == boolean.class) {
0752: verb = "is";
0753: } else {
0754: continue;
0755: }
0756:
0757: // must only throw Remote or Runtime Exceptions
0758: boolean exceptionsValid = true;
0759: Class[] exceptionTypes = method.getExceptionTypes();
0760: for (int j = 0; j < exceptionTypes.length; j++) {
0761: Class exceptionType = exceptionTypes[j];
0762: if (!RemoteException.class
0763: .isAssignableFrom(exceptionType)
0764: && !RuntimeException.class
0765: .isAssignableFrom(exceptionType)
0766: && !Error.class.isAssignableFrom(exceptionType)) {
0767: exceptionsValid = false;
0768: break;
0769: }
0770: }
0771: if (!exceptionsValid) {
0772: continue;
0773: }
0774:
0775: String propertyName;
0776: if (methodName.length() > verb.length() + 1
0777: && Character.isUpperCase(methodName.charAt(verb
0778: .length() + 1))) {
0779: propertyName = methodName.substring(verb.length());
0780: } else {
0781: propertyName = Character.toLowerCase(methodName
0782: .charAt(verb.length()))
0783: + methodName.substring(verb.length() + 1);
0784: }
0785: getterByMethod.put(method, propertyName);
0786: getterByName.put(propertyName, method);
0787: }
0788:
0789: HashMap setterByMethod = new HashMap(methods.length);
0790: for (int i = 0; i < methods.length; i++) {
0791: Method method = methods[i];
0792: String methodName = method.getName();
0793:
0794: // must have exactally one arg
0795: if (method.getParameterTypes().length != 1) {
0796: continue;
0797: }
0798:
0799: // must return non void
0800: if (method.getReturnType() != void.class) {
0801: continue;
0802: }
0803:
0804: // must start with set
0805: if (!methodName.startsWith("set")
0806: || methodName.length() <= 3) {
0807: continue;
0808: }
0809:
0810: // must only throw Remote or Runtime Exceptions
0811: boolean exceptionsValid = true;
0812: Class[] exceptionTypes = method.getExceptionTypes();
0813: for (int j = 0; j < exceptionTypes.length; j++) {
0814: Class exceptionType = exceptionTypes[j];
0815: if (!RemoteException.class
0816: .isAssignableFrom(exceptionType)
0817: && !RuntimeException.class
0818: .isAssignableFrom(exceptionType)
0819: && !Error.class.isAssignableFrom(exceptionType)) {
0820: exceptionsValid = false;
0821: break;
0822: }
0823: }
0824: if (!exceptionsValid) {
0825: continue;
0826: }
0827:
0828: String propertyName;
0829: if (methodName.length() > 4
0830: && Character.isUpperCase(methodName.charAt(4))) {
0831: propertyName = methodName.substring(3);
0832: } else {
0833: propertyName = Character.toLowerCase(methodName
0834: .charAt(3))
0835: + methodName.substring(4);
0836: }
0837:
0838: // must have a matching getter
0839: Method getter = (Method) getterByName.get(propertyName);
0840: if (getter == null) {
0841: continue;
0842: }
0843:
0844: // setter property must match gettter return value
0845: if (!method.getParameterTypes()[0].equals(getter
0846: .getReturnType())) {
0847: continue;
0848: }
0849: setterByMethod.put(method, propertyName);
0850: }
0851:
0852: // index the methods by name... used to determine which methods are overloaded
0853: HashMap overloadedMethods = new HashMap(methods.length);
0854: for (int i = 0; i < methods.length; i++) {
0855: Method method = methods[i];
0856: if (getterByMethod.containsKey(method)
0857: || setterByMethod.containsKey(method)) {
0858: continue;
0859: }
0860: String methodName = method.getName();
0861: List methodList = (List) overloadedMethods.get(methodName);
0862: if (methodList == null) {
0863: methodList = new LinkedList();
0864: overloadedMethods.put(methodName, methodList);
0865: }
0866: methodList.add(method);
0867: }
0868:
0869: // index the methods by lower case name... used to determine which methods differ only by case
0870: HashMap caseCollisionMethods = new HashMap(methods.length);
0871: for (int i = 0; i < methods.length; i++) {
0872: Method method = methods[i];
0873: if (getterByMethod.containsKey(method)
0874: || setterByMethod.containsKey(method)) {
0875: continue;
0876: }
0877: String lowerCaseMethodName = method.getName().toLowerCase();
0878: Set methodSet = (Set) caseCollisionMethods
0879: .get(lowerCaseMethodName);
0880: if (methodSet == null) {
0881: methodSet = new HashSet();
0882: caseCollisionMethods
0883: .put(lowerCaseMethodName, methodSet);
0884: }
0885: methodSet.add(method.getName());
0886: }
0887:
0888: String className = getClassName(intfClass);
0889: Map iiopMap = new HashMap(methods.length);
0890: for (int i = 0; i < methods.length; i++) {
0891: Method method = methods[i];
0892:
0893: String iiopName = (String) getterByMethod.get(method);
0894: if (iiopName != null) {
0895: // if we have a leading underscore prepend with J
0896: if (iiopName.charAt(0) == '_') {
0897: iiopName = "J_get_" + iiopName.substring(1);
0898: } else {
0899: iiopName = "_get_" + iiopName;
0900: }
0901: } else {
0902: iiopName = (String) setterByMethod.get(method);
0903: if (iiopName != null) {
0904: // if we have a leading underscore prepend with J
0905: if (iiopName.charAt(0) == '_') {
0906: iiopName = "J_set_" + iiopName.substring(1);
0907: } else {
0908: iiopName = "_set_" + iiopName;
0909: }
0910: } else {
0911: iiopName = method.getName();
0912:
0913: // if we have a leading underscore prepend with J
0914: if (iiopName.charAt(0) == '_') {
0915: iiopName = "J" + iiopName;
0916: }
0917: }
0918: }
0919:
0920: // if this name only differs by case add the case index to the end
0921: Set caseCollisions = (Set) caseCollisionMethods.get(method
0922: .getName().toLowerCase());
0923: if (caseCollisions != null && caseCollisions.size() > 1) {
0924: iiopName += upperCaseIndexString(iiopName);
0925: }
0926:
0927: // if this is an overloaded method append the parameter string
0928: List overloads = (List) overloadedMethods.get(method
0929: .getName());
0930: if (overloads != null && overloads.size() > 1) {
0931: iiopName += buildOverloadParameterString(method
0932: .getParameterTypes());
0933: }
0934:
0935: // if we have a leading underscore prepend with J
0936: iiopName = replace(iiopName, '$', "U0024");
0937:
0938: // if we have matched a keyword prepend with an underscore
0939: if (keywords.contains(iiopName.toLowerCase())) {
0940: iiopName = "_" + iiopName;
0941: }
0942:
0943: // if the name is the same as the class name, append an underscore
0944: if (iiopName.equalsIgnoreCase(className)) {
0945: iiopName += "_";
0946: }
0947:
0948: if (operationToMethod) {
0949: iiopMap.put(iiopName, method);
0950: } else {
0951: iiopMap.put(method, iiopName);
0952: }
0953: }
0954:
0955: return iiopMap;
0956: }
0957:
0958: private static Method[] getAllMethods(Class intfClass) {
0959: LinkedList methods = new LinkedList();
0960: for (Iterator iterator = getAllInterfaces(intfClass).iterator(); iterator
0961: .hasNext();) {
0962: Class intf = (Class) iterator.next();
0963: methods.addAll(Arrays.asList(intf.getDeclaredMethods()));
0964: }
0965:
0966: return (Method[]) methods.toArray(new Method[methods.size()]);
0967: }
0968:
0969: /**
0970: * Return the a string containing an underscore '_' index of each uppercase character in the iiop name.
0971: *
0972: * This is used for distinction of names that only differ by case, since corba does not support case sensitive names.
0973: */
0974: private static String upperCaseIndexString(String iiopName) {
0975: StringBuffer stringBuffer = new StringBuffer();
0976: for (int i = 0; i < iiopName.length(); i++) {
0977: char c = iiopName.charAt(i);
0978: if (Character.isUpperCase(c)) {
0979: stringBuffer.append('_').append(i);
0980: }
0981: }
0982: return stringBuffer.toString();
0983: }
0984:
0985: /**
0986: * Replaces any occurnace of the specified "oldChar" with the nes string.
0987: *
0988: * This is used to replace occurances if '$' in corba names since '$' is a special character
0989: */
0990: private static String replace(String source, char oldChar,
0991: String newString) {
0992: StringBuffer stringBuffer = new StringBuffer(source.length());
0993: for (int i = 0; i < source.length(); i++) {
0994: char c = source.charAt(i);
0995: if (c == oldChar) {
0996: stringBuffer.append(newString);
0997: } else {
0998: stringBuffer.append(c);
0999: }
1000: }
1001: return stringBuffer.toString();
1002: }
1003:
1004: /**
1005: * Return the a string containing a double underscore '__' list of parameter types encoded using the Java to IDL rules.
1006: *
1007: * This is used for distinction of methods that only differ by parameter lists.
1008: */
1009: private static String buildOverloadParameterString(
1010: Class[] parameterTypes) {
1011: String name = "";
1012: if (parameterTypes.length == 0) {
1013: name += "__";
1014: } else {
1015: for (int i = 0; i < parameterTypes.length; i++) {
1016: Class parameterType = parameterTypes[i];
1017: name += buildOverloadParameterString(parameterType);
1018: }
1019: }
1020: return name.replace('.', '_');
1021: }
1022:
1023: /**
1024: * Returns a single parameter type encoded using the Java to IDL rules.
1025: */
1026: private static String buildOverloadParameterString(
1027: Class parameterType) {
1028: String name = "_";
1029:
1030: int arrayDimensions = 0;
1031: while (parameterType.isArray()) {
1032: arrayDimensions++;
1033: parameterType = parameterType.getComponentType();
1034: }
1035:
1036: // arrays start with org_omg_boxedRMI_
1037: if (arrayDimensions > 0) {
1038: name += "_org_omg_boxedRMI";
1039: }
1040:
1041: // IDLEntity types must be prefixed with org_omg_boxedIDL_
1042: if (IDLEntity.class.isAssignableFrom(parameterType)) {
1043: name += "_org_omg_boxedIDL";
1044: }
1045:
1046: // add package... some types have special mappings in corba
1047: String packageName = (String) specialTypePackages
1048: .get(parameterType.getName());
1049: if (packageName == null) {
1050: packageName = getPackageName(parameterType.getName());
1051: }
1052: if (packageName.length() > 0) {
1053: name += "_" + packageName;
1054: }
1055:
1056: // arrays now contain a dimension indicator
1057: if (arrayDimensions > 0) {
1058: name += "_" + "seq" + arrayDimensions;
1059: }
1060:
1061: // add the class name
1062: String className = (String) specialTypeNames.get(parameterType
1063: .getName());
1064: if (className == null) {
1065: className = buildClassName(parameterType);
1066: }
1067: name += "_" + className;
1068:
1069: return name;
1070: }
1071:
1072: /**
1073: * Returns a string contianing an encoded class name.
1074: */
1075: private static String buildClassName(Class type) {
1076: if (type.isArray()) {
1077: throw new IllegalArgumentException("type is an array: "
1078: + type);
1079: }
1080:
1081: // get the classname
1082: String typeName = type.getName();
1083: int endIndex = typeName.lastIndexOf('.');
1084: if (endIndex < 0) {
1085: return typeName;
1086: }
1087: StringBuffer className = new StringBuffer(typeName
1088: .substring(endIndex + 1));
1089:
1090: // for innerclasses replace the $ separator with two underscores
1091: // we can't just blindly replace all $ characters since class names can contain the $ character
1092: if (type.getDeclaringClass() != null) {
1093: String declaringClassName = getClassName(type
1094: .getDeclaringClass());
1095: assert className.toString().startsWith(
1096: declaringClassName + "$");
1097: className.replace(declaringClassName.length(),
1098: declaringClassName.length() + 1, "__");
1099: }
1100:
1101: // if we have a leading underscore prepend with J
1102: if (className.charAt(0) == '_') {
1103: className.insert(0, "J");
1104: }
1105: return className.toString();
1106: }
1107:
1108: private static String getClassName(Class type) {
1109: if (type.isArray()) {
1110: throw new IllegalArgumentException("type is an array: "
1111: + type);
1112: }
1113:
1114: // get the classname
1115: String typeName = type.getName();
1116: int endIndex = typeName.lastIndexOf('.');
1117: if (endIndex < 0) {
1118: return typeName;
1119: }
1120: return typeName.substring(endIndex + 1);
1121: }
1122:
1123: private static String getPackageName(String interfaceName) {
1124: int endIndex = interfaceName.lastIndexOf('.');
1125: if (endIndex < 0) {
1126: return "";
1127: }
1128: return interfaceName.substring(0, endIndex);
1129: }
1130:
1131: private static final Map specialTypeNames;
1132: private static final Map specialTypePackages;
1133: private static final Set keywords;
1134:
1135: static {
1136: specialTypeNames = new HashMap();
1137: specialTypeNames.put("boolean", "boolean");
1138: specialTypeNames.put("char", "wchar");
1139: specialTypeNames.put("byte", "octet");
1140: specialTypeNames.put("short", "short");
1141: specialTypeNames.put("int", "long");
1142: specialTypeNames.put("long", "long_long");
1143: specialTypeNames.put("float", "float");
1144: specialTypeNames.put("double", "double");
1145: specialTypeNames.put("java.lang.Class", "ClassDesc");
1146: specialTypeNames.put("java.lang.String", "WStringValue");
1147: specialTypeNames.put("org.omg.CORBA.Object", "Object");
1148:
1149: specialTypePackages = new HashMap();
1150: specialTypePackages.put("boolean", "");
1151: specialTypePackages.put("char", "");
1152: specialTypePackages.put("byte", "");
1153: specialTypePackages.put("short", "");
1154: specialTypePackages.put("int", "");
1155: specialTypePackages.put("long", "");
1156: specialTypePackages.put("float", "");
1157: specialTypePackages.put("double", "");
1158: specialTypePackages.put("java.lang.Class", "javax.rmi.CORBA");
1159: specialTypePackages.put("java.lang.String", "CORBA");
1160: specialTypePackages.put("org.omg.CORBA.Object", "");
1161:
1162: keywords = new HashSet();
1163: keywords.add("abstract");
1164: keywords.add("any");
1165: keywords.add("attribute");
1166: keywords.add("boolean");
1167: keywords.add("case");
1168: keywords.add("char");
1169: keywords.add("const");
1170: keywords.add("context");
1171: keywords.add("custom");
1172: keywords.add("default");
1173: keywords.add("double");
1174: keywords.add("enum");
1175: keywords.add("exception");
1176: keywords.add("factory");
1177: keywords.add("false");
1178: keywords.add("fixed");
1179: keywords.add("float");
1180: keywords.add("in");
1181: keywords.add("inout");
1182: keywords.add("interface");
1183: keywords.add("long");
1184: keywords.add("module");
1185: keywords.add("native");
1186: keywords.add("object");
1187: keywords.add("octet");
1188: keywords.add("oneway");
1189: keywords.add("out");
1190: keywords.add("private");
1191: keywords.add("public");
1192: keywords.add("raises");
1193: keywords.add("readonly");
1194: keywords.add("sequence");
1195: keywords.add("short");
1196: keywords.add("string");
1197: keywords.add("struct");
1198: keywords.add("supports");
1199: keywords.add("switch");
1200: keywords.add("true");
1201: keywords.add("truncatable");
1202: keywords.add("typedef");
1203: keywords.add("union");
1204: keywords.add("unsigned");
1205: keywords.add("valuebase");
1206: keywords.add("valuetype");
1207: keywords.add("void");
1208: keywords.add("wchar");
1209: keywords.add("wstring");
1210: }
1211:
1212: }
|