Package org.butor.web.servlet

Source Code of org.butor.web.servlet.AjaxServiceCallerFactory

/*******************************************************************************
* Copyright 2013 butor.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.butor.web.servlet;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.butor.json.JsonHelper;
import org.butor.json.JsonServiceRequest;
import org.butor.json.service.Context;
import org.butor.json.service.ServiceCallerFactory;
import org.butor.utils.Message;
import org.butor.utils.Message.MessageType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import com.google.common.base.Preconditions;
import com.google.common.reflect.Reflection;
/**
* Factory class that return a proxy of the service interface
* that calls a remote service
*
* This eliminate the need of creating a new service class
* that use a service caller to invoke remote services with identical
* name and signature (pass through services).
*
* @author asawan
*
* @param <T>
*/
public class AjaxServiceCallerFactory<T> implements InitializingBean, FactoryBean<T>{
  protected Logger _logger = LoggerFactory.getLogger(getClass());
  private Class<T> serviceInterface;
  private T proxy;

  private String namespace;
  private String url;
  private ServiceCallerFactory scf;
  private int maxPayloadLengthToLog = -1;
  private Set<String> servicesToNotLogArgs = Collections.emptySet();

  @Override
  public void afterPropertiesSet() throws Exception {
    Preconditions.checkNotNull(namespace,"namespace is mandatory");
    Preconditions.checkNotNull(url, "url is mandatory");
    Preconditions.checkNotNull(serviceInterface, "serviceInterface is mandatory");
    scf = createServiceCallerFactory(namespace, url, maxPayloadLengthToLog, servicesToNotLogArgs);
    proxy = buildProxy();
  }

  public ServiceCallerFactory createServiceCallerFactory(String namespace,
    String url, int maxPayloadLengthToLog, Set<String> servicesToNotLogArgs) {
    return new ServiceCallerFactory(namespace, url, maxPayloadLengthToLog, servicesToNotLogArgs);
  }

  @Override
  public T getObject() throws Exception {
    return proxy;
  }

  @Override
  public Class<?> getObjectType() {
    return serviceInterface;
  }

  @Override
  public boolean isSingleton() {
    return true;
  }

  public void setServiceInterface(Class<T> serviceInterface) {
    this.serviceInterface = serviceInterface;
  }

  private String buildKey(String methodName_, int nbArgs_) {
    return String.format("%s.%d", methodName_, nbArgs_);
  }

  private T buildProxy() {
    final T serviceCaller = scf.createServiceCaller(serviceInterface);
    final Map<String, Method> methodsMap = new HashMap<String, Method>();
    for (Method m : serviceCaller.getClass().getMethods()) {
      String serviceName = m.getName();
      if (Modifier.isPublic(m.getModifiers())) {
        Class<?>[] argsTypes = m.getParameterTypes();
        if (argsTypes.length > 0
            && Context.class.isAssignableFrom(argsTypes[0])) {
          String key = buildKey(serviceName, argsTypes.length);
          Preconditions.checkArgument(
            !methodsMap.containsKey(key),
            "Service with first parameter Context and same name and same number of args %s.%s",
            serviceInterface, serviceName);
          methodsMap.put(key, m);
        }
      }
    }

    final JsonHelper jsh = new JsonHelper();

    InvocationHandler ih = new InvocationHandler() {
      //private Logger logger = LoggerFactory.getLogger(this.getClass());
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        AjaxContext actx = (AjaxContext)args[0];
        try {
          String key = buildKey(method.getName(), args.length);
          Method target = methodsMap.get(key);
          if (target == null) {
            //TODO
            return null;
          }

          Context ctx = scf.getContext(actx.getRequest().getUserId(),
              actx.getRequest().getSessionId(),
              actx.getRequest().getLang(), actx.getResponseHandler());

          Object[] serviceArgs = new Object[args.length];
          serviceArgs[0] = ctx;
          for (int ii=1; ii<args.length;ii++)
            serviceArgs[ii] = args[ii];

          JsonServiceRequest jsr = (JsonServiceRequest)ctx.getRequest();
          String sargs = jsh.serialize(serviceArgs);
          //logger.info("calling service {} with args {}", method.getName(), sargs);
          jsr.setService(method.getName());
          jsr.setServiceArgsJson(sargs);

          return target.invoke(serviceCaller, serviceArgs);
        } catch (Exception ex) {
          actx.getResponseHandler().addMessage(new Message(0, MessageType.ERROR,
              ex.getCause().toString()));
        }
        return null;
      }

      @Override
      public String toString() {
        return String.format("ServiceCallerInvokationHandler of service interface %s",
            serviceInterface);
      }
    };
    return Reflection.newProxy(serviceInterface, ih);
  }

  public void setNamespace(String namespace) {
    this.namespace = namespace;
  }

  public void setUrl(String url) {
    this.url = url;
  }

  public String getNamespace() {
    return namespace;
  }

  public void setMaxPayloadLengthToLog(int maxPayloadLengthToLog) {
    this.maxPayloadLengthToLog = maxPayloadLengthToLog;
  }

  public void setServicesToNotLogArgs(Set<String> servicesToNotLogArgs) {
    this.servicesToNotLogArgs = servicesToNotLogArgs;
  }
}
TOP

Related Classes of org.butor.web.servlet.AjaxServiceCallerFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.