001    /* ====================================================================
002     * The Apache Software License, Version 1.1
003     *
004     * Copyright (c) 2003 The Apache Software Foundation.  All rights
005     * reserved.
006     *
007     * Redistribution and use in source and binary forms, with or without
008     * modification, are permitted provided that the following conditions
009     * are met:
010     *
011     * 1. Redistributions of source code must retain the above copyright
012     *    notice, this list of conditions and the following disclaimer.
013     *
014     * 2. Redistributions in binary form must reproduce the above copyright
015     *    notice, this list of conditions and the following disclaimer in
016     *    the documentation and/or other materials provided with the
017     *    distribution.
018     *
019     * 3. The end-user documentation included with the redistribution,
020     *    if any, must include the following acknowledgment:
021     *       "This product includes software developed by the
022     *        Apache Software Foundation (http://www.apache.org/)."
023     *    Alternately, this acknowledgment may appear in the software itself,
024     *    if and wherever such third-party acknowledgments normally appear.
025     *
026     * 4. The names "The Jakarta Project", "Commons", and "Apache Software
027     *    Foundation" must not be used to endorse or promote products derived
028     *    from this software without prior written permission. For written
029     *    permission, please contact apache@apache.org.
030     *
031     * 5. Products derived from this software may not be called "Apache",
032     *    nor may "Apache" appear in their name, without prior written
033     *    permission of the Apache Software Foundation.
034     *
035     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038     * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039     * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042     * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046     * SUCH DAMAGE.
047     * ====================================================================
048     *
049     * This software consists of voluntary contributions made by many
050     * individuals on behalf of the Apache Software Foundation.  For more
051     * information on the Apache Software Foundation, please see
052     * <http://www.apache.org/>.
053     *
054     */
055    package org.jpu.patterns.serviceLocator;
056    
057    import java.util.ArrayList;
058    import java.util.Collections;
059    import java.util.HashMap;
060    import java.util.Iterator;
061    import java.util.List;
062    import java.util.Map;
063    import java.util.Properties;
064    
065    import javax.naming.InitialContext;
066    import javax.rmi.PortableRemoteObject;
067    
068    import org.apache.commons.lang.StringUtils;
069    import org.apache.commons.logging.Log;
070    import org.apache.commons.logging.LogFactory;
071    import org.jpu.patterns.common.JPUStringUtils;
072    
073    /**
074     * Implementation of the {@link ILocator} interface.  See its documentation for more info.
075     */
076    public class Locator implements ILocator {
077            public class DefaultIdentifierParser implements IIdentifierParser {
078                    public DefaultIdentifierParser(String str, Properties props) throws InvalidIdentifierException {
079                            List parts = JPUStringUtils.split( str, getDelimeter(), true );
080                            int i = 0;
081                            _name = (String)parts.get(i++);
082                            _props = new Properties();
083                            _props.putAll( props );
084                            while ( i < parts.size() ) {
085                                    String part = (String)parts.get(i++);
086                                    List halves = JPUStringUtils.splitN( part, "=", 2, true );
087                                    _props.setProperty( (String)halves.get(0), (String)halves.get(1) );
088                            }
089                    }
090    
091                    public String getIdentifier() {
092                            return toString();
093                    }
094    
095                    public String getName() {
096                            return _name;
097                    }
098                    public Properties getProperties() {
099                            return _props;
100                    }
101    
102                    protected String getDelimeter() {
103                            return ";";
104                    }
105    
106                    protected String concatJndiProperties( Properties props ) {
107                            List parts = new ArrayList();
108                            Iterator it = props.entrySet().iterator();
109                            while ( it.hasNext() ) {
110                                    Map.Entry entry = (Map.Entry)it.next();
111                                    parts.add( "" + entry.getKey() + "=" + entry.getValue() );
112                            }
113                            Collections.sort(parts);
114                            return StringUtils.join( parts.iterator(), getDelimeter() );
115                    }
116                    
117                    public String toString() {
118                            String str = getName();
119                            if ( ! _props.isEmpty() ) {
120                                    str += getDelimeter() + concatJndiProperties(_props);
121                            }
122                            return str;
123                    }
124                    private String _name = null;
125                    private Properties _props = new Properties();
126            }
127    
128        /** 
129         * Simple lookup strategy implementation based on "<code>new InitialContext(System.getProperties()).lookup()</code>".
130         */
131        public static class DefaultLookupStrategy implements ILookupStrategy {
132                    public DefaultLookupStrategy(Locator locator) {
133                            _locator = locator;
134                    }
135                    public Properties getProperties() {
136                            return _props;
137                    }
138    
139                    protected Properties getJndiProperties( Properties props ) {
140                            Iterator it = props.entrySet().iterator();
141                            Properties result = new Properties();
142                            while ( it.hasNext() ) {
143                                    Map.Entry entry = (Map.Entry)it.next();
144                                    String key = "" + entry.getKey();
145                                    if ( key.startsWith( "java.naming." ) ) {
146                                            result.put( key, entry.getValue() );
147                                    }
148                            }
149                            return result;
150                    }
151    
152            public Object lookup(String namingIdentifier, ServiceLocatorOptions options) throws ServiceLocatorException {
153                            options = ServiceLocatorOptions.defaultIfNull(options);
154                            InitialContext ic = null;
155                try {
156                    if ( _log.isDebugEnabled() ) {
157                        _log.debug( "lookup(namingIdentifier=[" + namingIdentifier + "], options=[" + options + "])" );
158                    }
159                                    
160                                    Properties props = new Properties();
161                                    props.putAll( System.getProperties() );
162                                    props.putAll( getProperties() );
163                                    Properties jndiProps = getJndiProperties( props );
164                                    IIdentifierParser identifier = _locator.newIdentifier(namingIdentifier, jndiProps);
165                    ic = new InitialContext( identifier.getProperties() );
166                    Object obj = ic.lookup( identifier.getName() );
167                    if ( _log.isDebugEnabled() ) {
168                        _log.debug( "lookup() obtained an object of class '" + obj.getClass().getName() + "'" );
169                    }
170                    return obj;
171                }
172                            catch( ServiceLocatorException e ) {
173                                    throw e;
174                            }
175                catch( Exception e ) {
176                    throw new ServiceLocatorException(e);
177                }
178                            finally {
179                                    if ( ic != null ) {
180                                            try { ic.close(); } catch( Throwable t ) {}
181                                    }
182                            }
183            }
184                    private Properties _props = new Properties();
185                    private Locator _locator = null;
186            private static Log _log = LogFactory.getLog(DefaultLookupStrategy.class); 
187        }
188    
189        /** 
190         * Simple narrow strategy implementation based on "<code>PortableRemoteObject.narrow()</code>".
191         */
192        public static class DefaultNarrowStrategy implements INarrowStrategy {
193            public Object narrow(Object o, Class c) throws ServiceLocatorException {
194                if ( c != null && o != null ) {
195                    try {
196                        if ( _log.isDebugEnabled() ) {
197                            _log.debug( "narrow() is narrowing a '" + o.getClass().getName() + "' to a '" + c.getName() + "'" );
198                        }
199                        o = PortableRemoteObject.narrow(o, c);
200                    }
201                    catch( Exception t ) {
202                        throw new ServiceLocatorException(t);
203                    }
204                }
205                return o;
206            } 
207        }
208    
209        /** 
210         * Simple cache implementation based on <code>HashMap</code>.
211         */
212        public static class NullCache implements ICache {
213            public Object get(String namingIdentifier) {
214                return null;
215            }
216    
217            public void put(String namingIdentifier, Object obj) {
218            }
219    
220            public void clear() {
221            }
222    
223            public void remove(String namingIdentifier) {
224            }
225        }
226    
227            public IIdentifierParser newIdentifier(String str, Properties props) throws InvalidIdentifierException {
228                    return new DefaultIdentifierParser(str, props);
229            }
230    
231            // See the version of this method declared in ILocator.
232        public Object getObject(String namingIdentifier) throws ServiceLocatorException {
233            return getObject(namingIdentifier, (ServiceLocatorOptions)null);
234        }
235    
236            // See the version of this method declared in ILocator.
237        public synchronized void removeFromCache(String namingIdentifier) {
238            getCache().remove(namingIdentifier);
239        }
240    
241            // See the version of this method declared in ILocator.
242        public synchronized void clearCache() {
243            getCache().clear();
244        }
245    
246            // See the version of this method declared in ILocator.
247        public synchronized Object getObject(String namingIdentifier, Class c) throws ServiceLocatorException {
248            return getObject( namingIdentifier, new ServiceLocatorOptions().setCastTo(c) );
249        }
250    
251            // See the version of this method declared in ILocator.
252        public synchronized Object getObject(String namingIdentifier, ServiceLocatorOptions options) throws ServiceLocatorException {
253            boolean dbg = _log.isDebugEnabled();
254            if ( dbg ) {
255                _log.debug( "getObject(namingIdentifier=[" + namingIdentifier + "], options=[" + options + "])" );
256            }
257            options = ServiceLocatorOptions.defaultIfNull(options);
258            Object object = null;
259            boolean noCache = options.noCache;
260            if ( ! noCache ) {
261                object = getCache().get(namingIdentifier);
262            }
263            if ( object == null ) {
264                try {
265                    object = lookup( namingIdentifier, options );
266                    getCache().put(namingIdentifier, object);
267                }
268                catch( ServiceLocatorException sle ) {
269                    throw sle;
270                }
271                catch( Exception e ) {
272                    throw new ServiceLocatorException(e);
273                }
274            }
275            return narrow(object, options);
276        }
277    
278            // See the version of this method declared in ILocator.
279        public synchronized void setCache(ICache cache) {
280            _cache = cache;
281        }
282    
283            // See the version of this method declared in ILocator.
284        public synchronized ICache getCache() {
285            return _cache;
286        }
287    
288            // See the version of this method declared in ILocator.
289        public synchronized void setNarrowStrategy(INarrowStrategy ls) {
290            _narrowStrategy = ls;
291        }
292    
293            // See the version of this method declared in ILocator.
294        public synchronized INarrowStrategy getNarrowStrategy() {
295            return _narrowStrategy;
296        }
297    
298            // See the version of this method declared in ILocator.
299        public synchronized void setLookupStrategy(ILookupStrategy ls) {
300            _lookupStrategy = ls;
301        }
302    
303            // See the version of this method declared in ILocator.
304        public synchronized ILookupStrategy getLookupStrategy() {
305            return _lookupStrategy;
306        }
307    
308            // See the version of this method declared in ILocator.
309        public ICache newCache() {
310            return new NullCache();
311        }
312    
313            // See the version of this method declared in ILocator.
314        public ILookupStrategy newLookupStrategy() {
315            return new DefaultLookupStrategy(this);
316        }
317    
318            // See the version of this method declared in ILocator.
319        public INarrowStrategy newNarrowStrategy() {
320            return new DefaultNarrowStrategy();
321        }
322    
323            // See the version of this method declared in ILocator.
324        public Object narrow(Object o, ServiceLocatorOptions options) throws ServiceLocatorException {
325            Object object = o;
326            Class castTo = null;
327            options = ServiceLocatorOptions.defaultIfNull(options);
328            if ( options.castTo == null ) {
329                castTo = getDefaultCast();
330            }
331            if ( castTo != null ) {
332                object = narrow(object, castTo);
333            }
334            return object;
335        }
336    
337            // See the version of this method declared in ILocator.
338        public Object narrow(Object o, Class c) throws ServiceLocatorException {
339            return getNarrowStrategy().narrow(o, c);
340        } 
341    
342        /**
343         * Simply calls "<code>getLookupStrategy().lookup(namingIdentifier, options)</code>".  If you wish to   
344         * change the way lookups are performed, write your own {@link ILocator.ILookupStrategy} and either override
345         * {@link #newLookupStrategy()} to instantiate it, or call the public {@link #setLookupStrategy(ILocator.ILookupStrategy)}
346         * method to replace the existing lookup strategy.
347         */
348        public Object lookup(String namingIdentifier, ServiceLocatorOptions options) throws ServiceLocatorException {
349            return getLookupStrategy().lookup(namingIdentifier, options);
350        } 
351    
352            // See the version of this method declared in ILocator.
353        public Class getDefaultCast() {
354            return null;
355        }
356    
357        private ICache _cache = newCache();
358        private ILookupStrategy _lookupStrategy = newLookupStrategy();
359        private INarrowStrategy _narrowStrategy = newNarrowStrategy();
360        private static Log _log = LogFactory.getLog(Locator.class); 
361    }
362