001package org.apache.commons.jcs.auxiliary.remote;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.IOException;
025import java.io.InputStream;
026import java.net.InetSocketAddress;
027import java.net.ServerSocket;
028import java.net.Socket;
029import java.net.URL;
030import java.rmi.RemoteException;
031import java.rmi.registry.LocateRegistry;
032import java.rmi.registry.Registry;
033import java.rmi.server.RMISocketFactory;
034import java.util.Enumeration;
035import java.util.Properties;
036
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039
040/**
041 * This class provides some basic utilities for doing things such as starting
042 * the registry properly.
043 */
044public class RemoteUtils
045{
046    /** The logger. */
047    private static final Log log = LogFactory.getLog(RemoteUtils.class);
048
049    /** No instances please. */
050    private RemoteUtils()
051    {
052        super();
053    }
054
055    /**
056     * Creates and exports a registry on the specified port of the local host.
057     * <p>
058     *
059     * @param port
060     * @return the registry
061     */
062    public static Registry createRegistry(int port)
063    {
064        Registry registry = null;
065
066        // if ( log.isInfoEnabled() )
067        // {
068        // log.info( "createRegistry> Setting security manager" );
069        // }
070        //
071        // System.setSecurityManager( new RMISecurityManager() );
072
073        if (port < 1024)
074        {
075            if (log.isWarnEnabled())
076            {
077                log.warn("createRegistry> Port chosen was less than 1024, will use default [" + Registry.REGISTRY_PORT + "] instead.");
078            }
079            port = Registry.REGISTRY_PORT;
080        }
081
082        try
083        {
084            registry = LocateRegistry.createRegistry(port);
085            log.info("createRegistry> Created the registry on port " + port);
086        }
087        catch (RemoteException e)
088        {
089            log.warn("createRegistry> Problem creating registry. It may already be started. " + e.getMessage());
090        }
091        catch (Throwable t)
092        {
093            log.error("createRegistry> Problem creating registry.", t);
094        }
095
096        if (registry == null)
097        {
098            try
099            {
100                registry = LocateRegistry.getRegistry(port);
101            }
102            catch (RemoteException e)
103            {
104                log.error("createRegistry> Problem getting a registry reference.", e);
105            }
106        }
107
108        return registry;
109    }
110
111    /**
112     * Loads properties for the named props file.
113     * First tries class path, then file, then URL
114     * <p>
115     *
116     * @param propFile
117     * @return The properties object for the file
118     * @throws IOException
119     */
120    public static Properties loadProps(String propFile)
121            throws IOException
122    {
123        InputStream is = RemoteUtils.class.getResourceAsStream(propFile);
124
125        if (null == is) // not found in class path
126        {
127            // Try root of class path
128            if (propFile != null && !propFile.startsWith("/"))
129            {
130                is = RemoteUtils.class.getResourceAsStream("/" + propFile);
131            }
132        }
133        
134        if (null == is) // not found in class path
135        {
136            if (new File(propFile).exists())
137            {
138                // file found
139                is = new FileInputStream(propFile);
140            }
141            else
142            {
143                // try URL
144                is = new URL(propFile).openStream();
145            }
146        }
147
148        Properties props = new Properties();
149        try
150        {
151            props.load(is);
152            if (log.isDebugEnabled())
153            {
154                log.debug("props.size=" + props.size());
155            }
156
157            if (log.isDebugEnabled())
158            {
159                Enumeration<Object> en = props.keys();
160                StringBuilder buf = new StringBuilder();
161                while (en.hasMoreElements())
162                {
163                    String key = (String) en.nextElement();
164                    buf.append("\n" + key + " = " + props.getProperty(key));
165                }
166                log.debug(buf.toString());
167            }
168
169        }
170        catch (Exception ex)
171        {
172            log.error("Error loading remote properties, for file name [" + propFile + "]", ex);
173        }
174        finally
175        {
176            if (is != null)
177            {
178                is.close();
179            }
180        }
181        return props;
182    }
183
184    /**
185     * Configure a custom socket factory to set the timeout value. This sets the
186     * global socket factory. It's used only if a custom factory is not
187     * configured for the specific object.
188     * <p>
189     *
190     * @param timeoutMillis
191     */
192    public static void configureGlobalCustomSocketFactory(final int timeoutMillis)
193    {
194        try
195        {
196            // Don't set a socket factory if the setting is -1
197            if (timeoutMillis > 0)
198            {
199                if (log.isInfoEnabled())
200                {
201                    log.info("RmiSocketFactoryTimeoutMillis [" + timeoutMillis + "]. "
202                            + " Configuring a custom socket factory.");
203                }
204
205                // use this socket factory to add a timeout.
206                RMISocketFactory.setSocketFactory(new RMISocketFactory()
207                {
208                    @Override
209                    public Socket createSocket(String host, int port)
210                            throws IOException
211                    {
212                        Socket socket = new Socket();
213                        socket.setSoTimeout(timeoutMillis);
214                        socket.setSoLinger(false, 0);
215                        socket.connect(new InetSocketAddress(host, port), timeoutMillis);
216                        return socket;
217                    }
218
219                    @Override
220                    public ServerSocket createServerSocket(int port)
221                            throws IOException
222                    {
223                        return new ServerSocket(port);
224                    }
225                });
226            }
227        }
228        catch (IOException e)
229        {
230            // Only try to do it once. Otherwise we
231            // Generate errors for each region on construction.
232            RMISocketFactory factoryInUse = RMISocketFactory.getSocketFactory();
233            if (factoryInUse != null && !factoryInUse.getClass().getName().startsWith("org.apache.commons.jcs"))
234            {
235                log.info("Could not create new custom socket factory. " + e.getMessage() + " Factory in use = "
236                        + RMISocketFactory.getSocketFactory());
237            }
238        }
239    }
240
241    /**
242     * Get the naming url used for RMI registration
243     *
244     * @param location
245     *            the remote location
246     * @param serviceName
247     *            the remote service name
248     * @return the URL for RMI lookup
249     */
250    public static String getNamingURL(final RemoteLocation location, final String serviceName)
251    {
252        return getNamingURL(location.getHost(), location.getPort(), serviceName);
253    }
254
255    /**
256     * Get the naming url used for RMI registration
257     *
258     * @param registryHost
259     *            the remote host
260     * @param registryPort
261     *            the remote port
262     * @param serviceName
263     *            the remote service name
264     * @return the URL for RMI lookup
265     */
266    public static String getNamingURL(final String registryHost, final int registryPort, final String serviceName)
267    {
268        if (registryHost.contains(":"))
269        { // TODO improve this check? See also JCS-133
270            return "//[" + registryHost.replaceFirst("%", "%25") + "]:" + registryPort + "/" + serviceName;
271        }
272        final String registryURL = "//" + registryHost + ":" + registryPort + "/" + serviceName;
273        return registryURL;
274    }
275}