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: */package org.apache.openejb.client;
017:
018: import org.omg.CORBA.ORB;
019:
020: import java.io.Serializable;
021: import java.net.URI;
022: import java.net.URISyntaxException;
023: import java.net.ConnectException;
024: import java.rmi.RemoteException;
025: import java.util.Hashtable;
026: import java.util.Properties;
027: import java.util.ArrayList;
028: import java.util.List;
029: import java.lang.reflect.Constructor;
030: import javax.naming.AuthenticationException;
031: import javax.naming.ConfigurationException;
032: import javax.naming.Context;
033: import javax.naming.InvalidNameException;
034: import javax.naming.Name;
035: import javax.naming.NameNotFoundException;
036: import javax.naming.NameParser;
037: import javax.naming.NamingEnumeration;
038: import javax.naming.NamingException;
039: import javax.naming.OperationNotSupportedException;
040: import javax.naming.ServiceUnavailableException;
041: import javax.naming.NameClassPair;
042: import javax.naming.Binding;
043: import javax.naming.spi.InitialContextFactory;
044: import javax.sql.DataSource;
045:
046: /**
047: * @version $Rev: 636282 $ $Date: 2008-03-12 04:21:21 -0700 $
048: */
049: public class JNDIContext implements Serializable,
050: InitialContextFactory, Context {
051:
052: public static final String DEFAULT_PROVIDER_URL = "ejbd://localhost:4201";
053:
054: private static final long serialVersionUID = 1L;
055:
056: private transient String tail = "/";
057: private transient ServerMetaData server;
058: private transient ClientMetaData client;
059: private transient Hashtable env;
060: private String moduleId;
061:
062: JNDIContext(Hashtable environment) throws NamingException {
063: init(environment);
064: }
065:
066: public JNDIContext() {
067: }
068:
069: /*
070: * A neater version of clone
071: */
072: public JNDIContext(JNDIContext that) {
073: this .tail = that.tail;
074: this .server = that.server;
075: this .client = that.client;
076: this .moduleId = that.moduleId;
077: this .env = (Hashtable) that.env.clone();
078: }
079:
080: public void init(Hashtable environment) throws NamingException {
081: }
082:
083: private JNDIResponse request(JNDIRequest req) throws Exception {
084: req.setServerHash(server.buildHash());
085:
086: JNDIResponse response = new JNDIResponse();
087: Client.request(req, response, server);
088: if (null != response.getServer()) {
089: server.merge(response.getServer());
090: }
091: return response;
092: }
093:
094: public static void print(String s) {
095:
096: }
097:
098: public static void println(String s) {
099:
100: }
101:
102: protected AuthenticationResponse requestAuthorization(
103: AuthenticationRequest req) throws RemoteException {
104: return (AuthenticationResponse) Client.request(req,
105: new AuthenticationResponse(), server);
106: }
107:
108: public Context getInitialContext(Hashtable environment)
109: throws NamingException {
110: if (environment == null) {
111: throw new NamingException(
112: "Invalid argument, hashtable cannot be null.");
113: } else {
114: env = (Hashtable) environment.clone();
115: }
116:
117: String userID = (String) env.get(Context.SECURITY_PRINCIPAL);
118: String psswrd = (String) env.get(Context.SECURITY_CREDENTIALS);
119: String providerUrl = (String) env.get(Context.PROVIDER_URL);
120: moduleId = (String) env.get("openejb.client.moduleId");
121:
122: URI location;
123: try {
124: providerUrl = addMissingParts(providerUrl);
125: location = new URI(providerUrl);
126: } catch (URISyntaxException e) {
127: throw (ConfigurationException) new ConfigurationException(
128: "Property value for " + Context.PROVIDER_URL
129: + " invalid: " + providerUrl + " - "
130: + e.getMessage()).initCause(e);
131: }
132: this .server = new ServerMetaData(location);
133: //TODO:1: Either aggressively initiate authentication or wait for the
134: // server to send us an authentication challange.
135: if (userID != null) {
136: authenticate(userID, psswrd);
137: } else {
138: client = new ClientMetaData();
139: }
140:
141: return this ;
142: }
143:
144: /**
145: * Add missing parts - expected only part of the required providerUrl
146: *
147: * TODO: Move the check to a place where it really belongs - ConnectionManager, ConnectionFactory or such
148: * This method (class in general) doesn't really know what is required as far as connection details go
149: * Assuming that java.net.URI or java.net.URL are going to be used is overly stated
150: */
151: String addMissingParts(String providerUrl)
152: throws URISyntaxException {
153: if (providerUrl == null || providerUrl.length() == 0) {
154: providerUrl = DEFAULT_PROVIDER_URL;
155: } else {
156: int colonIndex = providerUrl.indexOf(":");
157: int slashesIndex = providerUrl.indexOf("//");
158: if (colonIndex == -1 && slashesIndex == -1) { // hostname or ip address only
159: providerUrl = "ejbd://" + providerUrl + ":4201";
160: } else if (colonIndex == -1) {
161: URI providerUri = new URI(providerUrl);
162: String scheme = providerUri.getScheme();
163: if (!(scheme.equals("http") || scheme.equals("https"))) {
164: providerUrl = providerUrl + ":4201";
165: }
166: } else if (slashesIndex == -1) {
167: providerUrl = "ejbd://" + providerUrl;
168: }
169: }
170: return providerUrl;
171: }
172:
173: public void authenticate(String userID, String psswrd)
174: throws AuthenticationException {
175:
176: // May be null
177: String realmName = (String) env
178: .get("openejb.authentication.realmName");
179:
180: AuthenticationRequest req = new AuthenticationRequest(
181: realmName, userID, psswrd);
182: AuthenticationResponse res = null;
183:
184: try {
185: res = requestAuthorization(req);
186: } catch (RemoteException e) {
187: throw new AuthenticationException(e.getLocalizedMessage());
188: }
189:
190: switch (res.getResponseCode()) {
191: case ResponseCodes.AUTH_GRANTED:
192: client = res.getIdentity();
193: break;
194: case ResponseCodes.AUTH_REDIRECT:
195: client = res.getIdentity();
196: server = res.getServer();
197: break;
198: case ResponseCodes.AUTH_DENIED:
199: throw (AuthenticationException) new AuthenticationException(
200: "This principle is not authorized.").initCause(res
201: .getDeniedCause());
202: }
203: }
204:
205: public EJBHomeProxy createEJBHomeProxy(EJBMetaDataImpl ejbData) {
206:
207: EJBHomeHandler handler = EJBHomeHandler.createEJBHomeHandler(
208: ejbData, server, client);
209: EJBHomeProxy proxy = handler.createEJBHomeProxy();
210: handler.ejb.ejbHomeProxy = proxy;
211:
212: return proxy;
213:
214: }
215:
216: private Object createBusinessObject(Object result) {
217: EJBMetaDataImpl ejb = (EJBMetaDataImpl) result;
218: Object primaryKey = ejb.getPrimaryKey();
219:
220: EJBObjectHandler handler = EJBObjectHandler
221: .createEJBObjectHandler(ejb, server, client, primaryKey);
222: return handler.createEJBObjectProxy();
223: }
224:
225: public Object lookup(String name) throws NamingException {
226:
227: if (name == null)
228: throw new InvalidNameException("The name cannot be null");
229: else if (name.equals(""))
230: return new JNDIContext(this );
231: else if (name.startsWith("java:"))
232: name = name.replaceFirst("^java:", "");
233: else if (!name.startsWith("/"))
234: name = tail + name;
235:
236: String prop = name.replaceFirst("comp/env/", "");
237: String value = System.getProperty(prop);
238: if (value != null) {
239: return parseEntry(prop, value);
240: }
241:
242: if (name.equals("comp/ORB")) {
243: return getDefaultOrb();
244: }
245:
246: JNDIRequest req = new JNDIRequest();
247: req.setRequestMethod(RequestMethodConstants.JNDI_LOOKUP);
248: req.setRequestString(name);
249: req.setModuleId(moduleId);
250:
251: JNDIResponse res = null;
252: try {
253: res = request(req);
254: } catch (Exception e) {
255: if (e instanceof RemoteException
256: && e.getCause() instanceof ConnectException) {
257: e = (Exception) e.getCause();
258: throw (ServiceUnavailableException) new ServiceUnavailableException(
259: "Cannot lookup '" + name + "'.").initCause(e);
260: }
261: throw (NamingException) new NamingException(
262: "Cannot lookup '" + name + "'.").initCause(e);
263: }
264:
265: switch (res.getResponseCode()) {
266: case ResponseCodes.JNDI_EJBHOME:
267: return createEJBHomeProxy((EJBMetaDataImpl) res.getResult());
268:
269: case ResponseCodes.JNDI_BUSINESS_OBJECT:
270: return createBusinessObject(res.getResult());
271:
272: case ResponseCodes.JNDI_OK:
273: return res.getResult();
274:
275: case ResponseCodes.JNDI_INJECTIONS:
276: return res.getResult();
277:
278: case ResponseCodes.JNDI_CONTEXT:
279: JNDIContext subCtx = new JNDIContext(this );
280: if (!name.endsWith("/"))
281: name += '/';
282: subCtx.tail = name;
283: return subCtx;
284:
285: case ResponseCodes.JNDI_DATA_SOURCE:
286: return createDataSource((DataSourceMetaData) res
287: .getResult());
288:
289: case ResponseCodes.JNDI_WEBSERVICE:
290: return createWebservice((WsMetaData) res.getResult());
291:
292: case ResponseCodes.JNDI_RESOURCE:
293: String type = (String) res.getResult();
294: value = System.getProperty("Resource/" + type);
295: if (value == null) {
296: return null;
297: }
298: return parseEntry(prop, value);
299:
300: case ResponseCodes.JNDI_NOT_FOUND:
301: throw new NameNotFoundException(
302: name
303: + " does not exist in the system. Check that the app was successfully deployed.");
304:
305: case ResponseCodes.JNDI_NAMING_EXCEPTION:
306: Throwable throwable = ((ThrowableArtifact) res.getResult())
307: .getThrowable();
308: if (throwable instanceof NamingException) {
309: throw (NamingException) throwable;
310: }
311: throw (NamingException) new NamingException()
312: .initCause(throwable);
313:
314: case ResponseCodes.JNDI_RUNTIME_EXCEPTION:
315: throw (RuntimeException) res.getResult();
316:
317: case ResponseCodes.JNDI_ERROR:
318: throw (Error) res.getResult();
319:
320: default:
321: throw new RuntimeException("Invalid response from server :"
322: + res.getResponseCode());
323: }
324: }
325:
326: private Object parseEntry(String name, String value)
327: throws NamingException {
328: try {
329: URI uri = new URI(value);
330: String scheme = uri.getScheme();
331: if (scheme.equals("link")) {
332: value = System.getProperty(uri.getSchemeSpecificPart());
333: if (value == null) {
334: return null;
335: }
336: return parseEntry(name, value);
337: } else if (scheme.equals("datasource")) {
338: uri = new URI(uri.getSchemeSpecificPart());
339: String driver = uri.getScheme();
340: String url = uri.getSchemeSpecificPart();
341: return new ClientDataSource(driver, url, null, null);
342: } else if (scheme.equals("connectionfactory")) {
343: uri = new URI(uri.getSchemeSpecificPart());
344: String driver = uri.getScheme();
345: String url = uri.getSchemeSpecificPart();
346: ClassLoader classLoader = Thread.currentThread()
347: .getContextClassLoader();
348: if (classLoader == null)
349: getClass().getClassLoader();
350: if (classLoader == null)
351: ClassLoader.getSystemClassLoader();
352: try {
353: Class<?> clazz = Class.forName(driver, true,
354: classLoader);
355: Constructor<?> constructor = clazz
356: .getConstructor(String.class);
357: Object connectionFactory = constructor
358: .newInstance(url);
359: return connectionFactory;
360: } catch (Exception e) {
361: throw new IllegalStateException(
362: "Cannot use ConnectionFactory in client VM without the classh: "
363: + driver, e);
364: }
365: } else if (scheme.equals("javamail")) {
366: return javax.mail.Session
367: .getDefaultInstance(new Properties());
368: } else if (scheme.equals("orb")) {
369: return getDefaultOrb();
370: } else {
371: throw new UnsupportedOperationException(
372: "Unsupported Naming URI scheme '" + scheme
373: + "'");
374: }
375: } catch (URISyntaxException e) {
376: throw (NamingException) new NamingException(
377: "Unparsable jndi entry '" + name + "=" + value
378: + "'. Exception: " + e.getMessage())
379: .initCause(e);
380: }
381: }
382:
383: private DataSource createDataSource(
384: DataSourceMetaData dataSourceMetaData) {
385: return new ClientDataSource(dataSourceMetaData);
386: }
387:
388: private Object createWebservice(WsMetaData webserviceMetaData)
389: throws NamingException {
390: try {
391: return webserviceMetaData.createWebservice();
392: } catch (Exception e) {
393: throw (NamingException) new NamingException(
394: "Error creating webservice").initCause(e);
395: }
396: }
397:
398: private ORB getDefaultOrb() {
399: return ORB.init();
400: }
401:
402: public Object lookup(Name name) throws NamingException {
403: return lookup(name.toString());
404: }
405:
406: public NamingEnumeration<NameClassPair> list(String name)
407: throws NamingException {
408: if (name == null)
409: throw new InvalidNameException("The name cannot be null");
410: else if (name.startsWith("java:"))
411: name = name.replaceFirst("^java:", "");
412: else if (!name.startsWith("/"))
413: name = tail + name;
414:
415: JNDIRequest req = new JNDIRequest(
416: RequestMethodConstants.JNDI_LIST, name);
417: req.setModuleId(moduleId);
418:
419: JNDIResponse res = null;
420: try {
421: res = request(req);
422: } catch (Exception e) {
423: if (e instanceof RemoteException
424: && e.getCause() instanceof ConnectException) {
425: e = (Exception) e.getCause();
426: throw (ServiceUnavailableException) new ServiceUnavailableException(
427: "Cannot list '" + name + "'.").initCause(e);
428: }
429: throw (NamingException) new NamingException("Cannot list '"
430: + name + "'.").initCause(e);
431: }
432:
433: switch (res.getResponseCode()) {
434:
435: case ResponseCodes.JNDI_OK:
436: return null;
437:
438: case ResponseCodes.JNDI_ENUMERATION:
439: return (NamingEnumeration) res.getResult();
440:
441: case ResponseCodes.JNDI_NOT_FOUND:
442: throw new NameNotFoundException(name);
443:
444: case ResponseCodes.JNDI_NAMING_EXCEPTION:
445: Throwable throwable = ((ThrowableArtifact) res.getResult())
446: .getThrowable();
447: if (throwable instanceof NamingException) {
448: throw (NamingException) throwable;
449: }
450: throw (NamingException) new NamingException()
451: .initCause(throwable);
452:
453: case ResponseCodes.JNDI_ERROR:
454: throw (Error) res.getResult();
455:
456: default:
457: throw new RuntimeException("Invalid response from server :"
458: + res.getResponseCode());
459: }
460:
461: }
462:
463: public NamingEnumeration<NameClassPair> list(Name name)
464: throws NamingException {
465: return list(name.toString());
466: }
467:
468: public NamingEnumeration<Binding> listBindings(String name)
469: throws NamingException {
470: Object o = lookup(name);
471: if (o instanceof Context) {
472: Context context = (Context) o;
473: NamingEnumeration<NameClassPair> enumeration = context
474: .list("");
475: List<NameClassPair> bindings = new ArrayList<NameClassPair>();
476:
477: while (enumeration.hasMoreElements()) {
478: NameClassPair pair = enumeration.nextElement();
479: bindings.add(new LazyBinding(pair.getName(), pair
480: .getClassName(), context));
481: }
482:
483: return new NameClassPairEnumeration(bindings);
484:
485: } else {
486: return null;
487: }
488:
489: }
490:
491: private static class LazyBinding extends Binding {
492: private static final long serialVersionUID = 1L;
493: private RuntimeException failed;
494: private Context context;
495:
496: public LazyBinding(String name, String className,
497: Context context) {
498: super (name, className, null);
499: this .context = context;
500: }
501:
502: public synchronized Object getObject() {
503: if (super .getObject() == null) {
504: if (failed != null)
505: throw failed;
506: try {
507: super .setObject(context.lookup(getName()));
508: } catch (NamingException e) {
509: throw failed = new RuntimeException(
510: "Failed to lazily fetch the binding '"
511: + getName() + "'", e);
512: }
513: }
514: return super .getObject();
515: }
516: }
517:
518: public NamingEnumeration<Binding> listBindings(Name name)
519: throws NamingException {
520: return listBindings(name.toString());
521: }
522:
523: public Object lookupLink(String name) throws NamingException {
524: return lookup(name);
525: }
526:
527: public Object lookupLink(Name name) throws NamingException {
528: return lookupLink(name.toString());
529: }
530:
531: public NameParser getNameParser(String name) throws NamingException {
532: throw new OperationNotSupportedException(
533: "TODO: Needs to be implemented");
534: }
535:
536: public NameParser getNameParser(Name name) throws NamingException {
537: return getNameParser(name.toString());
538: }
539:
540: public String composeName(String name, String prefix)
541: throws NamingException {
542: throw new OperationNotSupportedException(
543: "TODO: Needs to be implemented");
544: }
545:
546: public Name composeName(Name name, Name prefix)
547: throws NamingException {
548: throw new OperationNotSupportedException(
549: "TODO: Needs to be implemented");
550: }
551:
552: public Object addToEnvironment(String key, Object value)
553: throws NamingException {
554: return env.put(key, value);
555: }
556:
557: public Object removeFromEnvironment(String key)
558: throws NamingException {
559: return env.remove(key);
560: }
561:
562: public Hashtable getEnvironment() throws NamingException {
563: return (Hashtable) env.clone();
564: }
565:
566: public String getNameInNamespace() throws NamingException {
567: return "";
568: }
569:
570: public void close() throws NamingException {
571: }
572:
573: public void bind(String name, Object obj) throws NamingException {
574: throw new OperationNotSupportedException();
575: }
576:
577: public void bind(Name name, Object obj) throws NamingException {
578: bind(name.toString(), obj);
579: }
580:
581: public void rebind(String name, Object obj) throws NamingException {
582: throw new OperationNotSupportedException();
583: }
584:
585: public void rebind(Name name, Object obj) throws NamingException {
586: rebind(name.toString(), obj);
587: }
588:
589: public void unbind(String name) throws NamingException {
590: throw new OperationNotSupportedException();
591: }
592:
593: public void unbind(Name name) throws NamingException {
594: unbind(name.toString());
595: }
596:
597: public void rename(String oldname, String newname)
598: throws NamingException {
599: throw new OperationNotSupportedException();
600: }
601:
602: public void rename(Name oldname, Name newname)
603: throws NamingException {
604: rename(oldname.toString(), newname.toString());
605: }
606:
607: public void destroySubcontext(String name) throws NamingException {
608: throw new OperationNotSupportedException();
609: }
610:
611: public void destroySubcontext(Name name) throws NamingException {
612: destroySubcontext(name.toString());
613: }
614:
615: public Context createSubcontext(String name) throws NamingException {
616: throw new OperationNotSupportedException();
617: }
618:
619: public Context createSubcontext(Name name) throws NamingException {
620: return createSubcontext(name.toString());
621: }
622:
623: }
|