001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.remoting.support;
018:
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021:
022: import org.springframework.aop.framework.ProxyFactory;
023: import org.springframework.beans.factory.BeanClassLoaderAware;
024: import org.springframework.util.ClassUtils;
025:
026: /**
027: * Abstract base class for classes that export a remote service.
028: * Provides "service" and "serviceInterface" bean properties.
029: *
030: * <p>Note that the service interface being used will show some signs of
031: * remotability, like the granularity of method calls that it offers.
032: * Furthermore, it has to have serializable arguments etc.
033: *
034: * @author Juergen Hoeller
035: * @since 26.12.2003
036: */
037: public abstract class RemoteExporter implements BeanClassLoaderAware {
038:
039: /** Logger available to subclasses */
040: protected final Log logger = LogFactory.getLog(getClass());
041:
042: private Object service;
043:
044: private Class serviceInterface;
045:
046: private boolean registerTraceInterceptor = true;
047:
048: private ClassLoader beanClassLoader = ClassUtils
049: .getDefaultClassLoader();
050:
051: /**
052: * Set the service to export.
053: * Typically populated via a bean reference.
054: */
055: public void setService(Object service) {
056: this .service = service;
057: }
058:
059: /**
060: * Return the service to export.
061: */
062: public Object getService() {
063: return this .service;
064: }
065:
066: /**
067: * Set the interface of the service to export.
068: * The interface must be suitable for the particular service and remoting strategy.
069: */
070: public void setServiceInterface(Class serviceInterface) {
071: if (serviceInterface != null && !serviceInterface.isInterface()) {
072: throw new IllegalArgumentException(
073: "'serviceInterface' must be an interface");
074: }
075: this .serviceInterface = serviceInterface;
076: }
077:
078: /**
079: * Return the interface of the service to export.
080: */
081: public Class getServiceInterface() {
082: return this .serviceInterface;
083: }
084:
085: /**
086: * Set whether to register a RemoteInvocationTraceInterceptor for exported
087: * services. Only applied when a subclass uses <code>getProxyForService</code>
088: * for creating the proxy to expose.
089: * <p>Default is "true". RemoteInvocationTraceInterceptor's most important value
090: * is that it logs exception stacktraces on the server, before propagating
091: * an exception to the client.
092: * @see #getProxyForService
093: * @see RemoteInvocationTraceInterceptor
094: */
095: public void setRegisterTraceInterceptor(
096: boolean registerTraceInterceptor) {
097: this .registerTraceInterceptor = registerTraceInterceptor;
098: }
099:
100: /**
101: * Return whether to register a RemoteInvocationTraceInterceptor for
102: * exported services.
103: */
104: protected boolean isRegisterTraceInterceptor() {
105: return this .registerTraceInterceptor;
106: }
107:
108: public void setBeanClassLoader(ClassLoader classLoader) {
109: this .beanClassLoader = classLoader;
110: }
111:
112: /**
113: * Check whether the service reference has been set.
114: * @see #setService
115: */
116: protected void checkService() throws IllegalArgumentException {
117: if (this .service == null) {
118: throw new IllegalArgumentException(
119: "Property 'service' is required");
120: }
121: }
122:
123: /**
124: * Check whether a service reference has been set,
125: * and whether it matches the specified service.
126: * @see #setServiceInterface
127: * @see #setService
128: */
129: protected void checkServiceInterface()
130: throws IllegalArgumentException {
131: if (this .serviceInterface == null) {
132: throw new IllegalArgumentException(
133: "Property 'serviceInterface' is required");
134: }
135: if (this .service instanceof String) {
136: throw new IllegalArgumentException(
137: "Service ["
138: + this .service
139: + "] is a String "
140: + "rather than an actual service reference: Have you accidentally specified "
141: + "the service bean name as value instead of as reference?");
142: }
143: if (!this .serviceInterface.isInstance(this .service)) {
144: throw new IllegalArgumentException("Service interface ["
145: + this .serviceInterface.getName()
146: + "] needs to be implemented by service ["
147: + this .service + "] of class ["
148: + this .service.getClass().getName() + "]");
149: }
150: }
151:
152: /**
153: * Get a proxy for the given service object, implementing the specified
154: * service interface.
155: * <p>Used to export a proxy that does not expose any internals but just
156: * a specific interface intended for remote access. Furthermore, a
157: * {@link RemoteInvocationTraceInterceptor} will be registered (by default).
158: * @return the proxy
159: * @see #setServiceInterface
160: * @see #setRegisterTraceInterceptor
161: * @see RemoteInvocationTraceInterceptor
162: */
163: protected Object getProxyForService() {
164: checkService();
165: checkServiceInterface();
166: ProxyFactory proxyFactory = new ProxyFactory();
167: proxyFactory.addInterface(getServiceInterface());
168: if (isRegisterTraceInterceptor()) {
169: proxyFactory
170: .addAdvice(new RemoteInvocationTraceInterceptor(
171: getExporterName()));
172: }
173: proxyFactory.setTarget(getService());
174: return proxyFactory.getProxy(this .beanClassLoader);
175: }
176:
177: /**
178: * Return a short name for this exporter.
179: * Used for tracing of remote invocations.
180: * <p>Default is the unqualified class name (without package).
181: * Can be overridden in subclasses.
182: * @see #getProxyForService
183: * @see RemoteInvocationTraceInterceptor
184: * @see org.springframework.util.ClassUtils#getShortName
185: */
186: protected String getExporterName() {
187: return ClassUtils.getShortName(getClass());
188: }
189:
190: }
|