View Javadoc
1   package org.apache.commons.jcs3.auxiliary.remote;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.InetSocketAddress;
25  import java.net.ServerSocket;
26  import java.net.Socket;
27  import java.net.URL;
28  import java.nio.file.Files;
29  import java.nio.file.Path;
30  import java.nio.file.Paths;
31  import java.rmi.RemoteException;
32  import java.rmi.registry.LocateRegistry;
33  import java.rmi.registry.Registry;
34  import java.rmi.server.RMISocketFactory;
35  import java.util.Properties;
36  
37  import org.apache.commons.jcs3.log.Log;
38  import org.apache.commons.jcs3.log.LogManager;
39  
40  /**
41   * This class provides some basic utilities for doing things such as starting
42   * the registry properly.
43   */
44  public class RemoteUtils
45  {
46      /** The logger. */
47      private static final Log log = LogManager.getLog(RemoteUtils.class);
48  
49      /** No instances please. */
50      private RemoteUtils()
51      {
52      }
53  
54      /**
55       * Creates and exports a registry on the specified port of the local host.
56       * <p>
57       *
58       * @param port
59       * @return the registry
60       */
61      public static Registry createRegistry(int port)
62      {
63          Registry registry = null;
64  
65          // if ( log.isInfoEnabled() )
66          // {
67          // log.info( "createRegistry> Setting security manager" );
68          // }
69          //
70          // System.setSecurityManager( new RMISecurityManager() );
71  
72          if (port < 1024)
73          {
74              log.warn("createRegistry> Port chosen was less than 1024, will use default [{0}] instead.",
75                      Registry.REGISTRY_PORT);
76              port = Registry.REGISTRY_PORT;
77          }
78  
79          try
80          {
81              registry = LocateRegistry.createRegistry(port);
82              log.info("createRegistry> Created the registry on port {0}", port);
83          }
84          catch (final RemoteException e)
85          {
86              log.warn("createRegistry> Problem creating registry. It may already be started.",
87                      e);
88          }
89          catch (final Throwable t)
90          {
91              log.error("createRegistry> Problem creating registry.", t);
92          }
93  
94          if (registry == null)
95          {
96              try
97              {
98                  registry = LocateRegistry.getRegistry(port);
99              }
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 }