001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *                               ***
008 *
009 *   Community License: GPL 3.0
010 *
011 *   This file is free software: you can redistribute it and/or modify
012 *   it under the terms of the GNU General Public License as published
013 *   by the Free Software Foundation, either version 3 of the License,
014 *   or (at your option) any later version.
015 *
016 *   This file is distributed in the hope that it will be useful, but
017 *   WITHOUT ANY WARRANTY; without even the implied warranty of
018 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019 *   GNU General Public License for more details.
020 *
021 *   You should have received a copy of the GNU General Public License
022 *   along with this program. If not, see <http://www.gnu.org/licenses/>.
023 *
024 *                               ***
025 *
026 *   Available Commercial License: GraniteDS SLA 1.0
027 *
028 *   This is the appropriate option if you are creating proprietary
029 *   applications and you are not prepared to distribute and share the
030 *   source code of your application under the GPL v3 license.
031 *
032 *   Please visit http://www.granitedataservices.com/license for more
033 *   details.
034 */
035package org.granite.client.tide.impl;
036
037import java.lang.reflect.InvocationHandler;
038import java.lang.reflect.Method;
039import java.util.Arrays;
040import java.util.List;
041import java.util.concurrent.Future;
042
043import org.granite.client.messaging.RemoteAlias;
044import org.granite.client.messaging.events.FaultEvent;
045import org.granite.client.messaging.events.IssueEvent;
046import org.granite.client.messaging.events.ResultEvent;
047import org.granite.client.tide.Context;
048import org.granite.client.tide.ContextAware;
049import org.granite.client.tide.NameAware;
050import org.granite.client.tide.PropertyHolder;
051import org.granite.client.tide.server.ArgumentPreprocessor;
052import org.granite.client.tide.server.Component;
053import org.granite.client.tide.server.ComponentListener;
054import org.granite.client.tide.server.InvocationInterceptor;
055import org.granite.client.tide.server.ServerSession;
056import org.granite.client.tide.server.TideResponder;
057import org.granite.client.tide.server.TideResultEvent;
058import org.granite.logging.Logger;
059
060/**
061 * Default implementation of remote component proxy
062 * Generated typesafe remote service proxies should extend this class
063 *
064 * Component proxies are meant to be defined in a DI container (Spring/CDI) or directly in the Tide context
065 * <pre>
066 * {@code
067 * Component myComponent = tideContext.set("myComponent", new ComponentImpl(serverSession));
068 * myComponent.call("myMethod", arg1, arg2);
069 * }
070 * </pre>
071 *
072 * @author William DRAI
073 */
074public class ComponentImpl implements Component, ContextAware, NameAware, InvocationHandler {
075    
076        private static final Logger log = Logger.getLogger(ComponentImpl.class);
077
078
079    private String name;
080    private Context context;
081    private final ServerSession serverSession;
082
083
084    /**
085     * Default constructor necessary for testing and CDI proxying...
086     */
087    public ComponentImpl() {   
088        this.serverSession = null;
089    }
090
091    /**
092     * Create a new proxy attached to the specified server session
093     * @param serverSession server session
094     */
095    public ComponentImpl(ServerSession serverSession) {
096        this.serverSession = serverSession;
097    }
098
099    /**
100     * Set the remote name of the component
101     * By default the component will use its name in its owning context as the remote name
102     * @param name name
103     */
104    public void setName(String name) {
105        this.name = name;
106    }
107    /**
108     * Remote name of the component
109     * @return name
110     */
111    public String getName() {
112        return name;
113    }
114
115    /**
116     * Set the context where the component is set
117     * @param context Tide context
118     */
119    public void setContext(Context context) {
120        this.context = context;
121    }
122
123    /**
124     * Context where the component is set
125     * @return Tide context
126     */
127    protected Context getContext() {
128        return context;
129    }
130
131    /**
132     * Server session to which the component is attached
133     * @return server session
134     */
135    protected ServerSession getServerSession() {
136        return serverSession;
137    }
138    
139    
140    @SuppressWarnings("unchecked")
141    public <T> Future<T> call(String operation, Object... args) {
142        Context context = this.context;
143        
144        if (args != null && args.length > 0 && args[0] instanceof Context) {
145            context = (Context)args[0];
146            Object[] newArgs = new Object[args.length-1];
147            for (int i = 1; i < args.length-1; i++)
148                newArgs[i-1] = args[i];
149            args = newArgs;
150        }
151        
152        return (Future<T>)callComponent(context, operation, args);
153    }
154
155    
156        @Override
157        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
158                if (!method.getDeclaringClass().isAnnotationPresent(RemoteAlias.class))
159                        return method.invoke(proxy, args);
160                
161                return callComponent(getContext(), method.getName(), args);
162        }
163
164
165    /**
166     *  Calls a remote component
167     * 
168     *  @param context the source context
169     *  @param operation name of the called metho
170     *  @param args method arguments
171     *
172     *  @return future returning the result of the call
173     */
174    @SuppressWarnings("unchecked")
175        protected <T> Future<T> callComponent(Context context, String operation, Object[] args) {
176        context.checkValid();
177        
178        log.debug("callComponent %s.%s", getName(), operation);
179        
180        TideResponder<T> responder = null;
181        if (args != null && args.length > 0 && args[args.length-1] instanceof TideResponder) {
182                responder = (TideResponder<T>)args[args.length-1];
183            Object[] newArgs = new Object[args.length-1];
184            for (int i = 0; i < args.length-1; i++)
185                newArgs[i] = args[i];
186            args = newArgs;
187        }
188        
189                // Force generation of uids by merging all arguments in the current context
190        context.getEntityManager().initMerge();
191        List<Object> argsList = Arrays.asList(args);
192                for (int i = 0; i < args.length; i++) {
193                        if (argsList.get(i) instanceof PropertyHolder)
194                                argsList.set(i, ((PropertyHolder)args[i]).getObject());
195                }
196                argsList = (List<Object>)context.getEntityManager().mergeExternalData(argsList);
197                for (int i = 0; i < args.length; i++)
198                        args[i] = argsList.get(i);
199                
200        Method method = null;
201        // TODO: improve method matching
202        for (Method m : getClass().getMethods()) {
203            if (m.getName().equals(operation) && m.getParameterTypes().length == args.length) {
204                method = m;
205                break;
206            }
207        }
208        if (method != null) {
209            // Call argument preprocessors if necessary before sending arguments to server
210            ArgumentPreprocessor[] apps = context.allByType(ArgumentPreprocessor.class);
211            if (apps != null) {
212                for (ArgumentPreprocessor app : apps)
213                    args = app.preprocess(method, args);
214            }
215        }
216        
217        return invoke(context, operation, args, responder);
218    }
219
220    /**
221     * Execute the invocation of the remote component
222     * @param context the source context
223     * @param operation method name
224     * @param args method arguments
225     * @param tideResponder Tide responder for the remote call
226     * @param <T> expected type of result
227     * @return future triggered asynchronously when response is received
228     */
229    @SuppressWarnings({"unchecked", "rawtypes"})
230    protected <T> Future<T> invoke(Context context, String operation, Object[] args, TideResponder<T> tideResponder) {
231        log.debug("invokeComponent %s > %s.%s", context.getContextId(), getName() != null ? getName() : getClass().getName(), operation);
232        
233        ComponentListener.Handler handler = new ComponentListener.Handler<T>() {
234                        @Override
235            public Runnable result(Context context, ResultEvent event, Object info, String componentName,
236                    String operation, TideResponder<T> tideResponder, ComponentListener<T> componentListener) {
237                return new ResultHandler(serverSession, context, componentName, operation, event, info, tideResponder, componentListener);
238            }
239            
240            @Override
241            public Runnable fault(Context context, FaultEvent event, Object info, String componentName,
242                    String operation, TideResponder<T> tideResponder, ComponentListener<T> componentListener) {
243                return new FaultHandler(serverSession, context, componentName, operation, event, info, tideResponder, componentListener);
244            }
245            
246            @Override
247            public Runnable issue(Context context, IssueEvent event, Object info, String componentName,
248                    String operation, TideResponder<T> tideResponder, ComponentListener<T> componentListener) {
249                return new FaultHandler(serverSession, context, componentName, operation, event, info, tideResponder, componentListener);
250            }
251        };
252        ComponentListener<T> componentListener = new ComponentListenerImpl<T>(context, handler, this, operation, args, null, tideResponder);
253        
254        InvocationInterceptor[] interceptors = context.allByType(InvocationInterceptor.class);
255        if (interceptors != null) {
256            for (InvocationInterceptor interceptor : interceptors)
257                interceptor.beforeInvocation(context, this, operation, args, componentListener);
258        }
259        
260        context.getContextManager().destroyFinishedContexts();
261        
262//        // Force generation of uids by merging all arguments in the current context
263//        for (int i = 0; i < args.length; i++) {
264//            if (args[i] instanceof PropertyHolder)
265//                args[i] = ((PropertyHolder)args[i]).getObject();
266//            args[i] = entityManager.mergeExternal(args[i], null);
267//        }
268//        
269//        // Call argument preprocessors before sending arguments to server
270//        var method:Method = Type.forInstance(component).getInstanceMethodNoCache(op);
271//        for each (var app:IArgumentPreprocessor in allByType(IArgumentPreprocessor, true))
272//            componentResponder.args = app.preprocess(method, args);
273        
274        return componentListener.invoke(serverSession);
275    }
276
277    /**
278     * Create a result event for this component
279     * @param result result to wrap in an event
280     * @param <T> expected type of the result
281     * @return result event
282     */
283    public <T> TideResultEvent<T> newResultEvent(T result) {
284        return new TideResultEvent<T>(getContext(), getServerSession(), null, result);
285    }
286    
287}