1 package org.codehaus.xfire.service.binding;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.lang.reflect.Modifier;
6
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
9 import org.codehaus.xfire.MessageContext;
10 import org.codehaus.xfire.XFireRuntimeException;
11 import org.codehaus.xfire.fault.XFireFault;
12 import org.codehaus.xfire.service.Service;
13 import org.codehaus.xfire.transport.Session;
14
15 /***
16 * An invoker which instantiates classes automatically based on the Service's scope.
17 * The default scope is SCOPE_APPLICATION, which creates once instance to use
18 * for the lifetime of the invoker.
19 *
20 * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
21 * @since Nov 16, 2004
22 */
23 public class ObjectInvoker
24 implements Invoker
25 {
26 private static final Log logger = LogFactory.getLog(ObjectInvoker.class.getName());
27
28 /***
29 * Scope to denote that a single implementation instance should exist for the lifetime
30 * of this invoker and should be reused for every request.
31 */
32 public final static int SCOPE_APPLICATION = 1;
33
34 /***
35 * Scope to denote that a new instance of the service implementation should be created
36 * on every call.
37 */
38 public final static int SCOPE_REQUEST = 3;
39
40 /***
41 * Scope for storing one object per session. An example of a session implementation is
42 * an http session, whereby one object is created per session and stored in the session scope.
43 */
44 public final static int SCOPE_SESSION = 2;
45
46 /***
47 * Constant to denote the implementation class for the service.
48 */
49 public static final String SERVICE_IMPL_CLASS = "xfire.serviceImplClass";
50
51 private int scope = ObjectInvoker.SCOPE_APPLICATION;
52
53 /***
54 * The object if the scope is SCOPE_APPLICATION.
55 */
56 private Object appObj;
57
58 public Object invoke(final Method method, final Object[] params, final MessageContext context)
59 throws XFireFault
60 {
61 try
62 {
63 final Object serviceObject = getServiceObject(context);
64
65 Object[] newParams = params;
66 for (int i = 0; i < method.getParameterTypes().length; i++)
67 {
68 if (method.getParameterTypes()[i].equals(MessageContext.class))
69 {
70 newParams = new Object[params.length+1];
71
72 for (int j = 0; j < newParams.length; j++)
73 {
74 if (j == i)
75 {
76 newParams[j] = context;
77 }
78 else if (j > i)
79 {
80 newParams[j] = params[j-1];
81 }
82 else
83 {
84 newParams[j] = params[j];
85 }
86 }
87 }
88 }
89
90 return method.invoke(serviceObject, newParams);
91 }
92 catch (IllegalArgumentException e)
93 {
94 throw new XFireFault("Illegal argument.", e, XFireFault.SENDER);
95 }
96 catch (InvocationTargetException e)
97 {
98 final Throwable t = e.getTargetException();
99
100 if (t instanceof XFireFault)
101 {
102 throw (XFireFault) t;
103 }
104 else if (t instanceof Exception)
105 {
106 logger.warn("Error invoking service.", t);
107 throw new XFireFault(t, XFireFault.SENDER);
108 }
109 else
110 {
111 logger.warn("Error invoking service.", e);
112 throw new XFireRuntimeException("Error invoking service.", e);
113 }
114 }
115 catch (IllegalAccessException e)
116 {
117 throw new XFireFault("Couldn't access service object.", e, XFireFault.RECEIVER);
118 }
119 }
120
121 /***
122 * Creates and returns a service object depending on the scope.
123 */
124 public Object getServiceObject(final MessageContext context)
125 throws XFireFault
126 {
127 final Service service = context.getService();
128
129 if (scope == ObjectInvoker.SCOPE_APPLICATION)
130 {
131 if (appObj == null)
132 {
133 synchronized (Service.class)
134 {
135 appObj = createServiceObject(service);
136 }
137 }
138 return appObj;
139 }
140 else if (scope == ObjectInvoker.SCOPE_SESSION)
141 {
142 final Session session = context.getSession();
143 final String key = "service." + service.getName();
144
145 Object sessObj = session.get(key);
146 if (sessObj == null)
147 {
148 synchronized (Service.class)
149 {
150 sessObj = createServiceObject(service);
151 session.put(key, sessObj);
152 }
153 }
154 return sessObj;
155 }
156 else if (scope == ObjectInvoker.SCOPE_REQUEST)
157 {
158 return createServiceObject(service);
159 }
160 else
161 {
162 throw new UnsupportedOperationException("Scope " + scope + " is invalid.");
163 }
164 }
165
166 /***
167 * Override this method to control how XFire creates the service object.
168 *
169 * @param service
170 * @return
171 * @throws XFireFault
172 */
173 public Object createServiceObject(final Service service)
174 throws XFireFault
175 {
176 try
177 {
178 Class svcClass = (Class) service.getProperty(ObjectInvoker.SERVICE_IMPL_CLASS);
179
180 if (svcClass == null)
181 {
182 svcClass = service.getServiceInfo().getServiceClass();
183 if(svcClass.isInterface())
184 {
185 throw new XFireFault("ObjectInvoker.SERVICE_IMPL_CLASS not set for interface '" + svcClass.getName() + "'", XFireFault.RECEIVER);
186 }
187 }
188
189 if(svcClass.isInterface())
190 {
191 throw new XFireFault("Service class '" + svcClass.getName() + "' is an interface", XFireFault.RECEIVER);
192 }
193
194 if(Modifier.isAbstract(svcClass.getModifiers()))
195 {
196 throw new XFireFault("Service class '" + svcClass.getName() + "' is abstract", XFireFault.RECEIVER);
197 }
198 return svcClass.newInstance();
199 }
200 catch (InstantiationException e)
201 {
202 throw new XFireFault("Couldn't instantiate service object.", e, XFireFault.RECEIVER);
203 }
204 catch (IllegalAccessException e)
205 {
206 throw new XFireFault("Couldn't access service object.", e, XFireFault.RECEIVER);
207 }
208 }
209
210 public int getScope()
211 {
212 return scope;
213 }
214
215 public void setScope(int scope)
216 {
217 this.scope = scope;
218 }
219 }