@Test public void rmiClientInterceptorRequiresUrl() throws Exception{ RmiClientInterceptor client = new RmiClientInterceptor(); client.setServiceInterface(IRemoteBean.class); try { client.afterPropertiesSet(); fail("url isn't set, expected IllegalArgumentException"); } catch (IllegalArgumentException ex){ // expected } }
/** * Apply the given AOP method invocation to the given {@link RmiInvocationHandler}. * <p>The default implementation delegates to {@link #createRemoteInvocation}. * @param methodInvocation the current AOP method invocation * @param invocationHandler the RmiInvocationHandler to apply the invocation to * @return the invocation result * @throws RemoteException in case of communication errors * @throws NoSuchMethodException if the method name could not be resolved * @throws IllegalAccessException if the method could not be accessed * @throws InvocationTargetException if the method invocation resulted in an exception * @see org.springframework.remoting.support.RemoteInvocation */ @Nullable protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler) throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (AopUtils.isToStringMethod(methodInvocation.getMethod())) { return "RMI invoker proxy for service URL [" + getServiceUrl() + "]"; } return invocationHandler.invoke(createRemoteInvocation(methodInvocation)); }
/** * Fetches an RMI stub and delegates to {@code doInvoke}. * If configured to refresh on connect failure, it will call * {@link #refreshAndRetry} on corresponding RMI exceptions. * @see #getStub * @see #doInvoke(MethodInvocation, Remote) * @see #refreshAndRetry * @see java.rmi.ConnectException * @see java.rmi.ConnectIOException * @see java.rmi.NoSuchObjectException */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { Remote stub = getStub(); try { return doInvoke(invocation, stub); } catch (RemoteConnectFailureException ex) { return handleRemoteConnectFailure(invocation, ex); } catch (RemoteException ex) { if (isConnectFailure(ex)) { return handleRemoteConnectFailure(invocation, ex); } else { throw ex; } } }
/** * Refresh the RMI stub and retry the given invocation. * Called by invoke on connect failure. * @param invocation the AOP method invocation * @return the invocation result, if any * @throws Throwable in case of invocation failure * @see #invoke */ @Nullable protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable { Remote freshStub = null; synchronized (this.stubMonitor) { this.cachedStub = null; freshStub = lookupStub(); if (this.cacheStub) { this.cachedStub = freshStub; } } return doInvoke(invocation, freshStub); }
/** * Fetches RMI stub on startup, if necessary. * @throws RemoteLookupFailureException if RMI stub creation failed * @see #setLookupStubOnStartup * @see #lookupStub */ public void prepare() throws RemoteLookupFailureException { // Cache RMI stub on initialization? if (this.lookupStubOnStartup) { Remote remoteObj = lookupStub(); if (logger.isDebugEnabled()) { if (remoteObj instanceof RmiInvocationHandler) { logger.debug("RMI stub [" + getServiceUrl() + "] is an RMI invoker"); } else if (getServiceInterface() != null) { boolean isImpl = getServiceInterface().isInstance(remoteObj); logger.debug("Using service interface [" + getServiceInterface().getName() + "] for RMI stub [" + getServiceUrl() + "] - " + (!isImpl ? "not " : "") + "directly implemented"); } } if (this.cacheStub) { this.cachedStub = remoteObj; } } }
return doInvoke(invocation, (RmiInvocationHandler) stub); invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl()); "] failed in RMI service [" + getServiceUrl() + "]", ex); RemoteException rex = (RemoteException) targetEx; throw RmiClientInterceptorUtils.convertRmiAccessException( invocation.getMethod(), rex, isConnectFailure(rex), getServiceUrl());
/** * Refresh the stub and retry the remote invocation if necessary. * <p>If not configured to refresh on connect failure, this method * simply rethrows the original exception. * @param invocation the invocation that failed * @param ex the exception raised on remote invocation * @return the result value of the new invocation, if succeeded * @throws Throwable an exception raised by the new invocation, * if it failed as well * @see #setRefreshStubOnConnectFailure * @see #doInvoke */ @Nullable private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable { if (this.refreshStubOnConnectFailure) { String msg = "Could not connect to RMI service [" + getServiceUrl() + "] - retrying"; if (logger.isDebugEnabled()) { logger.warn(msg, ex); } else if (logger.isWarnEnabled()) { logger.warn(msg); } return refreshAndRetry(invocation); } else { throw ex; } }
@Override public void afterPropertiesSet() { super.afterPropertiesSet(); Class<?> ifc = getServiceInterface(); Assert.notNull(ifc, "Property 'serviceInterface' is required"); this.serviceProxy = new ProxyFactory(ifc, this).getProxy(getBeanClassLoader()); }
@Override public void afterPropertiesSet() { super.afterPropertiesSet(); prepare(); }
/** * Return the RMI stub to use. Called for each invocation. * <p>The default implementation returns the stub created on initialization, * if any. Else, it invokes {@link #lookupStub} to get a new stub for * each invocation. This can be overridden in subclasses, for example in * order to cache a stub for a given amount of time before recreating it, * or to test the stub whether it is still alive. * @return the RMI stub to use for an invocation * @throws RemoteLookupFailureException if RMI stub creation failed * @see #lookupStub */ protected Remote getStub() throws RemoteLookupFailureException { if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) { return (this.cachedStub != null ? this.cachedStub : lookupStub()); } else { synchronized (this.stubMonitor) { if (this.cachedStub == null) { this.cachedStub = lookupStub(); } return this.cachedStub; } } }
URL url = new URL(null, getServiceUrl(), new DummyURLStreamHandler()); String protocol = url.getProtocol(); if (protocol != null && !"rmi".equals(protocol)) { stub = Naming.lookup(getServiceUrl()); logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]"); throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex); "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
/** * Fetches RMI stub on startup, if necessary. * @throws RemoteLookupFailureException if RMI stub creation failed * @see #setLookupStubOnStartup * @see #lookupStub */ public void prepare() throws RemoteLookupFailureException { // Cache RMI stub on initialization? if (this.lookupStubOnStartup) { Remote remoteObj = lookupStub(); if (logger.isDebugEnabled()) { if (remoteObj instanceof RmiInvocationHandler) { logger.debug("RMI stub [" + getServiceUrl() + "] is an RMI invoker"); } else if (getServiceInterface() != null) { boolean isImpl = getServiceInterface().isInstance(remoteObj); logger.debug("Using service interface [" + getServiceInterface().getName() + "] for RMI stub [" + getServiceUrl() + "] - " + (!isImpl ? "not " : "") + "directly implemented"); } } if (this.cacheStub) { this.cachedStub = remoteObj; } } }
return doInvoke(invocation, (RmiInvocationHandler) stub); invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl()); "] failed in RMI service [" + getServiceUrl() + "]", ex); RemoteException rex = (RemoteException) targetEx; throw RmiClientInterceptorUtils.convertRmiAccessException( invocation.getMethod(), rex, isConnectFailure(rex), getServiceUrl());
/** * Refresh the RMI stub and retry the given invocation. * Called by invoke on connect failure. * @param invocation the AOP method invocation * @return the invocation result, if any * @throws Throwable in case of invocation failure * @see #invoke */ @Nullable protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable { Remote freshStub = null; synchronized (this.stubMonitor) { this.cachedStub = null; freshStub = lookupStub(); if (this.cacheStub) { this.cachedStub = freshStub; } } return doInvoke(invocation, freshStub); }
/** * Refresh the stub and retry the remote invocation if necessary. * <p>If not configured to refresh on connect failure, this method * simply rethrows the original exception. * @param invocation the invocation that failed * @param ex the exception raised on remote invocation * @return the result value of the new invocation, if succeeded * @throws Throwable an exception raised by the new invocation, * if it failed as well * @see #setRefreshStubOnConnectFailure * @see #doInvoke */ @Nullable private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable { if (this.refreshStubOnConnectFailure) { String msg = "Could not connect to RMI service [" + getServiceUrl() + "] - retrying"; if (logger.isDebugEnabled()) { logger.warn(msg, ex); } else if (logger.isWarnEnabled()) { logger.warn(msg); } return refreshAndRetry(invocation); } else { throw ex; } }
@Override public void afterPropertiesSet() { super.afterPropertiesSet(); Class<?> ifc = getServiceInterface(); Assert.notNull(ifc, "Property 'serviceInterface' is required"); this.serviceProxy = new ProxyFactory(ifc, this).getProxy(getBeanClassLoader()); }
@Override public void afterPropertiesSet() { super.afterPropertiesSet(); prepare(); }
/** * Return the RMI stub to use. Called for each invocation. * <p>The default implementation returns the stub created on initialization, * if any. Else, it invokes {@link #lookupStub} to get a new stub for * each invocation. This can be overridden in subclasses, for example in * order to cache a stub for a given amount of time before recreating it, * or to test the stub whether it is still alive. * @return the RMI stub to use for an invocation * @throws RemoteLookupFailureException if RMI stub creation failed * @see #lookupStub */ protected Remote getStub() throws RemoteLookupFailureException { if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) { return (this.cachedStub != null ? this.cachedStub : lookupStub()); } else { synchronized (this.stubMonitor) { if (this.cachedStub == null) { this.cachedStub = lookupStub(); } return this.cachedStub; } } }
URL url = new URL(null, getServiceUrl(), new DummyURLStreamHandler()); String protocol = url.getProtocol(); if (protocol != null && !"rmi".equals(protocol)) { stub = Naming.lookup(getServiceUrl()); logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]"); throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex); "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
/** * Fetches an RMI stub and delegates to {@code doInvoke}. * If configured to refresh on connect failure, it will call * {@link #refreshAndRetry} on corresponding RMI exceptions. * @see #getStub * @see #doInvoke(MethodInvocation, Remote) * @see #refreshAndRetry * @see java.rmi.ConnectException * @see java.rmi.ConnectIOException * @see java.rmi.NoSuchObjectException */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { Remote stub = getStub(); try { return doInvoke(invocation, stub); } catch (RemoteConnectFailureException ex) { return handleRemoteConnectFailure(invocation, ex); } catch (RemoteException ex) { if (isConnectFailure(ex)) { return handleRemoteConnectFailure(invocation, ex); } else { throw ex; } } }