1 16 17 package org.springframework.remoting.rmi; 18 19 import java.io.IOException ; 20 import java.lang.reflect.InvocationTargetException ; 21 import java.net.MalformedURLException ; 22 import java.net.URL ; 23 import java.net.URLConnection ; 24 import java.net.URLStreamHandler ; 25 import java.rmi.Naming ; 26 import java.rmi.NotBoundException ; 27 import java.rmi.Remote ; 28 import java.rmi.RemoteException ; 29 import java.rmi.registry.LocateRegistry ; 30 import java.rmi.registry.Registry ; 31 import java.rmi.server.RMIClientSocketFactory ; 32 33 import org.aopalliance.intercept.MethodInterceptor; 34 import org.aopalliance.intercept.MethodInvocation; 35 36 import org.springframework.aop.support.AopUtils; 37 import org.springframework.remoting.RemoteConnectFailureException; 38 import org.springframework.remoting.RemoteLookupFailureException; 39 import org.springframework.remoting.RemoteProxyFailureException; 40 import org.springframework.remoting.support.RemoteInvocationBasedAccessor; 41 import org.springframework.remoting.support.RemoteInvocationUtils; 42 43 69 public class RmiClientInterceptor extends RemoteInvocationBasedAccessor 70 implements MethodInterceptor { 71 72 private boolean lookupStubOnStartup = true; 73 74 private boolean cacheStub = true; 75 76 private boolean refreshStubOnConnectFailure = false; 77 78 private RMIClientSocketFactory registryClientSocketFactory; 79 80 private Remote cachedStub; 81 82 private final Object stubMonitor = new Object (); 83 84 85 91 public void setLookupStubOnStartup(boolean lookupStubOnStartup) { 92 this.lookupStubOnStartup = lookupStubOnStartup; 93 } 94 95 102 public void setCacheStub(boolean cacheStub) { 103 this.cacheStub = cacheStub; 104 } 105 106 117 public void setRefreshStubOnConnectFailure(boolean refreshStubOnConnectFailure) { 118 this.refreshStubOnConnectFailure = refreshStubOnConnectFailure; 119 } 120 121 126 public void setRegistryClientSocketFactory(RMIClientSocketFactory registryClientSocketFactory) { 127 this.registryClientSocketFactory = registryClientSocketFactory; 128 } 129 130 131 public void afterPropertiesSet() { 132 super.afterPropertiesSet(); 133 prepare(); 134 } 135 136 142 public void prepare() throws RemoteLookupFailureException { 143 if (this.lookupStubOnStartup) { 145 Remote remoteObj = lookupStub(); 146 if (logger.isDebugEnabled()) { 147 if (remoteObj instanceof RmiInvocationHandler) { 148 logger.debug("RMI stub [" + getServiceUrl() + "] is an RMI invoker"); 149 } 150 else if (getServiceInterface() != null) { 151 boolean isImpl = getServiceInterface().isInstance(remoteObj); 152 logger.debug("Using service interface [" + getServiceInterface().getName() + 153 "] for RMI stub [" + getServiceUrl() + "] - " + 154 (!isImpl ? "not " : "") + "directly implemented"); 155 } 156 } 157 if (this.cacheStub) { 158 this.cachedStub = remoteObj; 159 } 160 } 161 } 162 163 174 protected Remote lookupStub() throws RemoteLookupFailureException { 175 try { 176 Remote stub = null; 177 if (this.registryClientSocketFactory != null) { 178 URL url = new URL (null, getServiceUrl(), new DummyURLStreamHandler()); 183 String protocol = url.getProtocol(); 184 if (protocol != null && !"rmi".equals(protocol)) { 185 throw new MalformedURLException ("Invalid URL scheme '" + protocol + "'"); 186 } 187 String host = url.getHost(); 188 int port = url.getPort(); 189 String name = url.getPath(); 190 if (name != null && name.startsWith("/")) { 191 name = name.substring(1); 192 } 193 Registry registry = LocateRegistry.getRegistry(host, port, this.registryClientSocketFactory); 194 stub = registry.lookup(name); 195 } 196 else { 197 stub = Naming.lookup(getServiceUrl()); 199 } 200 if (logger.isDebugEnabled()) { 201 logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]"); 202 } 203 return stub; 204 } 205 catch (MalformedURLException ex) { 206 throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex); 207 } 208 catch (NotBoundException ex) { 209 throw new RemoteLookupFailureException( 210 "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex); 211 } 212 catch (RemoteException ex) { 213 throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex); 214 } 215 } 216 217 228 protected Remote getStub() throws RemoteLookupFailureException { 229 if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) { 230 return (this.cachedStub != null ? this.cachedStub : lookupStub()); 231 } 232 else { 233 synchronized (this.stubMonitor) { 234 if (this.cachedStub == null) { 235 this.cachedStub = lookupStub(); 236 } 237 return this.cachedStub; 238 } 239 } 240 } 241 242 243 254 public Object invoke(MethodInvocation invocation) throws Throwable { 255 Remote stub = getStub(); 256 try { 257 return doInvoke(invocation, stub); 258 } 259 catch (RemoteConnectFailureException ex) { 260 return handleRemoteConnectFailure(invocation, ex); 261 } 262 catch (RemoteException ex) { 263 if (isConnectFailure(ex)) { 264 return handleRemoteConnectFailure(invocation, ex); 265 } 266 else { 267 throw ex; 268 } 269 } 270 } 271 272 279 protected boolean isConnectFailure(RemoteException ex) { 280 return RmiClientInterceptorUtils.isConnectFailure(ex); 281 } 282 283 292 private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable { 293 if (this.refreshStubOnConnectFailure) { 294 if (logger.isDebugEnabled()) { 295 logger.debug("Could not connect to RMI service [" + getServiceUrl() + "] - retrying", ex); 296 } 297 else if (logger.isWarnEnabled()) { 298 logger.warn("Could not connect to RMI service [" + getServiceUrl() + "] - retrying"); 299 } 300 return refreshAndRetry(invocation); 301 } 302 else { 303 throw ex; 304 } 305 } 306 307 315 protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable { 316 Remote freshStub = null; 317 synchronized (this.stubMonitor) { 318 freshStub = lookupStub(); 319 if (this.cacheStub) { 320 this.cachedStub = freshStub; 321 } 322 } 323 return doInvoke(invocation, freshStub); 324 } 325 326 333 protected Object doInvoke(MethodInvocation invocation, Remote stub) throws Throwable { 334 if (stub instanceof RmiInvocationHandler) { 335 try { 337 return doInvoke(invocation, (RmiInvocationHandler) stub); 338 } 339 catch (RemoteException ex) { 340 throw RmiClientInterceptorUtils.convertRmiAccessException( 341 invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl()); 342 } 343 catch (InvocationTargetException ex) { 344 Throwable exToThrow = ex.getTargetException(); 345 RemoteInvocationUtils.fillInClientStackTraceIfPossible(exToThrow); 346 throw exToThrow; 347 } 348 catch (Throwable ex) { 349 throw new RemoteProxyFailureException( 350 "Failed to invoke RMI stub for remote service [" + getServiceUrl() + "]", ex); 351 } 352 } 353 else { 354 try { 356 return RmiClientInterceptorUtils.doInvoke(invocation, stub); 357 } 358 catch (InvocationTargetException ex) { 359 Throwable targetEx = ex.getTargetException(); 360 if (targetEx instanceof RemoteException ) { 361 RemoteException rex = (RemoteException ) targetEx; 362 throw RmiClientInterceptorUtils.convertRmiAccessException( 363 invocation.getMethod(), rex, isConnectFailure(rex), getServiceUrl()); 364 } 365 else { 366 throw targetEx; 367 } 368 } 369 } 370 } 371 372 384 protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler) 385 throws RemoteException , NoSuchMethodException , IllegalAccessException , InvocationTargetException { 386 387 if (AopUtils.isToStringMethod(methodInvocation.getMethod())) { 388 return "RMI invoker proxy for service URL [" + getServiceUrl() + "]"; 389 } 390 391 return invocationHandler.invoke(createRemoteInvocation(methodInvocation)); 392 } 393 394 395 400 private static class DummyURLStreamHandler extends URLStreamHandler { 401 402 protected URLConnection openConnection(URL url) throws IOException { 403 throw new UnsupportedOperationException (); 404 } 405 } 406 407 } 408 | Popular Tags |