001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.remoting.support; 018 019import org.springframework.aop.framework.ProxyFactory; 020import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry; 021import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry; 022import org.springframework.util.Assert; 023import org.springframework.util.ClassUtils; 024 025/** 026 * Abstract base class for classes that export a remote service. 027 * Provides "service" and "serviceInterface" bean properties. 028 * 029 * <p>Note that the service interface being used will show some signs of 030 * remotability, like the granularity of method calls that it offers. 031 * Furthermore, it has to have serializable arguments etc. 032 * 033 * @author Juergen Hoeller 034 * @since 26.12.2003 035 */ 036public abstract class RemoteExporter extends RemotingSupport { 037 038 private Object service; 039 040 private Class<?> serviceInterface; 041 042 private Boolean registerTraceInterceptor; 043 044 private Object[] interceptors; 045 046 047 /** 048 * Set the service to export. 049 * Typically populated via a bean reference. 050 */ 051 public void setService(Object service) { 052 this.service = service; 053 } 054 055 /** 056 * Return the service to export. 057 */ 058 public Object getService() { 059 return this.service; 060 } 061 062 /** 063 * Set the interface of the service to export. 064 * The interface must be suitable for the particular service and remoting strategy. 065 */ 066 public void setServiceInterface(Class<?> serviceInterface) { 067 Assert.notNull(serviceInterface, "'serviceInterface' must not be null"); 068 Assert.isTrue(serviceInterface.isInterface(), "'serviceInterface' must be an interface"); 069 this.serviceInterface = serviceInterface; 070 } 071 072 /** 073 * Return the interface of the service to export. 074 */ 075 public Class<?> getServiceInterface() { 076 return this.serviceInterface; 077 } 078 079 /** 080 * Set whether to register a RemoteInvocationTraceInterceptor for exported 081 * services. Only applied when a subclass uses {@code getProxyForService} 082 * for creating the proxy to expose. 083 * <p>Default is "true". RemoteInvocationTraceInterceptor's most important value 084 * is that it logs exception stacktraces on the server, before propagating an 085 * exception to the client. Note that RemoteInvocationTraceInterceptor will <i>not</i> 086 * be registered by default if the "interceptors" property has been specified. 087 * @see #setInterceptors 088 * @see #getProxyForService 089 * @see RemoteInvocationTraceInterceptor 090 */ 091 public void setRegisterTraceInterceptor(boolean registerTraceInterceptor) { 092 this.registerTraceInterceptor = registerTraceInterceptor; 093 } 094 095 /** 096 * Set additional interceptors (or advisors) to be applied before the 097 * remote endpoint, e.g. a PerformanceMonitorInterceptor. 098 * <p>You may specify any AOP Alliance MethodInterceptors or other 099 * Spring AOP Advices, as well as Spring AOP Advisors. 100 * @see #getProxyForService 101 * @see org.springframework.aop.interceptor.PerformanceMonitorInterceptor 102 */ 103 public void setInterceptors(Object[] interceptors) { 104 this.interceptors = interceptors; 105 } 106 107 108 /** 109 * Check whether the service reference has been set. 110 * @see #setService 111 */ 112 protected void checkService() throws IllegalArgumentException { 113 Assert.notNull(getService(), "Property 'service' is required"); 114 } 115 116 /** 117 * Check whether a service reference has been set, 118 * and whether it matches the specified service. 119 * @see #setServiceInterface 120 * @see #setService 121 */ 122 protected void checkServiceInterface() throws IllegalArgumentException { 123 Class<?> serviceInterface = getServiceInterface(); 124 Assert.notNull(serviceInterface, "Property 'serviceInterface' is required"); 125 126 Object service = getService(); 127 if (service instanceof String) { 128 throw new IllegalArgumentException("Service [" + service + "] is a String " + 129 "rather than an actual service reference: Have you accidentally specified " + 130 "the service bean name as value instead of as reference?"); 131 } 132 if (!serviceInterface.isInstance(service)) { 133 throw new IllegalArgumentException("Service interface [" + serviceInterface.getName() + 134 "] needs to be implemented by service [" + service + "] of class [" + 135 service.getClass().getName() + "]"); 136 } 137 } 138 139 /** 140 * Get a proxy for the given service object, implementing the specified 141 * service interface. 142 * <p>Used to export a proxy that does not expose any internals but just 143 * a specific interface intended for remote access. Furthermore, a 144 * {@link RemoteInvocationTraceInterceptor} will be registered (by default). 145 * @return the proxy 146 * @see #setServiceInterface 147 * @see #setRegisterTraceInterceptor 148 * @see RemoteInvocationTraceInterceptor 149 */ 150 protected Object getProxyForService() { 151 checkService(); 152 checkServiceInterface(); 153 154 ProxyFactory proxyFactory = new ProxyFactory(); 155 proxyFactory.addInterface(getServiceInterface()); 156 157 if (this.registerTraceInterceptor != null ? this.registerTraceInterceptor : this.interceptors == null) { 158 proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName())); 159 } 160 if (this.interceptors != null) { 161 AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); 162 for (Object interceptor : this.interceptors) { 163 proxyFactory.addAdvisor(adapterRegistry.wrap(interceptor)); 164 } 165 } 166 167 proxyFactory.setTarget(getService()); 168 proxyFactory.setOpaque(true); 169 170 return proxyFactory.getProxy(getBeanClassLoader()); 171 } 172 173 /** 174 * Return a short name for this exporter. 175 * Used for tracing of remote invocations. 176 * <p>Default is the unqualified class name (without package). 177 * Can be overridden in subclasses. 178 * @see #getProxyForService 179 * @see RemoteInvocationTraceInterceptor 180 * @see org.springframework.util.ClassUtils#getShortName 181 */ 182 protected String getExporterName() { 183 return ClassUtils.getShortName(getClass()); 184 } 185 186}