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.server.ejbd;
18
19 import java.io.ObjectInputStream;
20 import java.io.ObjectOutputStream;
21 import java.rmi.RemoteException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24
25 import org.apache.openejb.DeploymentInfo;
26 import org.apache.openejb.ProxyInfo;
27 import org.apache.openejb.RpcContainer;
28 import org.apache.openejb.client.EJBHomeProxyHandle;
29 import org.apache.openejb.client.EJBObjectProxyHandle;
30 import org.apache.openejb.client.EJBRequest;
31 import org.apache.openejb.client.EJBResponse;
32 import org.apache.openejb.client.RequestMethodConstants;
33 import org.apache.openejb.client.ResponseCodes;
34 import org.apache.openejb.client.ThrowableArtifact;
35 import org.apache.openejb.loader.SystemInstance;
36 import org.apache.openejb.spi.SecurityService;
37 import org.apache.openejb.util.LogCategory;
38 import org.apache.openejb.util.Logger;
39
40 class EjbRequestHandler {
41 public static final ServerSideResolver SERVER_SIDE_RESOLVER = new ServerSideResolver();
42
43 private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB_SERVER_REMOTE.createChild("ejb"), "org.apache.openejb.server.util.resources");
44 private final EjbDaemon daemon;
45
46 private final ClusterableRequestHandler clusterableRequestHandler;
47
48 EjbRequestHandler(EjbDaemon daemon) {
49 this.daemon = daemon;
50
51 clusterableRequestHandler = newClusterableRequestHandler();
52 }
53
54 protected BasicClusterableRequestHandler newClusterableRequestHandler() {
55 return new BasicClusterableRequestHandler();
56 }
57
58 public void processRequest(ObjectInputStream in, ObjectOutputStream out) {
59 // Setup the client proxy replacement to replace
60 // the proxies with the IntraVM proxy implementations
61 EJBHomeProxyHandle.resolver.set(SERVER_SIDE_RESOLVER);
62 EJBObjectProxyHandle.resolver.set(SERVER_SIDE_RESOLVER);
63
64 EJBRequest req = new EJBRequest();
65 EJBResponse res = new EJBResponse();
66
67 try {
68 req.readExternal(in);
69 } catch (Throwable t) {
70 replyWithFatalError(out, t, "Error caught during request processing");
71 return;
72 }
73
74 CallContext call = null;
75 DeploymentInfo di = null;
76 RpcContainer c = null;
77
78 try {
79 di = this.daemon.getDeployment(req);
80 } catch (RemoteException e) {
81 replyWithFatalError
82 (out, e, "No such deployment");
83 return;
84 /*
85 logger.warn( req + "No such deployment: "+e.getMessage());
86 res.setResponse( EJB_SYS_EXCEPTION, e);
87 res.writeExternal( out );
88 return;
89 */
90 } catch (Throwable t) {
91 replyWithFatalError
92 (out, t, "Unkown error occured while retrieving deployment");
93 return;
94 }
95
96 // Need to set this for deserialization of the body
97 ClassLoader classLoader = di.getBeanClass().getClassLoader();
98 Thread.currentThread().setContextClassLoader(classLoader);
99
100 try {
101 req.getBody().readExternal(in);
102 } catch (Throwable t) {
103 replyWithFatalError(out, t, "Error caught during request processing");
104 return;
105 }
106
107 try {
108 call = CallContext.getCallContext();
109 call.setEJBRequest(req);
110 call.setDeploymentInfo(di);
111 } catch (Throwable t) {
112 replyWithFatalError(out, t, "Unable to set the thread context for this request");
113 return;
114 }
115
116 SecurityService securityService = SystemInstance.get().getComponent(SecurityService.class);
117 try {
118 Object clientIdentity = req.getClientIdentity();
119 if (clientIdentity != null) securityService.associate(clientIdentity);
120 } catch (Throwable t) {
121 replyWithFatalError(out, t, "Security system failed to associate thread with the thread");
122 return;
123 }
124
125 try {
126 switch (req.getRequestMethod()) {
127 // Remote interface methods
128 case RequestMethodConstants.EJB_OBJECT_BUSINESS_METHOD:
129 doEjbObject_BUSINESS_METHOD(req, res);
130 updateServer(req, res);
131 break;
132
133 // Home interface methods
134 case RequestMethodConstants.EJB_HOME_CREATE:
135 doEjbHome_CREATE(req, res);
136 updateServer(req, res);
137 break;
138
139 // Home interface methods
140 case RequestMethodConstants.EJB_HOME_METHOD:
141 doEjbHome_METHOD(req, res);
142 updateServer(req, res);
143 break;
144
145 case RequestMethodConstants.EJB_HOME_FIND:
146 doEjbHome_FIND(req, res);
147 updateServer(req, res);
148 break;
149
150 // javax.ejb.EJBObject methods
151 case RequestMethodConstants.EJB_OBJECT_GET_EJB_HOME:
152 doEjbObject_GET_EJB_HOME(req, res);
153 updateServer(req, res);
154 break;
155
156 case RequestMethodConstants.EJB_OBJECT_GET_HANDLE:
157 doEjbObject_GET_HANDLE(req, res);
158 updateServer(req, res);
159 break;
160
161 case RequestMethodConstants.EJB_OBJECT_GET_PRIMARY_KEY:
162 doEjbObject_GET_PRIMARY_KEY(req, res);
163 updateServer(req, res);
164 break;
165
166 case RequestMethodConstants.EJB_OBJECT_IS_IDENTICAL:
167 doEjbObject_IS_IDENTICAL(req, res);
168 updateServer(req, res);
169 break;
170
171 case RequestMethodConstants.EJB_OBJECT_REMOVE:
172 doEjbObject_REMOVE(req, res);
173 break;
174
175 // javax.ejb.EJBHome methods
176 case RequestMethodConstants.EJB_HOME_GET_EJB_META_DATA:
177 doEjbHome_GET_EJB_META_DATA(req, res);
178 updateServer(req, res);
179 break;
180
181 case RequestMethodConstants.EJB_HOME_GET_HOME_HANDLE:
182 doEjbHome_GET_HOME_HANDLE(req, res);
183 updateServer(req, res);
184 break;
185
186 case RequestMethodConstants.EJB_HOME_REMOVE_BY_HANDLE:
187 doEjbHome_REMOVE_BY_HANDLE(req, res);
188 break;
189
190 case RequestMethodConstants.EJB_HOME_REMOVE_BY_PKEY:
191 doEjbHome_REMOVE_BY_PKEY(req, res);
192 break;
193 }
194
195 } catch (org.apache.openejb.InvalidateReferenceException e) {
196 res.setResponse(ResponseCodes.EJB_SYS_EXCEPTION, new ThrowableArtifact(e.getRootCause()));
197 } catch (org.apache.openejb.ApplicationException e) {
198 res.setResponse(ResponseCodes.EJB_APP_EXCEPTION, new ThrowableArtifact(e.getRootCause()));
199 } catch (org.apache.openejb.SystemException e) {
200 res.setResponse(ResponseCodes.EJB_ERROR, new ThrowableArtifact(e.getRootCause()));
201
202 logger.fatal(req + ": OpenEJB encountered an unknown system error in container: ", e);
203 } catch (java.lang.Throwable t) {
204 // todo this causes the response to be written twice but the code below
205 replyWithFatalError
206 (out, t, "Unknown error in container");
207 return;
208 } finally {
209 if (logger.isDebugEnabled()){
210 try {
211 logger.debug("EJB REQUEST: "+req+" -- RESPONSE: " + res);
212 } catch (Exception justInCase) {}
213 }
214 try {
215 res.writeExternal(out);
216 } catch (java.io.IOException ie) {
217 logger.fatal("Couldn't write EjbResponse to output stream", ie);
218 }
219 securityService.disassociate();
220 call.reset();
221 EJBHomeProxyHandle.resolver.set(null);
222 EJBObjectProxyHandle.resolver.set(null);
223
224 }
225 }
226
227 protected void updateServer(EJBRequest req, EJBResponse res) {
228 CallContext callContext = CallContext.getCallContext();
229 DeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
230 clusterableRequestHandler.updateServer(deploymentInfo, req, res);
231 }
232
233 protected void doEjbObject_BUSINESS_METHOD(EJBRequest req, EJBResponse res) throws Exception {
234
235 CallContext call = CallContext.getCallContext();
236 RpcContainer c = (RpcContainer) call.getDeploymentInfo().getContainer();
237
238 Object result = c.invoke(req.getDeploymentId(),
239 req.getInterfaceClass(), req.getMethodInstance(),
240 req.getMethodParameters(),
241 req.getPrimaryKey()
242 );
243
244 res.setResponse(ResponseCodes.EJB_OK, result);
245 }
246
247 protected void doEjbHome_METHOD(EJBRequest req, EJBResponse res) throws Exception {
248
249 CallContext call = CallContext.getCallContext();
250 RpcContainer c = (RpcContainer) call.getDeploymentInfo().getContainer();
251
252 Object result = c.invoke(req.getDeploymentId(),
253 req.getInterfaceClass(), req.getMethodInstance(),
254 req.getMethodParameters(),
255 req.getPrimaryKey()
256 );
257
258 res.setResponse(ResponseCodes.EJB_OK, result);
259 }
260
261 protected void doEjbHome_CREATE(EJBRequest req, EJBResponse res) throws Exception {
262
263 CallContext call = CallContext.getCallContext();
264 RpcContainer c = (RpcContainer) call.getDeploymentInfo().getContainer();
265
266 Object result = c.invoke(req.getDeploymentId(),
267 req.getInterfaceClass(), req.getMethodInstance(),
268 req.getMethodParameters(),
269 req.getPrimaryKey()
270 );
271
272 if (result instanceof ProxyInfo) {
273 ProxyInfo info = (ProxyInfo) result;
274 res.setResponse(ResponseCodes.EJB_OK, info.getPrimaryKey());
275 } else {
276
277 result = new RemoteException("The bean is not EJB compliant. The bean should be created or and exception should be thrown.");
278 logger.error(req + "The bean is not EJB compliant. The bean should be created or and exception should be thrown.");
279 res.setResponse(ResponseCodes.EJB_SYS_EXCEPTION, new ThrowableArtifact((Throwable) result));
280 }
281 }
282
283 protected void doEjbHome_FIND(EJBRequest req, EJBResponse res) throws Exception {
284
285 CallContext call = CallContext.getCallContext();
286 RpcContainer c = (RpcContainer) call.getDeploymentInfo().getContainer();
287
288 Object result = c.invoke(req.getDeploymentId(),
289 req.getInterfaceClass(), req.getMethodInstance(),
290 req.getMethodParameters(),
291 req.getPrimaryKey()
292 );
293
294 /* Multiple instances found */
295 if (result instanceof Collection) {
296
297 Object [] primaryKeys = ((Collection) result).toArray();
298
299 for (int i = 0; i < primaryKeys.length; i++) {
300 ProxyInfo proxyInfo = ((ProxyInfo) primaryKeys[i]);
301 if (proxyInfo == null) {
302 primaryKeys[i] = null;
303 } else {
304 primaryKeys[i] = proxyInfo.getPrimaryKey();
305 }
306 }
307
308 res.setResponse(ResponseCodes.EJB_OK_FOUND_COLLECTION, primaryKeys);
309
310 } else if (result instanceof java.util.Enumeration) {
311
312 java.util.Enumeration resultAsEnum = (java.util.Enumeration) result;
313 java.util.List<Object> listOfPKs = new ArrayList<Object>();
314 while (resultAsEnum.hasMoreElements()) {
315 ProxyInfo proxyInfo = ((ProxyInfo) resultAsEnum.nextElement());
316 if (proxyInfo == null) {
317 listOfPKs.add(null);
318 } else {
319 listOfPKs.add(proxyInfo.getPrimaryKey());
320 }
321 }
322
323 res.setResponse(ResponseCodes.EJB_OK_FOUND_ENUMERATION, listOfPKs.toArray(new Object[listOfPKs.size()]));
324 /* Single instance found */
325 } else if (result instanceof ProxyInfo) {
326 ProxyInfo proxyInfo = ((ProxyInfo) result);
327 result = proxyInfo.getPrimaryKey();
328 res.setResponse(ResponseCodes.EJB_OK_FOUND, result);
329 } else if (result == null) {
330 res.setResponse(ResponseCodes.EJB_OK_FOUND, null);
331 } else {
332
333 final String message = "The bean is not EJB compliant. " +
334 "The finder method [" + req.getMethodInstance().getName() + "] is declared " +
335 "to return neither Collection nor the Remote Interface, " +
336 "but [" + result.getClass().getName() + "]";
337 result = new RemoteException(message);
338 logger.error(req + " " + message);
339 res.setResponse(ResponseCodes.EJB_SYS_EXCEPTION, result);
340 }
341 }
342
343 protected void doEjbObject_GET_EJB_HOME(EJBRequest req, EJBResponse res) throws Exception {
344 checkMethodAuthorization(req, res);
345 }
346
347 protected void doEjbObject_GET_HANDLE(EJBRequest req, EJBResponse res) throws Exception {
348 checkMethodAuthorization(req, res);
349 }
350
351 protected void doEjbObject_GET_PRIMARY_KEY(EJBRequest req, EJBResponse res) throws Exception {
352 checkMethodAuthorization(req, res);
353 }
354
355 protected void doEjbObject_IS_IDENTICAL(EJBRequest req, EJBResponse res) throws Exception {
356 checkMethodAuthorization(req, res);
357 }
358
359 protected void doEjbObject_REMOVE(EJBRequest req, EJBResponse res) throws Exception {
360
361 CallContext call = CallContext.getCallContext();
362 RpcContainer c = (RpcContainer) call.getDeploymentInfo().getContainer();
363
364 Object result = c.invoke(req.getDeploymentId(),
365 req.getInterfaceClass(), req.getMethodInstance(),
366 req.getMethodParameters(),
367 req.getPrimaryKey()
368 );
369
370 res.setResponse(ResponseCodes.EJB_OK, null);
371 }
372
373 protected void doEjbHome_GET_EJB_META_DATA(EJBRequest req, EJBResponse res) throws Exception {
374 checkMethodAuthorization(req, res);
375 }
376
377 protected void doEjbHome_GET_HOME_HANDLE(EJBRequest req, EJBResponse res) throws Exception {
378 checkMethodAuthorization(req, res);
379 }
380
381 protected void doEjbHome_REMOVE_BY_HANDLE(EJBRequest req, EJBResponse res) throws Exception {
382
383 CallContext call = CallContext.getCallContext();
384 RpcContainer c = (RpcContainer) call.getDeploymentInfo().getContainer();
385
386 Object result = c.invoke(req.getDeploymentId(),
387 req.getInterfaceClass(), req.getMethodInstance(),
388 req.getMethodParameters(),
389 req.getPrimaryKey()
390 );
391
392 res.setResponse(ResponseCodes.EJB_OK, null);
393 }
394
395 protected void doEjbHome_REMOVE_BY_PKEY(EJBRequest req, EJBResponse res) throws Exception {
396
397 CallContext call = CallContext.getCallContext();
398 RpcContainer c = (RpcContainer) call.getDeploymentInfo().getContainer();
399
400 Object result = c.invoke(req.getDeploymentId(),
401 req.getInterfaceClass(), req.getMethodInstance(),
402 req.getMethodParameters(),
403 req.getPrimaryKey()
404 );
405
406 res.setResponse(ResponseCodes.EJB_OK, null);
407 }
408
409 protected void checkMethodAuthorization(EJBRequest req, EJBResponse res) throws Exception {
410 res.setResponse(ResponseCodes.EJB_OK, null);
411 // SecurityService sec = SystemInstance.get().getComponent(SecurityService.class);
412 // CallContext caller = CallContext.getCallContext();
413 // DeploymentInfo di = caller.getDeploymentInfo();
414 //
415 // if (sec.isCallerAuthorized(req.getMethodInstance(), null)) {
416 // res.setResponse(ResponseCodes.EJB_OK, null);
417 // } else {
418 // logger.info(req + "Unauthorized Access by Principal Denied");
419 // res.setResponse(ResponseCodes.EJB_APP_EXCEPTION, new ThrowableArtifact(new EJBAccessException("Unauthorized Access by Principal Denied")));
420 // }
421 }
422
423 private void replyWithFatalError(ObjectOutputStream out, Throwable error, String message) {
424 logger.fatal(message, error);
425 RemoteException re = new RemoteException
426 ("The server has encountered a fatal error: " + message + " " + error, error);
427 EJBResponse res = new EJBResponse();
428 res.setResponse(ResponseCodes.EJB_ERROR, new ThrowableArtifact(re));
429 try {
430 res.writeExternal(out);
431 } catch (java.io.IOException ie) {
432 logger.error("Failed to write to EJBResponse", ie);
433 }
434 }
435 }