001: package org.jboss.seam.remoting.gwt;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.apache.log4j.Logger;
025: import org.drools.brms.client.rpc.SecurityService;
026: import org.drools.brms.server.ServiceImplementation;
027: import org.drools.brms.server.security.SecurityServiceImpl;
028: import org.drools.brms.server.util.TestEnvironmentSessionHelper;
029: import org.drools.repository.RulesRepository;
030: import org.jboss.seam.Component;
031: import org.jboss.seam.annotations.WebRemote;
032: import org.jboss.seam.contexts.Contexts;
033:
034: /**
035: * This class adapts GWT RPC mechanism to Seam actions.
036: *
037: * @author Michael Neale
038: */
039: public class GWTToSeamAdapter {
040:
041: /** A very simple cache of previously looked up methods */
042: static final Map METHOD_CACHE = new HashMap();
043: private static final Logger log = Logger
044: .getLogger(GWTToSeamAdapter.class);
045:
046: /**
047: * Call the service.
048: * @param serviceIntfName The interface name - this will be the fully qualified name of the remote service interface as
049: * understood by GWT. This correlates to a component name in seam.
050: * @param methodName The method name of the service being invoked.
051: * @param paramTypes The types of parameters - needed for method lookup for polymorphism.
052: * @param args The values to be passed to the service method.
053: * @return A populated ReturnedObject - the returned object payload may be null, but the type will not be.
054: * @throws InvocationTargetException
055: * @throws IllegalAccessException
056: */
057: public ReturnedObject callWebRemoteMethod(String serviceIntfName,
058: String methodName, Class[] paramTypes, Object[] args)
059: throws InvocationTargetException, IllegalAccessException,
060: SecurityException {
061:
062: Object component = getServiceComponent(serviceIntfName);
063: Class clz = component.getClass();
064:
065: Method method = getMethod(serviceIntfName, methodName, clz,
066: paramTypes);
067:
068: Object result = method.invoke(component, args);
069: return new ReturnedObject(method.getReturnType(), result);
070: }
071:
072: /**
073: * Get the method on the class, including walking up the class hierarchy if needed.
074: * Methods have to be marked as "@WebRemote" to be allowed
075: * @param methodName
076: * @param clz
077: * @param paramTypes
078: * @return
079: */
080: private Method getMethod(String serviceName, String methodName,
081: Class clz, Class[] paramTypes) {
082: String key = getKey(serviceName, methodName, paramTypes);
083: if (METHOD_CACHE.containsKey(key)) {
084: return (Method) METHOD_CACHE.get(key);
085: } else {
086: try {
087: synchronized (METHOD_CACHE) {
088: Method m = findMethod(clz, methodName, paramTypes);
089: if (m == null)
090: throw new NoSuchMethodException();
091: METHOD_CACHE.put(key, m);
092: return m;
093: }
094:
095: } catch (NoSuchMethodException e) {
096: throw new SecurityException(
097: "Unable to access a service method called ["
098: + methodName
099: + "] on class ["
100: + clz.getName()
101: + "] without the @WebRemote attribute. "
102: + "This may be a hack attempt, or someone simply neglected to use the @WebRemote attribute to indicate a method as"
103: + " remotely accessible.");
104: }
105: }
106: }
107:
108: private String getKey(String serviceName, String methodName,
109: Class[] paramTypes) {
110: if (paramTypes == null) {
111: return serviceName + "." + methodName;
112: } else {
113: String pTypes = "";
114: for (int i = 0; i < paramTypes.length; i++) {
115: pTypes += paramTypes[i].getName();
116: }
117: return serviceName + "." + methodName + "(" + pTypes + ")";
118: }
119:
120: }
121:
122: /**
123: * Recurse up the class hierarchy, looking for a compatible method that is marked as "@WebRemote".
124: * If one is not found (or we hit Object.class) then we barf - basically trust nothing from the client
125: * other then what we want to allow them to call.
126: */
127: private Method findMethod(Class clz, String methodName,
128: Class[] paramTypes) throws NoSuchMethodException {
129: if (clz == Object.class) {
130: return null;
131: } else {
132: Method m = clz.getMethod(methodName, paramTypes);
133: if (isWebRemoteAnnotated(m)) {
134: return m;
135: } else {
136: return findMethod(clz.getSuperclass(), methodName,
137: paramTypes);
138: }
139: }
140: }
141:
142: /**
143: * Only allow methods annotated with @WebRemote for security reasons.
144: */
145: private boolean isWebRemoteAnnotated(Method method) {
146: if (method == null)
147: return false;
148: return method.getAnnotation(WebRemote.class) != null;
149: }
150:
151: /**
152: * Return the service component that has been bound to the given name.
153: */
154: protected Object getServiceComponent(String serviceIntfName) {
155: if (Contexts.isApplicationContextActive()) {
156: log
157: .debug("Running in seam mode multi user and authentication enabled");
158: return Component.getInstance(serviceIntfName);
159: } else {
160:
161: //MN: NOTE THIS IS MY HACKERY TO GET IT WORKING IN GWT HOSTED MODE.
162: //THIS IS ALL THAT IS NEEDED.
163: log
164: .debug("WARNING: RUNNING IN NON SEAM MODE SINGLE USER MODE - ONLY FOR TESTING AND DEBUGGING !!!!!");
165: if (serviceIntfName.equals(SecurityService.class.getName())) {
166: return new SecurityServiceImpl();
167: }
168: ServiceImplementation impl = new ServiceImplementation();
169:
170: try {
171: impl.repository = new RulesRepository(
172: TestEnvironmentSessionHelper.getSession(false));
173: return impl;
174: } catch (Exception e) {
175: throw new IllegalStateException(
176: "Unable to launch debug mode...");
177: }
178:
179: }
180: }
181:
182: /**
183: * This is used for returning results to the GWT service endpoint.
184: * The class is needed even if the result is null.
185: * a void.class responseType is perfectly acceptable.
186: * @author Michael Neale
187: */
188: static class ReturnedObject {
189: public ReturnedObject(Class type, Object result) {
190: this .returnType = type;
191: this .returnedObject = result;
192: }
193:
194: public Class returnType;
195: public Object returnedObject;
196: }
197:
198: }
|