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}