001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: SmartContextFactory.java 2044 2007-11-16 15:21:54Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.component.smartclient.spi;
025:
026: import java.util.HashMap;
027: import java.util.Hashtable;
028: import java.util.Map;
029: import java.util.StringTokenizer;
030: import java.util.logging.Level;
031: import java.util.logging.Logger;
032:
033: import javax.naming.Context;
034: import javax.naming.InitialContext;
035: import javax.naming.NamingException;
036: import javax.naming.spi.InitialContextFactory;
037:
038: import org.ow2.easybeans.component.smartclient.client.AskingClassLoader;
039:
040: /**
041: * Initial Context factory used on the client side.<br>
042: * It will ask the server for every class/resource not found in local.
043: * @author Florent Benoit
044: *
045: */
046: public class SmartContextFactory implements InitialContextFactory {
047:
048: /**
049: * Use the JDK logger (to avoid any dependency).
050: */
051: private static Logger logger = Logger
052: .getLogger(SmartContextFactory.class.getName());
053:
054: /**
055: * Default PROVIDER_URL.
056: */
057: private static final String DEFAULT_URL = "smart://localhost:2503";
058:
059: /**
060: * Carol factory.
061: */
062: private static final String CAROL_FACTORY = "org.objectweb.carol.jndi.spi.MultiOrbInitialContextFactory";
063:
064: /**
065: * EasyBeans delegating factory. (by default it will be ourself)
066: */
067: public static final String EASYBEANS_DELEGATING_FACTORY = "easybeans.smart.delegate.factory";
068:
069: /**
070: * EasyBeans JNDI factory. (by default it will be Carol)
071: */
072: public static final String EASYBEANS_SMART_JNDI_FACTORY = "easybeans.smart.jndi.factory";
073:
074: /**
075: * EasyBeans factory.
076: */
077: public static final String EASYBEANS_FACTORY = "easybeans.rpc.rmi.factory";
078:
079: /**
080: * Map between a Smart Provider_URL and associated infos.
081: */
082: private static Map<String, SmartContextFactoryInfo> infos = new HashMap<String, SmartContextFactoryInfo>();
083:
084: /**
085: * Default constructor.<br>
086: * Sets the Portable RemoteObject Wrapper class too.
087: */
088: public SmartContextFactory() {
089: // sets our class (or the delegating factory)
090: System.setProperty(EASYBEANS_FACTORY, System
091: .getProperty(EASYBEANS_DELEGATING_FACTORY, this
092: .getClass().getName()));
093: System.setProperty("javax.rmi.CORBA.PortableRemoteObjectClass",
094: ProDelegate.class.getName());
095: }
096:
097: /**
098: * Creates an Initial Context for beginning name resolution.
099: * Special requirements of this context are supplied
100: * @param environment the given environment.
101: * @return the context.
102: * @throws NamingException if no context can be built.
103: */
104: @SuppressWarnings("unchecked")
105: public Context getInitialContext(final Hashtable environment)
106: throws NamingException {
107:
108: SmartContextFactoryInfo info = checkInit(environment);
109:
110: ClassLoader old = Thread.currentThread()
111: .getContextClassLoader();
112: Thread.currentThread().setContextClassLoader(
113: info.getClassLoader());
114: try {
115:
116: // JNDI Factory set in the environment ?
117: String jndiFactory = (String) environment
118: .get(EASYBEANS_SMART_JNDI_FACTORY);
119:
120: // JNDI factory set as system property ?
121: if (jndiFactory == null) {
122: jndiFactory = System
123: .getProperty(EASYBEANS_SMART_JNDI_FACTORY);
124: }
125:
126: // Else, use default factory
127: if (jndiFactory == null) {
128: jndiFactory = CAROL_FACTORY;
129: }
130:
131: // set the right factory
132: environment.put(Context.INITIAL_CONTEXT_FACTORY,
133: jndiFactory);
134: environment
135: .put(Context.PROVIDER_URL, info.getProviderURL());
136:
137: // return wrapped context
138: return new SmartContext(new InitialContext(environment),
139: info.getClassLoader());
140: } finally {
141: Thread.currentThread().setContextClassLoader(old);
142: }
143:
144: }
145:
146: /**
147: * Ensure that all is setup.
148: * It has to work for each PROVIDER_URL
149: * @param environment the InitialContext env.
150: * @return data about the smart factory.
151: * @throws NamingException if there is an exception
152: */
153: protected static SmartContextFactoryInfo checkInit(
154: final Hashtable<?, ?> environment) throws NamingException {
155:
156: // Gets the provider url
157: String currentProviderURL = (String) environment
158: .get(Context.PROVIDER_URL);
159:
160: if (currentProviderURL == null) {
161: logger.log(Level.WARNING,
162: "No PROVIDER_URL setting found, use the default URL '"
163: + DEFAULT_URL + "'.");
164: currentProviderURL = DEFAULT_URL;
165: }
166:
167: // Data existing for the given provider URL ?
168: SmartContextFactoryInfo info = infos.get(currentProviderURL);
169:
170: // Not found, need to initialize
171: if (info == null) {
172: try {
173:
174: info = new SmartContextFactoryInfo();
175:
176: if (logger.isLoggable(Level.FINE)) {
177: logger.log(Level.FINE,
178: "Initializing Smart Factory with remote URL '"
179: + currentProviderURL + "'.");
180: }
181:
182: // extract host
183: String host = getHostOfUrl(currentProviderURL);
184:
185: // extract port
186: int portNumber = getPortOfUrl(currentProviderURL);
187:
188: // build the classloader to use.
189: AskingClassLoader classLoader = new AskingClassLoader(
190: host, portNumber);
191: info.setClassLoader(classLoader);
192:
193: // Set the classloader for the ProDelegate class
194: //TODO: maybe change this ?
195: ProDelegate.setClassLoader(classLoader);
196:
197: String providerURL = classLoader.getProviderURL();
198: if (logger.isLoggable(Level.FINE)) {
199: logger.log(Level.FINE, "Got remote PROVIDER_URL '"
200: + providerURL + "'.");
201: }
202: info.setProviderURL(providerURL);
203:
204: // Add it to the list
205: infos.put(currentProviderURL, info);
206: // And add also the real provider URL
207: infos.put(providerURL, info);
208:
209: } catch (Exception e) {
210: NamingException ne = new NamingException(
211: "Cannot get a remote ClassLoader");
212: ne.initCause(e);
213: throw ne;
214: }
215: }
216:
217: return info;
218: }
219:
220: /**
221: * Parses the given url, and returns the port number. 0 is given in error
222: * case)
223: * @param url given url on which extract port number
224: * @return port number of the url
225: * @throws NamingException if URL is invalid
226: */
227: public static int getPortOfUrl(final String url)
228: throws NamingException {
229: int portNumber = 0;
230: try {
231: StringTokenizer st = new StringTokenizer(url, ":");
232: st.nextToken();
233: st.nextToken();
234: if (st.hasMoreTokens()) {
235: StringTokenizer lastst = new StringTokenizer(st
236: .nextToken(), "/");
237: String pts = lastst.nextToken().trim();
238: int i = pts.indexOf(',');
239: if (i > 0) {
240: pts = pts.substring(0, i);
241: }
242: portNumber = new Integer(pts).intValue();
243: }
244: return portNumber;
245: } catch (Exception e) {
246: // don't rethrow original exception. only URL name is important
247: throw new NamingException(
248: "Invalid URL '"
249: + url
250: + "'. It should be on the format <protocol>://<hostname>:<port>");
251: }
252: }
253:
254: /**
255: * Parses the given url, and returns the hostname.
256: * @param url given url on which extract hostname
257: * @return hostname of the url
258: * @throws NamingException if URL is invalid
259: */
260: private static String getHostOfUrl(final String url)
261: throws NamingException {
262: String host = null;
263: // this would be simpler with a regexp :)
264: try {
265: // url is of the form protocol://<hostname>:<port>
266: String[] tmpSplitStr = url.split(":");
267:
268: // array should be of length = 3
269: // get 2nd element (should be //<hostname>)
270: String tmpHost = tmpSplitStr[1];
271:
272: // remove //
273: String[] tmpSplitHost = tmpHost.split("/");
274:
275: // Get last element of the array to get hostname
276: host = tmpSplitHost[tmpSplitHost.length - 1];
277: } catch (Exception e) {
278: // don't rethrow original exception. only URL name is important
279: throw new NamingException(
280: "Invalid URL '"
281: + url
282: + "'. It should be on the format <protocol>://<hostname>:<port>");
283: }
284: return host;
285: }
286: }
|