001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.corba.util; 018 019 import java.io.ByteArrayInputStream; 020 import java.io.ByteArrayOutputStream; 021 import java.io.IOException; 022 import java.io.Serializable; 023 import java.io.UnsupportedEncodingException; 024 import java.io.ObjectOutputStream; 025 import java.rmi.Remote; 026 import java.rmi.UnexpectedException; 027 import java.rmi.RemoteException; 028 import java.lang.reflect.Method; 029 import java.util.List; 030 import java.util.LinkedList; 031 import java.util.Iterator; 032 import java.util.Map; 033 import java.util.Set; 034 import java.util.Arrays; 035 import java.util.LinkedHashSet; 036 import java.util.HashMap; 037 import java.util.HashSet; 038 import java.util.regex.Pattern; 039 import java.util.regex.Matcher; 040 041 import javax.ejb.spi.HandleDelegate; 042 import javax.naming.InitialContext; 043 import javax.naming.NamingException; 044 import javax.rmi.PortableRemoteObject; 045 046 import org.apache.geronimo.corba.ORBConfiguration; 047 import org.apache.geronimo.crypto.asn1.DERInputStream; 048 import org.apache.geronimo.crypto.asn1.DERObjectIdentifier; 049 import org.apache.geronimo.crypto.asn1.DEROutputStream; 050 import org.apache.geronimo.crypto.asn1.x509.GeneralName; 051 import org.apache.geronimo.crypto.asn1.x509.X509Name; 052 import org.omg.CORBA.Any; 053 import org.omg.CORBA.ORB; 054 import org.omg.CORBA.UserException; 055 import org.omg.CORBA.portable.ResponseHandler; 056 import org.omg.CORBA.portable.UnknownException; 057 import org.omg.CORBA.portable.IDLEntity; 058 import org.omg.GSSUP.GSSUPMechOID; 059 import org.omg.GSSUP.InitialContextToken; 060 import org.omg.GSSUP.InitialContextTokenHelper; 061 import org.omg.IOP.Codec; 062 import org.omg.IOP.CodecFactory; 063 import org.omg.IOP.ENCODING_CDR_ENCAPS; 064 import org.omg.IOP.Encoding; 065 import org.omg.CORBA_2_3.portable.OutputStream; 066 import org.omg.CORBA_2_3.portable.InputStream; 067 import org.apache.commons.logging.Log; 068 import org.apache.commons.logging.LogFactory; 069 import org.apache.geronimo.corba.CorbaApplicationServer; 070 import org.apache.openejb.ProxyInfo; 071 import org.apache.openejb.spi.ApplicationServer; 072 import org.apache.openejb.core.ServerFederation; 073 074 /** 075 * Various utility functions. 076 * <p/> 077 * Note: #getORB() and #getCodec() rely on UtilInitializer to initialze the ORB and codec. 078 * 079 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 080 * @see UtilInitializer 081 */ 082 public final class Util { 083 private static final Log log = LogFactory.getLog(Util.class); 084 private static final byte ASN_TAG_NT_EXPORTED_NAME1 = 0x04; 085 private static final byte ASN_TAG_NT_EXPORTED_NAME2 = 0x01; 086 private static final byte ASN_TAG_OID = 0x06; 087 private static final byte ASN_TAG_GSS = 0x60; 088 private static ORB orb; 089 private static Codec codec; 090 private static HandleDelegate handleDelegate; 091 private static CorbaApplicationServer corbaApplicationServer = new CorbaApplicationServer(); 092 private static HashMap<String,ORBConfiguration> configuredOrbs = new HashMap<String,ORBConfiguration>(); 093 094 public static ORB getORB() { 095 assert orb != null; 096 return orb; 097 } 098 099 public static void registerORB(String id, ORBConfiguration orb) { 100 configuredOrbs.put(id, orb); 101 } 102 103 public static ORBConfiguration getRegisteredORB(String id) { 104 return configuredOrbs.get(id); 105 } 106 107 public static void unregisterORB(String id) { 108 configuredOrbs.remove(id); 109 } 110 111 112 public static void setORB(ORB orb) throws UserException { 113 if (Util.orb == null) { 114 Util.orb = orb; 115 CodecFactory factory = (CodecFactory) Util.orb.resolve_initial_references("CodecFactory"); 116 codec = factory.create_codec(new Encoding(ENCODING_CDR_ENCAPS.value, (byte) 1, (byte) 2)); 117 } 118 } 119 120 public static Codec getCodec() { 121 assert codec != null; 122 return codec; 123 } 124 125 public static HandleDelegate getHandleDelegate() throws NamingException { 126 if (handleDelegate == null) { 127 InitialContext ic = new InitialContext(); 128 handleDelegate = (HandleDelegate) ic.lookup("java:comp/HandleDelegate"); 129 } 130 return handleDelegate; 131 } 132 133 public static Object getEJBProxy(ProxyInfo info) { 134 if (info.getInterfaceType().isHome()) { 135 return corbaApplicationServer.getEJBHome(info); 136 } 137 else { 138 return corbaApplicationServer.getEJBObject(info); 139 } 140 } 141 142 public static byte[] encodeOID(String oid) throws IOException { 143 oid = (oid.startsWith("oid:") ? oid.substring(4) : oid); 144 return encodeOID(new DERObjectIdentifier(oid)); 145 } 146 147 public static byte[] encodeOID(DERObjectIdentifier oid) throws IOException { 148 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 149 DEROutputStream dOut = new DEROutputStream(bOut); 150 151 dOut.writeObject(oid); 152 153 return bOut.toByteArray(); 154 } 155 156 public static String decodeOID(byte[] oid) throws IOException { 157 return decodeOIDDERObjectIdentifier(oid).getId(); 158 } 159 160 public static DERObjectIdentifier decodeOIDDERObjectIdentifier(byte[] oid) throws IOException { 161 ByteArrayInputStream bIn = new ByteArrayInputStream(oid); 162 DERInputStream dIn = new DERInputStream(bIn); 163 164 return (DERObjectIdentifier) dIn.readObject(); 165 } 166 167 public static byte[] encodeGeneralName(String name) throws IOException { 168 return encodeGeneralName(new X509Name(name)); 169 } 170 171 public static byte[] encodeGeneralName(X509Name x509Name) throws IOException { 172 return encodeGeneralName(new GeneralName(x509Name)); 173 } 174 175 public static byte[] encodeGeneralName(GeneralName generalName) throws IOException { 176 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 177 DEROutputStream dOut = new DEROutputStream(bOut); 178 179 dOut.writeObject(generalName); 180 181 return bOut.toByteArray(); 182 } 183 184 public static String decodeGeneralName(byte[] name) throws IOException { 185 throw new java.lang.UnsupportedOperationException(); 186 } 187 188 /** 189 * This method encodes a name as if it was encoded using the GSS-API 190 * gss_export_name() function call (see RFC 2743, page 84). 191 * The oid to indicate names of this format is:<br/> 192 * {1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes), 193 * 4(gss-api-exported-name)}<br/> 194 * The token has the following format: 195 * <table> 196 * <tr><td><b>Offset</b></td><td><b>Meaning</b></td><td><b>Value</b></td></tr> 197 * <tr><td>0</td><td>token id</td><td>0x04</td></tr> 198 * <tr><td>1</td><td>token id</td><td>0x01</td></tr> 199 * <p/> 200 * <tr><td>2</td><td>oid length</td><td>hi-byte (len/0xFF)</td></tr> 201 * <tr><td>3</td><td>oid length</td><td>lo-byte (len%0xFF)</td></tr> 202 * <p/> 203 * <tr><td>4</td><td>oid</td><td>oid:1.3.6.1.5.6.4</td></tr> 204 * <p/> 205 * <tr><td>n+0</td><td>name length</td><td>len/0xFFFFFF</td></tr> 206 * <tr><td>n+1</td><td>name length</td><td>(len%0xFFFFFF)/0xFFFF</td></tr> 207 * <tr><td>n+2</td><td>name length</td><td>((len%0xFFFFFF)%0xFFFF)/0xFF</td></tr> 208 * <tr><td>n+3</td><td>name length</td><td>((len%0xFFFFFF)%0xFFFF)%0xFF</td></tr> 209 * <p/> 210 * <tr><td>n+4</td><td>name</td><td>foo</td></tr> 211 * </table> 212 * 213 * @param oid The oid of the mechanism this name is exported from. 214 * @param name The name to be exported. 215 * @return The byte array representing the exported name object. 216 */ 217 public static byte[] encodeGSSExportName(String oid, String name) { 218 try { 219 byte[] oid_arr = encodeOID(oid); 220 int oid_len = oid_arr.length; 221 byte[] name_arr = name.getBytes("UTF-8"); 222 int name_len = name_arr.length; 223 224 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 225 // token id at 0 226 baos.write(ASN_TAG_NT_EXPORTED_NAME1); 227 baos.write(ASN_TAG_NT_EXPORTED_NAME2); 228 229 // write the two length bytes 230 baos.write((byte) (oid_len & 0xFF00) >> 8); 231 baos.write((byte) (oid_len & 0x00FF)); 232 233 // oid at 2 234 baos.write(oid_arr); 235 236 // name length at n 237 baos.write((byte) (name_len & 0xFF000000) >> 24); 238 baos.write((byte) (name_len & 0x00FF0000) >> 16); 239 baos.write((byte) (name_len & 0x0000FF00) >> 8); 240 baos.write((byte) (name_len & 0x000000FF)); 241 242 // name at n+4 243 baos.write(name_arr); 244 return baos.toByteArray(); 245 } catch (Exception ex) { 246 // do nothing, return null 247 } 248 return null; 249 } 250 251 /** 252 * This function reads a name from a byte array which was created 253 * by the gssExportName() method. 254 * 255 * @param name_tok The GSS name token. 256 * @return The name from the GSS name token. 257 */ 258 public static String decodeGSSExportName(byte[] name_tok) { 259 String result = null; 260 if (name_tok != null) { 261 ByteArrayInputStream bais = new ByteArrayInputStream(name_tok); 262 try { 263 // GSSToken tag 1 0x04 264 int t1 = bais.read(); 265 if (t1 == ASN_TAG_NT_EXPORTED_NAME1) { 266 // GSSToken tag 2 0x01 267 int t2 = bais.read(); 268 if (t2 == ASN_TAG_NT_EXPORTED_NAME2) { 269 // read the two length bytes 270 int l = bais.read() << 8; 271 l += bais.read(); 272 273 // read the oid 274 byte[] oid_arr = new byte[l]; 275 bais.read(oid_arr, 0, l); 276 String oid = decodeOID(oid_arr); 277 278 if (oid.equals(GSSUPMechOID.value.substring(4))) { 279 int l1 = bais.read(); 280 int l2 = bais.read(); 281 int l3 = bais.read(); 282 int l4 = bais.read(); 283 284 int name_len = (l1 << 24) + (l2 << 16) + (l3 << 8) + l4; 285 byte[] name_arr = new byte[name_len]; 286 bais.read(name_arr, 0, name_len); 287 result = new String(name_arr); 288 } else { 289 System.err.print("ASN1Utils.gssImportName: Unknown OID: " + oid + 290 " ('" + Integer.toHexString(oid_arr[0]) + "')"); 291 } 292 } 293 } 294 } catch (Exception ex) { 295 ex.printStackTrace(); 296 // do nothing, return null 297 } 298 } 299 return result; 300 } 301 302 private static final Pattern SCOPED_NAME_EXTRACTION_PATTERN = Pattern.compile("(\\\\\\\\)|(\\\\@)|(@)|(\\z)"); 303 304 /** 305 * See csiv2 spec 16.2.5 par. 63-64. We extract the username if any and un-escape any 306 * escaped \ and @ characters. 307 * 308 * @param scopedNameBytes 309 * @return 310 * @throws UnsupportedEncodingException 311 */ 312 public static String extractUserNameFromScopedName(byte[] scopedNameBytes) throws UnsupportedEncodingException { 313 String scopedUserName = new String(scopedNameBytes, "UTF8"); 314 return extractUserNameFromScopedName(scopedUserName); 315 } 316 317 public static String extractUserNameFromScopedName(String scopedUserName) { 318 Matcher m = SCOPED_NAME_EXTRACTION_PATTERN.matcher(scopedUserName); 319 StringBuffer buf = new StringBuffer(); 320 while (m.find()) { 321 m.appendReplacement(buf, ""); 322 if (m.group(1) != null) { 323 buf.append('\\'); 324 } else if (m.group(2) != null) { 325 buf.append("@"); 326 } else if (m.group(3) != null) { 327 break; 328 } 329 } 330 return buf.toString(); 331 } 332 333 private static final Pattern SCOPED_NAME_ESCAPE_PATTERN = Pattern.compile("(\\\\)|(@)"); 334 335 public static String buildScopedUserName(String user, String domain) { 336 StringBuffer buf = new StringBuffer(); 337 if (user != null) { 338 escape(user, buf); 339 } 340 if (domain != null) { 341 buf.append('@'); 342 escape(domain, buf); 343 } 344 return buf.toString(); 345 } 346 347 private static void escape(String s, StringBuffer buf) { 348 Matcher m = SCOPED_NAME_ESCAPE_PATTERN.matcher(s); 349 while (m.find()) { 350 m.appendReplacement(buf, ""); 351 if (m.group(1) != null) { 352 buf.append("\\\\"); 353 } else if (m.group(2) != null) { 354 buf.append("\\@"); 355 } 356 } 357 m.appendTail(buf); 358 } 359 360 361 /** 362 * Encode a mechanism independent initial context token (GSSToken). Defined 363 * in [IETF RFC 2743] Section 3.1, "Mechanism-Independent token Format" pp. 81-82. 364 * <table> 365 * <tr><td><b>Offset</b></td><td><b>Meaning</b></td></tr> 366 * <tr><td>0</td><td>ASN1 tag</td></tr> 367 * <tr><td>1</td><td>token length (<128)</td></tr> 368 * <tr><td>2</td><td>mechanism oid</td></tr> 369 * <tr><td>n</td><td>mechanism specific token (e.g. GSSUP::InitialContextToken)</td></tr> 370 * </table> 371 * Currently only one mechanism specific token is supported: GSS username password 372 * (GSSUP::InitialContextToken). 373 * 374 * @param orb The orb to get an Any from. 375 * @param codec The codec to do the encoding of the Any. 376 * @param user The username. 377 * @param pwd The password of the user. 378 * @param target The target name. 379 * @return The byte array of the ASN1 encoded GSSToken. 380 */ 381 public static byte[] encodeGSSUPToken(ORB orb, Codec codec, String user, String pwd, String target) { 382 byte[] result = null; 383 try { 384 // write the GSS ASN tag 385 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 386 baos.write(ASN_TAG_GSS); 387 388 // create and encode a GSSUP initial context token 389 InitialContextToken init_token = new InitialContextToken(); 390 init_token.username = user.getBytes("UTF-8"); 391 392 init_token.password = pwd.getBytes("UTF-8"); 393 394 init_token.target_name = encodeGSSExportName(GSSUPMechOID.value.substring(4), target); 395 396 Any a = orb.create_any(); 397 InitialContextTokenHelper.insert(a, init_token); 398 byte[] init_ctx_token = codec.encode_value(a); 399 400 // encode the mechanism oid 401 byte[] oid_arr = encodeOID(GSSUPMechOID.value.substring(4)); 402 403 // write the length 404 baos.write((byte) (oid_arr.length + init_ctx_token.length + 2)); 405 406 // write the mechanism oid 407 baos.write(oid_arr); 408 409 // write the 410 baos.write(init_ctx_token); 411 412 // get the bytes 413 result = baos.toByteArray(); 414 } catch (Exception ex) { 415 // do nothing, return null 416 } 417 return result; 418 } 419 420 /** 421 * Decode an GSSUP InitialContextToken from a GSSToken. 422 * 423 * @param codec The codec to do the encoding of the Any. 424 * @param gssup_tok The InitialContextToken struct to fill in the decoded values. 425 * @return Return true when decoding was successful, false otherwise. 426 */ 427 public static boolean decodeGSSUPToken(Codec codec, byte[] token_arr, 428 InitialContextToken gssup_tok) { 429 boolean result = false; 430 if (gssup_tok != null) { 431 ByteArrayInputStream bais = new ByteArrayInputStream(token_arr); 432 try { 433 // GSSToken tag 434 int c = bais.read(); 435 if (c == ASN_TAG_GSS) { 436 // GSSToken length 437 int token_len = bais.read(); 438 // OID tag 439 int oid_tag = bais.read(); 440 if (oid_tag == ASN_TAG_OID) { 441 // OID length 442 int oid_len = bais.read(); 443 byte[] oid_tmp_arr = new byte[oid_len]; 444 bais.read(oid_tmp_arr, 0, oid_len); 445 byte[] oid_arr = new byte[oid_len + 2]; 446 oid_arr[0] = (byte) oid_tag; 447 oid_arr[1] = (byte) oid_len; 448 System.arraycopy(oid_tmp_arr, 0, oid_arr, 2, oid_len); 449 String oid = decodeOID(oid_arr); 450 if (oid.equals(GSSUPMechOID.value.substring(4))) { 451 int len = token_len - oid_len; 452 byte[] init_tok_arr = new byte[len]; 453 bais.read(init_tok_arr, 0, len); 454 Any a = codec.decode_value(init_tok_arr, 455 InitialContextTokenHelper.type()); 456 InitialContextToken token = InitialContextTokenHelper.extract(a); 457 if (token != null) { 458 gssup_tok.username = token.username; 459 gssup_tok.password = token.password; 460 gssup_tok.target_name = decodeGSSExportName(token.target_name).getBytes("UTF-8"); 461 462 result = true; 463 } 464 } 465 } 466 } 467 } catch (Exception ex) { 468 // do nothing, return false 469 } 470 } 471 return result; 472 } 473 474 public static String byteToString(byte[] data) { 475 StringBuffer buffer = new StringBuffer(); 476 for (int i = 0; i < data.length; i++) { 477 buffer.append(HEXCHAR[(data[i] >>> 4) & 0x0F]); 478 buffer.append(HEXCHAR[(data[i]) & 0x0F]); 479 } 480 return buffer.toString(); 481 482 } 483 484 private static final char[] HEXCHAR = { 485 '0', '1', '2', '3', '4', '5', '6', '7', 486 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 487 }; 488 489 public static void writeObject(Class type, Object object, OutputStream out) { 490 if (type == Void.TYPE) { 491 // do nothing for a void 492 } else if (type == Boolean.TYPE) { 493 out.write_boolean(((Boolean) object).booleanValue()); 494 } else if (type == Byte.TYPE) { 495 out.write_octet(((Byte) object).byteValue()); 496 } else if (type == Character.TYPE) { 497 out.write_wchar(((Character) object).charValue()); 498 } else if (type == Double.TYPE) { 499 out.write_double(((Double) object).doubleValue()); 500 } else if (type == Float.TYPE) { 501 out.write_float(((Float) object).floatValue()); 502 } else if (type == Integer.TYPE) { 503 out.write_long(((Integer) object).intValue()); 504 } else if (type == Long.TYPE) { 505 out.write_longlong(((Long) object).longValue()); 506 } else if (type == Short.TYPE) { 507 out.write_short(((Short) object).shortValue()); 508 } else { 509 // object types must bbe written in the context of the corba application server 510 // which properly write replaces our objects for corba 511 ApplicationServer oldApplicationServer = ServerFederation.getApplicationServer(); 512 try { 513 ServerFederation.setApplicationServer(corbaApplicationServer); 514 515 // todo check if 516 // copy the result to force replacement 517 // corba does not call writeReplace on remote proxies 518 // 519 // HOWEVER, if this is an array, then we don't want to do the replacement 520 // because we can end up with a replacement element that's not compatible with the 521 // original array type, which results in an ArrayStoreException. Fortunately, 522 // the Yoko RMI support appears to be able to sort this out for us correctly. 523 if (object instanceof Serializable && !object.getClass().isArray()) { 524 try { 525 object = copyObj(Thread.currentThread().getContextClassLoader(), object); 526 } catch (Exception e) { 527 log.debug("Exception in result copy", e); 528 throw new UnknownException(e); 529 } 530 } 531 532 if (type == Object.class || type == Serializable.class) { 533 javax.rmi.CORBA.Util.writeAny(out, object); 534 } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)) { 535 out.write_Object((org.omg.CORBA.Object) object); 536 } else if (Remote.class.isAssignableFrom(type)) { 537 javax.rmi.CORBA.Util.writeRemoteObject(out, object); 538 } else if (type.isInterface() && Serializable.class.isAssignableFrom(type)) { 539 javax.rmi.CORBA.Util.writeAbstractObject(out, object); 540 } else { 541 out.write_value((Serializable) object, type); 542 } 543 } finally { 544 ServerFederation.setApplicationServer(oldApplicationServer); 545 } 546 } 547 } 548 549 private static Object copyObj(ClassLoader classLoader, Object object) throws IOException, ClassNotFoundException { 550 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 551 ObjectOutputStream oos = new ObjectOutputStream(baos); 552 oos.writeObject(object); 553 oos.flush(); 554 oos.close(); 555 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 556 ObjectInputStreamExt ois = new ObjectInputStreamExt(bais, classLoader); 557 return ois.readObject(); 558 } 559 560 public static Object readObject(Class type, InputStream in) { 561 if (type == Void.TYPE) { 562 return null; 563 } else if (type == Boolean.TYPE) { 564 return new Boolean(in.read_boolean()); 565 } else if (type == Byte.TYPE) { 566 return new Byte(in.read_octet()); 567 } else if (type == Character.TYPE) { 568 return new Character(in.read_wchar()); 569 } else if (type == Double.TYPE) { 570 return new Double(in.read_double()); 571 } else if (type == Float.TYPE) { 572 return new Float(in.read_float()); 573 } else if (type == Integer.TYPE) { 574 return new Integer(in.read_long()); 575 } else if (type == Long.TYPE) { 576 return new Long(in.read_longlong()); 577 } else if (type == Short.TYPE) { 578 return new Short(in.read_short()); 579 } else if (type == Object.class || type == Serializable.class) { 580 return javax.rmi.CORBA.Util.readAny(in); 581 } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)) { 582 return in.read_Object(type); 583 } else if (Remote.class.isAssignableFrom(type)) { 584 return PortableRemoteObject.narrow(in.read_Object(), type); 585 } else if (type.isInterface() && Serializable.class.isAssignableFrom(type)) { 586 return in.read_abstract_interface(); 587 } else { 588 return in.read_value(type); 589 } 590 } 591 592 public static void throwException(Method method, InputStream in) throws Throwable { 593 // read the exception id 594 final String id = in.read_string(); 595 596 // get the class name from the id 597 if (!id.startsWith("IDL:")) { 598 log.warn("Malformed exception id: " + id); 599 return; 600 } 601 602 Class[] exceptionTypes = method.getExceptionTypes(); 603 for (int i = 0; i < exceptionTypes.length; i++) { 604 Class exceptionType = exceptionTypes[i]; 605 606 String exceptionId = getExceptionId(exceptionType); 607 if (id.equals(exceptionId)) { 608 throw (Throwable) in.read_value(exceptionType); 609 } 610 } 611 throw new UnexpectedException(id); 612 } 613 614 public static OutputStream writeUserException(Method method, ResponseHandler reply, Exception exception) throws Exception { 615 if (exception instanceof RuntimeException || exception instanceof RemoteException) { 616 throw exception; 617 } 618 619 Class[] exceptionTypes = method.getExceptionTypes(); 620 for (int i = 0; i < exceptionTypes.length; i++) { 621 Class exceptionType = exceptionTypes[i]; 622 if (!exceptionType.isInstance(exception)) { 623 continue; 624 } 625 626 OutputStream out = (OutputStream) reply.createExceptionReply(); 627 String exceptionId = getExceptionId(exceptionType); 628 out.write_string(exceptionId); 629 out.write_value(exception); 630 return out; 631 } 632 throw exception; 633 } 634 635 private static String getExceptionId(Class exceptionType) { 636 String exceptionName = exceptionType.getName().replace('.', '/'); 637 if (exceptionName.endsWith("Exception")) { 638 exceptionName = exceptionName.substring(0, exceptionName.length() - "Exception".length()); 639 } 640 exceptionName += "Ex"; 641 String exceptionId = "IDL:" + exceptionName + ":1.0"; 642 return exceptionId; 643 } 644 645 public static String[] createCorbaIds(Class type) { 646 List ids = new LinkedList(); 647 for (Iterator iterator = getAllInterfaces(type).iterator(); iterator.hasNext();) { 648 Class superInterface = (Class) iterator.next(); 649 if (Remote.class.isAssignableFrom(superInterface) && superInterface != Remote.class) { 650 ids.add("RMI:" + superInterface.getName() + ":0000000000000000"); 651 } 652 } 653 return (String[]) ids.toArray(new String[ids.size()]); 654 } 655 656 private static Set getAllInterfaces(Class intfClass) { 657 Set allInterfaces = new LinkedHashSet(); 658 659 LinkedList stack = new LinkedList(); 660 stack.addFirst(intfClass); 661 662 while (!stack.isEmpty()) { 663 Class intf = (Class) stack.removeFirst(); 664 allInterfaces.add(intf); 665 stack.addAll(0, Arrays.asList(intf.getInterfaces())); 666 } 667 668 return allInterfaces; 669 } 670 671 public static Map mapMethodToOperation(Class intfClass) { 672 return iiopMap(intfClass, false); 673 } 674 675 public static Map mapOperationToMethod(Class intfClass) { 676 return iiopMap(intfClass, true); 677 } 678 679 private static Map iiopMap(Class intfClass, boolean operationToMethod) { 680 Method[] methods = getAllMethods(intfClass); 681 682 // find every valid getter 683 HashMap getterByMethod = new HashMap(methods.length); 684 HashMap getterByName = new HashMap(methods.length); 685 for (int i = 0; i < methods.length; i++) { 686 Method method = methods[i]; 687 String methodName = method.getName(); 688 689 // no arguments allowed 690 if (method.getParameterTypes().length != 0) { 691 continue; 692 } 693 694 // must start with get or is 695 String verb; 696 if (methodName.startsWith("get") && methodName.length() > 3 && method.getReturnType() != void.class) { 697 verb = "get"; 698 } else if (methodName.startsWith("is") && methodName.length() > 2 && method.getReturnType() == boolean.class) { 699 verb = "is"; 700 } else { 701 continue; 702 } 703 704 // must only throw Remote or Runtime Exceptions 705 boolean exceptionsValid = true; 706 Class[] exceptionTypes = method.getExceptionTypes(); 707 for (int j = 0; j < exceptionTypes.length; j++) { 708 Class exceptionType = exceptionTypes[j]; 709 if (!RemoteException.class.isAssignableFrom(exceptionType) && 710 !RuntimeException.class.isAssignableFrom(exceptionType) && 711 !Error.class.isAssignableFrom(exceptionType)) { 712 exceptionsValid = false; 713 break; 714 } 715 } 716 if (!exceptionsValid) { 717 continue; 718 } 719 720 String propertyName; 721 if (methodName.length() > verb.length() + 1 && Character.isUpperCase(methodName.charAt(verb.length() + 1))) { 722 propertyName = methodName.substring(verb.length()); 723 } else { 724 propertyName = Character.toLowerCase(methodName.charAt(verb.length())) + methodName.substring(verb.length() + 1); 725 } 726 getterByMethod.put(method, propertyName); 727 getterByName.put(propertyName, method); 728 } 729 730 HashMap setterByMethod = new HashMap(methods.length); 731 for (int i = 0; i < methods.length; i++) { 732 Method method = methods[i]; 733 String methodName = method.getName(); 734 735 // must have exactally one arg 736 if (method.getParameterTypes().length != 1) { 737 continue; 738 } 739 740 // must return non void 741 if (method.getReturnType() != void.class) { 742 continue; 743 } 744 745 // must start with set 746 if (!methodName.startsWith("set") || methodName.length() <= 3) { 747 continue; 748 } 749 750 // must only throw Remote or Runtime Exceptions 751 boolean exceptionsValid = true; 752 Class[] exceptionTypes = method.getExceptionTypes(); 753 for (int j = 0; j < exceptionTypes.length; j++) { 754 Class exceptionType = exceptionTypes[j]; 755 if (!RemoteException.class.isAssignableFrom(exceptionType) && 756 !RuntimeException.class.isAssignableFrom(exceptionType) && 757 !Error.class.isAssignableFrom(exceptionType)) { 758 exceptionsValid = false; 759 break; 760 } 761 } 762 if (!exceptionsValid) { 763 continue; 764 } 765 766 String propertyName; 767 if (methodName.length() > 4 && Character.isUpperCase(methodName.charAt(4))) { 768 propertyName = methodName.substring(3); 769 } else { 770 propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4); 771 } 772 773 // must have a matching getter 774 Method getter = (Method) getterByName.get(propertyName); 775 if (getter == null) { 776 continue; 777 } 778 779 // setter property must match gettter return value 780 if (!method.getParameterTypes()[0].equals(getter.getReturnType())) { 781 continue; 782 } 783 setterByMethod.put(method, propertyName); 784 } 785 786 // index the methods by name... used to determine which methods are overloaded 787 HashMap overloadedMethods = new HashMap(methods.length); 788 for (int i = 0; i < methods.length; i++) { 789 Method method = methods[i]; 790 if (getterByMethod.containsKey(method) || setterByMethod.containsKey(method)) { 791 continue; 792 } 793 String methodName = method.getName(); 794 List methodList = (List) overloadedMethods.get(methodName); 795 if (methodList == null) { 796 methodList = new LinkedList(); 797 overloadedMethods.put(methodName, methodList); 798 } 799 methodList.add(method); 800 } 801 802 // index the methods by lower case name... used to determine which methods differ only by case 803 HashMap caseCollisionMethods = new HashMap(methods.length); 804 for (int i = 0; i < methods.length; i++) { 805 Method method = methods[i]; 806 if (getterByMethod.containsKey(method) || setterByMethod.containsKey(method)) { 807 continue; 808 } 809 String lowerCaseMethodName = method.getName().toLowerCase(); 810 Set methodSet = (Set) caseCollisionMethods.get(lowerCaseMethodName); 811 if (methodSet == null) { 812 methodSet = new HashSet(); 813 caseCollisionMethods.put(lowerCaseMethodName, methodSet); 814 } 815 methodSet.add(method.getName()); 816 } 817 818 String className = getClassName(intfClass); 819 Map iiopMap = new HashMap(methods.length); 820 for (int i = 0; i < methods.length; i++) { 821 Method method = methods[i]; 822 823 String iiopName = (String) getterByMethod.get(method); 824 if (iiopName != null) { 825 // if we have a leading underscore prepend with J 826 if (iiopName.charAt(0) == '_') { 827 iiopName = "J_get_" + iiopName.substring(1); 828 } else { 829 iiopName = "_get_" + iiopName; 830 } 831 } else { 832 iiopName = (String) setterByMethod.get(method); 833 if (iiopName != null) { 834 // if we have a leading underscore prepend with J 835 if (iiopName.charAt(0) == '_') { 836 iiopName = "J_set_" + iiopName.substring(1); 837 } else { 838 iiopName = "_set_" + iiopName; 839 } 840 } else { 841 iiopName = method.getName(); 842 843 // if we have a leading underscore prepend with J 844 if (iiopName.charAt(0) == '_') { 845 iiopName = "J" + iiopName; 846 } 847 } 848 } 849 850 // if this name only differs by case add the case index to the end 851 Set caseCollisions = (Set) caseCollisionMethods.get(method.getName().toLowerCase()); 852 if (caseCollisions != null && caseCollisions.size() > 1) { 853 iiopName += upperCaseIndexString(iiopName); 854 } 855 856 // if this is an overloaded method append the parameter string 857 List overloads = (List) overloadedMethods.get(method.getName()); 858 if (overloads != null && overloads.size() > 1) { 859 iiopName += buildOverloadParameterString(method.getParameterTypes()); 860 } 861 862 // if we have a leading underscore prepend with J 863 iiopName = replace(iiopName, '$', "U0024"); 864 865 // if we have matched a keyword prepend with an underscore 866 if (keywords.contains(iiopName.toLowerCase())) { 867 iiopName = "_" + iiopName; 868 } 869 870 // if the name is the same as the class name, append an underscore 871 if (iiopName.equalsIgnoreCase(className)) { 872 iiopName += "_"; 873 } 874 875 if (operationToMethod) { 876 iiopMap.put(iiopName, method); 877 } else { 878 iiopMap.put(method, iiopName); 879 } 880 } 881 882 return iiopMap; 883 } 884 885 private static Method[] getAllMethods(Class intfClass) { 886 LinkedList methods = new LinkedList(); 887 for (Iterator iterator = getAllInterfaces(intfClass).iterator(); iterator.hasNext();) { 888 Class intf = (Class) iterator.next(); 889 methods.addAll(Arrays.asList(intf.getDeclaredMethods())); 890 } 891 892 return (Method[]) methods.toArray(new Method[methods.size()]); 893 } 894 895 /** 896 * Return the a string containing an underscore '_' index of each uppercase character in the iiop name. 897 * 898 * This is used for distinction of names that only differ by case, since corba does not support case sensitive names. 899 */ 900 private static String upperCaseIndexString(String iiopName) { 901 StringBuffer stringBuffer = new StringBuffer(); 902 for (int i = 0; i < iiopName.length(); i++) { 903 char c = iiopName.charAt(i); 904 if (Character.isUpperCase(c)) { 905 stringBuffer.append('_').append(i); 906 } 907 } 908 return stringBuffer.toString(); 909 } 910 911 /** 912 * Replaces any occurnace of the specified "oldChar" with the nes string. 913 * 914 * This is used to replace occurances if '$' in corba names since '$' is a special character 915 */ 916 private static String replace(String source, char oldChar, String newString) { 917 StringBuffer stringBuffer = new StringBuffer(source.length()); 918 for (int i = 0; i < source.length(); i++) { 919 char c = source.charAt(i); 920 if (c == oldChar) { 921 stringBuffer.append(newString); 922 } else { 923 stringBuffer.append(c); 924 } 925 } 926 return stringBuffer.toString(); 927 } 928 929 /** 930 * Return the a string containing a double underscore '__' list of parameter types encoded using the Java to IDL rules. 931 * 932 * This is used for distinction of methods that only differ by parameter lists. 933 */ 934 private static String buildOverloadParameterString(Class[] parameterTypes) { 935 String name = ""; 936 if (parameterTypes.length ==0) { 937 name += "__"; 938 } else { 939 for (int i = 0; i < parameterTypes.length; i++) { 940 Class parameterType = parameterTypes[i]; 941 name += buildOverloadParameterString(parameterType); 942 } 943 } 944 return name.replace('.', '_'); 945 } 946 947 /** 948 * Returns a single parameter type encoded using the Java to IDL rules. 949 */ 950 private static String buildOverloadParameterString(Class parameterType) { 951 String name = "_"; 952 953 int arrayDimensions = 0; 954 while (parameterType.isArray()) { 955 arrayDimensions++; 956 parameterType = parameterType.getComponentType(); 957 } 958 959 // arrays start with org_omg_boxedRMI_ 960 if (arrayDimensions > 0) { 961 name += "_org_omg_boxedRMI"; 962 } 963 964 // IDLEntity types must be prefixed with org_omg_boxedIDL_ 965 if (IDLEntity.class.isAssignableFrom(parameterType)) { 966 name += "_org_omg_boxedIDL"; 967 } 968 969 // add package... some types have special mappings in corba 970 String packageName = (String) specialTypePackages.get(parameterType.getName()); 971 if (packageName == null) { 972 packageName = getPackageName(parameterType.getName()); 973 } 974 if (packageName.length() > 0) { 975 name += "_" + packageName; 976 } 977 978 // arrays now contain a dimension indicator 979 if (arrayDimensions > 0) { 980 name += "_" + "seq" + arrayDimensions; 981 } 982 983 // add the class name 984 String className = (String) specialTypeNames.get(parameterType.getName()); 985 if (className == null) { 986 className = buildClassName(parameterType); 987 } 988 name += "_" + className; 989 990 return name; 991 } 992 993 /** 994 * Returns a string contianing an encoded class name. 995 */ 996 private static String buildClassName(Class type) { 997 if (type.isArray()) { 998 throw new IllegalArgumentException("type is an array: " + type); 999 } 1000 1001 // get the classname 1002 String typeName = type.getName(); 1003 int endIndex = typeName.lastIndexOf('.'); 1004 if (endIndex < 0) { 1005 return typeName; 1006 } 1007 StringBuffer className = new StringBuffer(typeName.substring(endIndex + 1)); 1008 1009 // for innerclasses replace the $ separator with two underscores 1010 // we can't just blindly replace all $ characters since class names can contain the $ character 1011 if (type.getDeclaringClass() != null) { 1012 String declaringClassName = getClassName(type.getDeclaringClass()); 1013 assert className.toString().startsWith(declaringClassName + "$"); 1014 className.replace(declaringClassName.length(), declaringClassName.length() + 1, "__"); 1015 } 1016 1017 // if we have a leading underscore prepend with J 1018 if (className.charAt(0) == '_') { 1019 className.insert(0, "J"); 1020 } 1021 return className.toString(); 1022 } 1023 1024 private static String getClassName(Class type) { 1025 if (type.isArray()) { 1026 throw new IllegalArgumentException("type is an array: " + type); 1027 } 1028 1029 // get the classname 1030 String typeName = type.getName(); 1031 int endIndex = typeName.lastIndexOf('.'); 1032 if (endIndex < 0) { 1033 return typeName; 1034 } 1035 return typeName.substring(endIndex + 1); 1036 } 1037 1038 private static String getPackageName(String interfaceName) { 1039 int endIndex = interfaceName.lastIndexOf('.'); 1040 if (endIndex < 0) { 1041 return ""; 1042 } 1043 return interfaceName.substring(0, endIndex); 1044 } 1045 1046 private static final Map specialTypeNames; 1047 private static final Map specialTypePackages; 1048 private static final Set keywords; 1049 1050 static { 1051 specialTypeNames = new HashMap(); 1052 specialTypeNames.put("boolean", "boolean"); 1053 specialTypeNames.put("char", "wchar"); 1054 specialTypeNames.put("byte", "octet"); 1055 specialTypeNames.put("short", "short"); 1056 specialTypeNames.put("int", "long"); 1057 specialTypeNames.put("long", "long_long"); 1058 specialTypeNames.put("float", "float"); 1059 specialTypeNames.put("double", "double"); 1060 specialTypeNames.put("java.lang.Class", "ClassDesc"); 1061 specialTypeNames.put("java.lang.String", "WStringValue"); 1062 specialTypeNames.put("org.omg.CORBA.Object", "Object"); 1063 1064 specialTypePackages = new HashMap(); 1065 specialTypePackages.put("boolean", ""); 1066 specialTypePackages.put("char", ""); 1067 specialTypePackages.put("byte", ""); 1068 specialTypePackages.put("short", ""); 1069 specialTypePackages.put("int", ""); 1070 specialTypePackages.put("long", ""); 1071 specialTypePackages.put("float", ""); 1072 specialTypePackages.put("double", ""); 1073 specialTypePackages.put("java.lang.Class", "javax.rmi.CORBA"); 1074 specialTypePackages.put("java.lang.String", "CORBA"); 1075 specialTypePackages.put("org.omg.CORBA.Object", ""); 1076 1077 keywords = new HashSet(); 1078 keywords.add("abstract"); 1079 keywords.add("any"); 1080 keywords.add("attribute"); 1081 keywords.add("boolean"); 1082 keywords.add("case"); 1083 keywords.add("char"); 1084 keywords.add("const"); 1085 keywords.add("context"); 1086 keywords.add("custom"); 1087 keywords.add("default"); 1088 keywords.add("double"); 1089 keywords.add("enum"); 1090 keywords.add("exception"); 1091 keywords.add("factory"); 1092 keywords.add("false"); 1093 keywords.add("fixed"); 1094 keywords.add("float"); 1095 keywords.add("in"); 1096 keywords.add("inout"); 1097 keywords.add("interface"); 1098 keywords.add("long"); 1099 keywords.add("module"); 1100 keywords.add("native"); 1101 keywords.add("object"); 1102 keywords.add("octet"); 1103 keywords.add("oneway"); 1104 keywords.add("out"); 1105 keywords.add("private"); 1106 keywords.add("public"); 1107 keywords.add("raises"); 1108 keywords.add("readonly"); 1109 keywords.add("sequence"); 1110 keywords.add("short"); 1111 keywords.add("string"); 1112 keywords.add("struct"); 1113 keywords.add("supports"); 1114 keywords.add("switch"); 1115 keywords.add("true"); 1116 keywords.add("truncatable"); 1117 keywords.add("typedef"); 1118 keywords.add("union"); 1119 keywords.add("unsigned"); 1120 keywords.add("valuebase"); 1121 keywords.add("valuetype"); 1122 keywords.add("void"); 1123 keywords.add("wchar"); 1124 keywords.add("wstring"); 1125 } 1126 1127 }