001package org.apache.commons.jcs3.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.IOException;
023import java.io.InputStream;
024import java.net.InetSocketAddress;
025import java.net.ServerSocket;
026import java.net.Socket;
027import java.net.URL;
028import java.nio.file.Files;
029import java.nio.file.Path;
030import java.nio.file.Paths;
031import java.rmi.RemoteException;
032import java.rmi.registry.LocateRegistry;
033import java.rmi.registry.Registry;
034import java.rmi.server.RMISocketFactory;
035import java.util.Properties;
036
037import org.apache.commons.jcs3.log.Log;
038import org.apache.commons.jcs3.log.LogManager;
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 = LogManager.getLog(RemoteUtils.class);
048
049    /** No instances please. */
050    private RemoteUtils()
051    {
052    }
053
054    /**
055     * Creates and exports a registry on the specified port of the local host.
056     * <p>
057     *
058     * @param port
059     * @return the registry
060     */
061    public static Registry createRegistry(int port)
062    {
063        Registry registry = null;
064
065        // if ( log.isInfoEnabled() )
066        // {
067        // log.info( "createRegistry> Setting security manager" );
068        // }
069        //
070        // System.setSecurityManager( new RMISecurityManager() );
071
072        if (port < 1024)
073        {
074            log.warn("createRegistry> Port chosen was less than 1024, will use default [{0}] instead.",
075                    Registry.REGISTRY_PORT);
076            port = Registry.REGISTRY_PORT;
077        }
078
079        try
080        {
081            registry = LocateRegistry.createRegistry(port);
082            log.info("createRegistry> Created the registry on port {0}", port);
083        }
084        catch (final RemoteException e)
085        {
086            log.warn("createRegistry> Problem creating registry. It may already be started.",
087                    e);
088        }
089        catch (final Throwable t)
090        {
091            log.error("createRegistry> Problem creating registry.", t);
092        }
093
094        if (registry == null)
095        {
096            try
097            {
098                registry = LocateRegistry.getRegistry(port);
099            }
100            catch (final RemoteException e)
101            {
102                log.error("createRegistry> Problem getting a registry reference.", e);
103            }
104        }
105
106        return registry;
107    }
108
109    /**
110     * Loads properties for the named props file.
111     * First tries class path, then file, then URL
112     * <p>
113     *
114     * @param propFile
115     * @return The properties object for the file, never null
116     * @throws IOException
117     */
118    public static Properties loadProps(final String propFile)
119            throws IOException
120    {
121        InputStream is = RemoteUtils.class.getResourceAsStream(propFile);
122
123        // Try root of class path
124        if (null == is && !propFile.startsWith("/"))
125        {
126            is = RemoteUtils.class.getResourceAsStream("/" + propFile);
127        }
128
129        if (null == is) // not found in class path
130        {
131            Path propPath = Paths.get(propFile);
132            if (Files.exists(propPath))
133            {
134                // file found
135                is = Files.newInputStream(propPath);
136            }
137            else
138            {
139                // try URL
140                is = new URL(propFile).openStream();
141            }
142        }
143
144        final Properties props = new Properties();
145        try
146        {
147            props.load(is);
148            log.debug("props.size={0}", props::size);
149
150            if (log.isTraceEnabled())
151            {
152                final StringBuilder buf = new StringBuilder();
153                props.forEach((key, value)
154                        -> buf.append('\n').append(key).append(" = ").append(value));
155                log.trace(buf.toString());
156            }
157
158        }
159        catch (final IOException ex)
160        {
161            log.error("Error loading remote properties, for file name "
162                    + "[{0}]", propFile, ex);
163        }
164        finally
165        {
166            if (is != null)
167            {
168                is.close();
169            }
170        }
171        return props;
172    }
173
174    /**
175     * Configure a custom socket factory to set the timeout value. This sets the
176     * global socket factory. It's used only if a custom factory is not
177     * configured for the specific object.
178     * <p>
179     *
180     * @param timeoutMillis
181     */
182    public static void configureGlobalCustomSocketFactory(final int timeoutMillis)
183    {
184        try
185        {
186            // Don't set a socket factory if the setting is -1
187            if (timeoutMillis > 0)
188            {
189                log.info("RmiSocketFactoryTimeoutMillis [{0}]. "
190                    + " Configuring a custom socket factory.", timeoutMillis);
191
192                // use this socket factory to add a timeout.
193                RMISocketFactory.setSocketFactory(new RMISocketFactory()
194                {
195                    @Override
196                    public Socket createSocket(final String host, final int port)
197                            throws IOException
198                    {
199                        final Socket socket = new Socket();
200                        socket.setSoTimeout(timeoutMillis);
201                        socket.setSoLinger(false, 0);
202                        socket.connect(new InetSocketAddress(host, port), timeoutMillis);
203                        return socket;
204                    }
205
206                    @Override
207                    public ServerSocket createServerSocket(final int port)
208                            throws IOException
209                    {
210                        return new ServerSocket(port);
211                    }
212                });
213            }
214        }
215        catch (final IOException e)
216        {
217            // Only try to do it once. Otherwise we
218            // Generate errors for each region on construction.
219            final RMISocketFactory factoryInUse = RMISocketFactory.getSocketFactory();
220            if (factoryInUse != null && !factoryInUse.getClass().getName().startsWith("org.apache.commons.jcs3"))
221            {
222                log.info("Could not create new custom socket factory. {0} Factory in use = {1}",
223                        e::getMessage, RMISocketFactory::getSocketFactory);
224            }
225        }
226    }
227
228    /**
229     * Get the naming url used for RMI registration
230     *
231     * @param location
232     *            the remote location
233     * @param serviceName
234     *            the remote service name
235     * @return the URL for RMI lookup
236     */
237    public static String getNamingURL(final RemoteLocation location, final String serviceName)
238    {
239        return getNamingURL(location.getHost(), location.getPort(), serviceName);
240    }
241
242    /**
243     * Get the naming url used for RMI registration
244     *
245     * @param registryHost
246     *            the remote host
247     * @param registryPort
248     *            the remote port
249     * @param serviceName
250     *            the remote service name
251     * @return the URL for RMI lookup
252     */
253    public static String getNamingURL(final String registryHost, final int registryPort, final String serviceName)
254    {
255        if (registryHost.contains(":"))
256        { // TODO improve this check? See also JCS-133
257            return "//[" + registryHost.replaceFirst("%", "%25") + "]:" + registryPort + "/" + serviceName;
258        }
259        return "//" + registryHost + ":" + registryPort + "/" + serviceName;
260    }
261}