1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.openejb.core.stateless;
18
19 import java.lang.reflect.Method;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import javax.ejb.EJBAccessException;
26 import javax.ejb.EJBHome;
27 import javax.ejb.EJBLocalHome;
28 import javax.ejb.EJBLocalObject;
29 import javax.ejb.EJBObject;
30 import javax.interceptor.AroundInvoke;
31 import javax.transaction.TransactionManager;
32
33 import org.apache.openejb.ContainerType;
34 import org.apache.openejb.DeploymentInfo;
35 import org.apache.openejb.OpenEJBException;
36 import org.apache.openejb.ProxyInfo;
37 import org.apache.openejb.InterfaceType;
38 import org.apache.openejb.core.CoreDeploymentInfo;
39 import org.apache.openejb.core.Operation;
40 import org.apache.openejb.core.ThreadContext;
41 import org.apache.openejb.core.ExceptionType;
42 import org.apache.openejb.core.interceptor.InterceptorData;
43 import org.apache.openejb.core.interceptor.InterceptorStack;
44 import org.apache.openejb.core.timer.EjbTimerService;
45 import org.apache.openejb.core.transaction.TransactionContainer;
46 import org.apache.openejb.core.transaction.TransactionContext;
47 import org.apache.openejb.core.transaction.TransactionPolicy;
48 import org.apache.openejb.spi.SecurityService;
49 import org.apache.xbean.finder.ClassFinder;
50
51 /**
52 * @org.apache.xbean.XBean element="statelessContainer"
53 */
54 public class StatelessContainer implements org.apache.openejb.RpcContainer, TransactionContainer {
55
56 private StatelessInstanceManager instanceManager;
57
58 private HashMap<String,DeploymentInfo> deploymentRegistry = new HashMap<String,DeploymentInfo>();
59
60 private Object containerID = null;
61 private TransactionManager transactionManager;
62 private SecurityService securityService;
63
64 public StatelessContainer(Object id, TransactionManager transactionManager, SecurityService securityService, int timeOut, int poolSize, boolean strictPooling) throws OpenEJBException {
65 this.containerID = id;
66 this.transactionManager = transactionManager;
67 this.securityService = securityService;
68
69 instanceManager = new StatelessInstanceManager(transactionManager, securityService, timeOut, poolSize, strictPooling);
70
71 for (DeploymentInfo deploymentInfo : deploymentRegistry.values()) {
72 org.apache.openejb.core.CoreDeploymentInfo di = (org.apache.openejb.core.CoreDeploymentInfo) deploymentInfo;
73 di.setContainer(this);
74 }
75 }
76
77 public synchronized DeploymentInfo [] deployments() {
78 return deploymentRegistry.values().toArray(new DeploymentInfo[deploymentRegistry.size()]);
79 }
80
81 public synchronized DeploymentInfo getDeploymentInfo(Object deploymentID) {
82 String id = (String) deploymentID;
83 return deploymentRegistry.get(id);
84 }
85
86 public ContainerType getContainerType() {
87 return ContainerType.STATELESS;
88 }
89
90 public Object getContainerID() {
91 return containerID;
92 }
93
94 public void deploy(DeploymentInfo info) throws OpenEJBException {
95 CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) info;
96 instanceManager.deploy(deploymentInfo);
97 String id = (String) deploymentInfo.getDeploymentID();
98 synchronized (this) {
99 deploymentRegistry.put(id, deploymentInfo);
100 deploymentInfo.setContainer(this);
101 }
102
103 EjbTimerService timerService = deploymentInfo.getEjbTimerService();
104 if (timerService != null) {
105 timerService.start();
106 }
107 }
108
109 public void undeploy(DeploymentInfo info) {
110 undeploy((CoreDeploymentInfo)info);
111 }
112
113 private void undeploy(CoreDeploymentInfo deploymentInfo) {
114 instanceManager.undeploy(deploymentInfo);
115 EjbTimerService timerService = deploymentInfo.getEjbTimerService();
116 if (timerService != null) {
117 timerService.stop();
118 }
119
120 synchronized (this) {
121 String id = (String) deploymentInfo.getDeploymentID();
122 deploymentInfo.setContainer(null);
123 deploymentInfo.setContainerData(null);
124 deploymentRegistry.remove(id);
125 }
126 }
127
128 /**
129 * @deprecated use invoke signature without 'securityIdentity' argument.
130 */
131 public Object invoke(Object deployID, Method callMethod, Object[] args, Object primKey, Object securityIdentity) throws OpenEJBException {
132 return invoke(deployID, callMethod.getDeclaringClass(), callMethod, args, primKey);
133 }
134
135 public Object invoke(Object deployID, Class callInterface, Method callMethod, Object [] args, Object primKey) throws OpenEJBException {
136 CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) this.getDeploymentInfo(deployID);
137 if (deployInfo == null) throw new OpenEJBException("Deployment does not exist in this container. Deployment(id='"+deployID+"'), Container(id='"+containerID+"')");
138
139 ThreadContext callContext = new ThreadContext(deployInfo, primKey);
140 ThreadContext oldCallContext = ThreadContext.enter(callContext);
141 try {
142 boolean authorized = getSecurityService().isCallerAuthorized(callMethod, deployInfo.getInterfaceType(callInterface));
143 if (!authorized)
144 throw new org.apache.openejb.ApplicationException(new EJBAccessException("Unauthorized Access by Principal Denied"));
145
146 Class declaringClass = callMethod.getDeclaringClass();
147 if (EJBHome.class.isAssignableFrom(declaringClass) || EJBLocalHome.class.isAssignableFrom(declaringClass)) {
148 if (callMethod.getName().startsWith("create")) {
149 return createEJBObject(deployInfo, callMethod);
150 } else
151 return null;// EJBHome.remove( ) and other EJBHome methods are not process by the container
152 } else if (EJBObject.class == declaringClass || EJBLocalObject.class == declaringClass) {
153 return null;// EJBObject.remove( ) and other EJBObject methods are not process by the container
154 }
155
156 Object bean = instanceManager.getInstance(callContext);
157
158 callContext.setCurrentOperation(Operation.BUSINESS);
159 callContext.setCurrentAllowedStates(StatelessContext.getStates());
160
161 Method runMethod = deployInfo.getMatchingBeanMethod(callMethod);
162
163 callContext.set(Method.class, runMethod);
164 callContext.setInvokedInterface(callInterface);
165 Object retValue = _invoke(callInterface, callMethod, runMethod, args, bean, callContext);
166 instanceManager.poolInstance(callContext, bean);
167
168 return retValue;
169
170 } finally {
171 ThreadContext.exit(oldCallContext);
172 }
173 }
174
175 private SecurityService getSecurityService() {
176 return securityService;
177 }
178
179 public StatelessInstanceManager getInstanceManager() {
180 return instanceManager;
181 }
182
183 /**
184 * @deprecated use type-safe {@link #_invoke(Class, java.lang.reflect.Method, java.lang.reflect.Method, Object[], Instance, org.apache.openejb.core.ThreadContext)}
185 */
186 protected Object _invoke(Class callInterface, Method callMethod, Method runMethod, Object[] args, Object object, ThreadContext callContext)
187 throws OpenEJBException {
188 return _invoke(callInterface, callMethod, runMethod, args, (Instance) object, callContext);
189 }
190
191 protected Object _invoke(Class callInterface, Method callMethod, Method runMethod, Object[] args, Instance instance, ThreadContext callContext)
192 throws OpenEJBException {
193
194 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
195 TransactionPolicy txPolicy = deploymentInfo.getTransactionPolicy(callMethod);
196 TransactionContext txContext = new TransactionContext(callContext, getTransactionManager());
197 txContext.callContext = callContext;
198
199 txPolicy.beforeInvoke(instance, txContext);
200
201 Object returnValue = null;
202 try {
203 InterfaceType type = deploymentInfo.getInterfaceType(callInterface);
204 if (type == InterfaceType.SERVICE_ENDPOINT){
205 callContext.setCurrentOperation(Operation.BUSINESS_WS);
206 returnValue = invokeWebService(args, deploymentInfo, runMethod, instance, returnValue);
207 } else {
208 List<InterceptorData> interceptors = deploymentInfo.getMethodInterceptors(runMethod);
209 InterceptorStack interceptorStack = new InterceptorStack(instance.bean, runMethod, Operation.BUSINESS, interceptors, instance.interceptors);
210 returnValue = interceptorStack.invoke(args);
211 }
212 } catch (Throwable re) {// handle reflection exception
213 ExceptionType type = deploymentInfo.getExceptionType(re);
214 if (type == ExceptionType.SYSTEM) {
215 /* System Exception ****************************/
216
217 /**
218 * The bean instance is not put into the pool via instanceManager.poolInstance
219 * and therefore the instance will be garbage collected and destroyed.
220 * For this reason the discardInstance method of the StatelessInstanceManager
221 * does nothing.
222 */
223
224 txPolicy.handleSystemException(re, instance, txContext);
225 } else {
226 /* Application Exception ***********************/
227 instanceManager.poolInstance(callContext, instance);
228
229 txPolicy.handleApplicationException(re, type == ExceptionType.APPLICATION_ROLLBACK, txContext);
230 }
231 } finally {
232
233 txPolicy.afterInvoke(instance, txContext);
234 }
235
236 return returnValue;
237 }
238
239 private Object invokeWebService(Object[] args, CoreDeploymentInfo deploymentInfo, Method runMethod, Instance instance, Object returnValue) throws Exception {
240 if (args.length != 2){
241 throw new IllegalArgumentException("WebService calls must follow format {messageContext, interceptor}.");
242 }
243
244 Object messageContext = args[0];
245
246 // This object will be used as an interceptor in the stack and will be responsible
247 // for unmarshalling the soap message parts into an argument list that will be
248 // used for the actual method invocation.
249 //
250 // We just need to make it an interceptor in the OpenEJB sense and tack it on the end
251 // of our stack.
252 Object interceptor = args[1];
253
254
255 // Add the webservice interceptor to the list of interceptor instances
256 Map<String, Object> interceptors = new HashMap<String, Object>(instance.interceptors);
257 {
258 interceptors.put(interceptor.getClass().getName(), interceptor);
259 }
260
261 // Create an InterceptorData for the webservice interceptor to the list of interceptorDatas for this method
262 List<InterceptorData> interceptorDatas = new ArrayList<InterceptorData>(deploymentInfo.getMethodInterceptors(runMethod));
263 {
264 InterceptorData providerData = new InterceptorData(interceptor.getClass());
265 ClassFinder finder = new ClassFinder(interceptor.getClass());
266 providerData.getAroundInvoke().addAll(finder.findAnnotatedMethods(AroundInvoke.class));
267 interceptorDatas.add(providerData);
268 }
269
270 InterceptorStack interceptorStack = new InterceptorStack(instance.bean, runMethod, Operation.BUSINESS_WS, interceptorDatas, interceptors);
271 Object[] params = new Object[runMethod.getParameterTypes().length];
272 if (messageContext instanceof javax.xml.rpc.handler.MessageContext) {
273 ThreadContext.getThreadContext().set(javax.xml.rpc.handler.MessageContext.class, (javax.xml.rpc.handler.MessageContext) messageContext);
274 returnValue = interceptorStack.invoke((javax.xml.rpc.handler.MessageContext) messageContext, params);
275 } else if (messageContext instanceof javax.xml.ws.handler.MessageContext) {
276 ThreadContext.getThreadContext().set(javax.xml.ws.handler.MessageContext.class, (javax.xml.ws.handler.MessageContext) messageContext);
277 returnValue = interceptorStack.invoke((javax.xml.ws.handler.MessageContext) messageContext, params);
278 }
279 return returnValue;
280 }
281
282 private TransactionManager getTransactionManager() {
283 return transactionManager;
284 }
285
286 protected ProxyInfo createEJBObject(org.apache.openejb.core.CoreDeploymentInfo deploymentInfo, Method callMethod) {
287 return new ProxyInfo(deploymentInfo, null);
288 }
289
290 public void discardInstance(Object instance, ThreadContext context) {
291 instanceManager.discardInstance(context, instance);
292 }
293 }