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