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 }