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.ClassUtils; 023 024/** 025 * Abstract base class for classes that export a remote service. 026 * Provides "service" and "serviceInterface" bean properties. 027 * 028 * <p>Note that the service interface being used will show some signs of 029 * remotability, like the granularity of method calls that it offers. 030 * Furthermore, it has to have serializable arguments etc. 031 * 032 * @author Juergen Hoeller 033 * @since 26.12.2003 034 */ 035public abstract class RemoteExporter extends RemotingSupport { 036 037 private Object service; 038 039 private Class<?> serviceInterface; 040 041 private Boolean registerTraceInterceptor; 042 043 private Object[] interceptors; 044 045 046 /** 047 * Set the service to export. 048 * Typically populated via a bean reference. 049 */ 050 public void setService(Object service) { 051 this.service = service; 052 } 053 054 /** 055 * Return the service to export. 056 */ 057 public Object getService() { 058 return this.service; 059 } 060 061 /** 062 * Set the interface of the service to export. 063 * The interface must be suitable for the particular service and remoting strategy. 064 */ 065 public void setServiceInterface(Class<?> serviceInterface) { 066 if (serviceInterface != null && !serviceInterface.isInterface()) { 067 throw new IllegalArgumentException("'serviceInterface' must be an interface"); 068 } 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 if (getService() == null) { 114 throw new IllegalArgumentException("Property 'service' is required"); 115 } 116 } 117 118 /** 119 * Check whether a service reference has been set, 120 * and whether it matches the specified service. 121 * @see #setServiceInterface 122 * @see #setService 123 */ 124 protected void checkServiceInterface() throws IllegalArgumentException { 125 Class<?> serviceInterface = getServiceInterface(); 126 Object service = getService(); 127 if (serviceInterface == null) { 128 throw new IllegalArgumentException("Property 'serviceInterface' is required"); 129 } 130 if (service instanceof String) { 131 throw new IllegalArgumentException("Service [" + service + "] is a String " + 132 "rather than an actual service reference: Have you accidentally specified " + 133 "the service bean name as value instead of as reference?"); 134 } 135 if (!serviceInterface.isInstance(service)) { 136 throw new IllegalArgumentException("Service interface [" + serviceInterface.getName() + 137 "] needs to be implemented by service [" + service + "] of class [" + 138 service.getClass().getName() + "]"); 139 } 140 } 141 142 /** 143 * Get a proxy for the given service object, implementing the specified 144 * service interface. 145 * <p>Used to export a proxy that does not expose any internals but just 146 * a specific interface intended for remote access. Furthermore, a 147 * {@link RemoteInvocationTraceInterceptor} will be registered (by default). 148 * @return the proxy 149 * @see #setServiceInterface 150 * @see #setRegisterTraceInterceptor 151 * @see RemoteInvocationTraceInterceptor 152 */ 153 protected Object getProxyForService() { 154 checkService(); 155 checkServiceInterface(); 156 157 ProxyFactory proxyFactory = new ProxyFactory(); 158 proxyFactory.addInterface(getServiceInterface()); 159 160 if (this.registerTraceInterceptor != null ? this.registerTraceInterceptor : this.interceptors == null) { 161 proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName())); 162 } 163 if (this.interceptors != null) { 164 AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); 165 for (Object interceptor : this.interceptors) { 166 proxyFactory.addAdvisor(adapterRegistry.wrap(interceptor)); 167 } 168 } 169 170 proxyFactory.setTarget(getService()); 171 proxyFactory.setOpaque(true); 172 173 return proxyFactory.getProxy(getBeanClassLoader()); 174 } 175 176 /** 177 * Return a short name for this exporter. 178 * Used for tracing of remote invocations. 179 * <p>Default is the unqualified class name (without package). 180 * Can be overridden in subclasses. 181 * @see #getProxyForService 182 * @see RemoteInvocationTraceInterceptor 183 * @see org.springframework.util.ClassUtils#getShortName 184 */ 185 protected String getExporterName() { 186 return ClassUtils.getShortName(getClass()); 187 } 188 189}