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 }