com.bstek.dorado.view.task.LongTaskSocketServer.java Source code

Java tutorial

Introduction

Here is the source code for com.bstek.dorado.view.task.LongTaskSocketServer.java

Source

/*
 * This file is part of Dorado 7.x (http://dorado7.bsdn.org).
 * 
 * Copyright (c) 2002-2012 BSTEK Corp. All rights reserved.
 * 
 * This file is dual-licensed under the AGPLv3 (http://www.gnu.org/licenses/agpl-3.0.html) 
 * and BSDN commercial (http://www.bsdn.org/licenses) licenses.
 * 
 * If you are unsure which license is appropriate for your use, please contact the sales department
 * at http://www.bstek.com/contact.
 */

package com.bstek.dorado.view.task;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.bstek.dorado.common.service.ExposedServiceDefintion;
import com.bstek.dorado.common.service.ExposedServiceManager;
import com.bstek.dorado.core.bean.BeanFactoryUtils;
import com.bstek.dorado.core.resource.ResourceManager;
import com.bstek.dorado.core.resource.ResourceManagerUtils;
import com.bstek.dorado.data.ParameterWrapper;
import com.bstek.dorado.data.method.MethodAutoMatchingException;
import com.bstek.dorado.data.method.MethodAutoMatchingUtils;
import com.bstek.dorado.data.method.MoreThanOneMethodsMatchsException;
import com.bstek.dorado.util.WeakHashSet;
import com.bstek.dorado.view.longpolling.LongPollingManager;
import com.bstek.dorado.view.socket.Socket;
import com.bstek.dorado.web.DoradoContext;

/**
 * @author Benny Bao (mailto:benny.bao@bstek.com)
 * @since 2014-1-26
 */
public class LongTaskSocketServer implements SocketLongTaskConnectorListener {
    public static final String LONG_TASK_KEY_PREFIX = DefaultTaskScheduler.class.getName() + '.';
    public static final String TASK_SCHEDULER_KEY_PREFIX = TaskScheduler.class.getName() + '.';
    public static final String TASK_CONNECTOR_KEY_PREFIX = SocketLongTaskConnector.class.getName() + '.';

    public static class TaskStatePacket extends TaskStateInfo {
        private long transferTimestamp = System.currentTimeMillis();
        private long waitingStartTime;
        private long runningStartTime;

        public TaskStatePacket(LongTaskThread taskThread) throws Exception {
            BeanUtils.copyProperties(this, taskThread.getTask().getStateInfo());
            setWaitingStartTime(taskThread.getWaitingStartTime());
            setRunningStartTime(taskThread.getRunningStartTime());
        }

        public long getTransferTimestamp() {
            return transferTimestamp;
        }

        public void setTransferTimestamp(long transferTimestamp) {
            this.transferTimestamp = transferTimestamp;
        }

        public long getWaitingStartTime() {
            return waitingStartTime;
        }

        public void setWaitingStartTime(long waitingStartTime) {
            this.waitingStartTime = waitingStartTime;
        }

        public long getRunningStartTime() {
            return runningStartTime;
        }

        public void setRunningStartTime(long runningStartTime) {
            this.runningStartTime = runningStartTime;
        }
    }

    private static final Log logger = LogFactory.getLog(LongTaskSocketServer.class);

    private static final ResourceManager resourceManager = ResourceManagerUtils.get(LongTaskSocketServer.class);

    private static final Object[] EMPTY_ARGS = new Object[0];
    private static final String[] EMPTY_NAMES = new String[0];
    private static final Class<?>[] EMPTY_TYPES = new Class[0];

    protected static final class AbortException extends RuntimeException {
        private static final long serialVersionUID = 796823157518443055L;
    }

    private ExposedServiceManager exposedServiceManager;
    private LongPollingManager longPollingManager;
    private Map<String, SocketLongTaskConnector> connectorMap = new ConcurrentHashMap<String, SocketLongTaskConnector>();

    public void setExposedServiceManager(ExposedServiceManager exposedServiceManager) {
        this.exposedServiceManager = exposedServiceManager;
    }

    public void setLongPollingManager(LongPollingManager longPollingManager) {
        this.longPollingManager = longPollingManager;
    }

    @SuppressWarnings("unchecked")
    public TaskStatePacket connectLongTask(DoradoContext context, Socket socket, String taskName) throws Exception {
        ExposedServiceDefintion exposedService = exposedServiceManager.getService(taskName);
        if (exposedService == null) {
            throw new IllegalArgumentException("Unknown ExposedService [" + taskName + "].");
        }

        LongTaskDefinition taskDefinition = (LongTaskDefinition) exposedService.getExDefinition();
        String threadAttributeKey = LONG_TASK_KEY_PREFIX + taskName;
        String scope = (taskDefinition != null && taskDefinition.getScope() == LongTaskScope.application)
                ? DoradoContext.APPLICATION
                : DoradoContext.SESSION;

        TaskStatePacket taskStatePacket = null;
        LongTaskThread taskThread = (LongTaskThread) context.getAttribute(scope, threadAttributeKey);
        if (taskThread != null && taskThread.isAlive()) {
            taskStatePacket = new TaskStatePacket(taskThread);
        }

        String socketId = socket.getId();
        SocketLongTaskConnector connector = connectorMap.get(socketId);
        if (connector == null) {
            connector = new SocketLongTaskConnector(socket);
            if (taskThread != null) {
                connector.setTaskThread(taskThread);
            }
            connector.addListener(this);
            connectorMap.put(socketId, connector);

            synchronized (this) {
                String connectorAttributeKey = TASK_CONNECTOR_KEY_PREFIX + taskName;
                Set<SocketLongTaskConnector> connectors = (Set<SocketLongTaskConnector>) context.getAttribute(scope,
                        connectorAttributeKey);
                if (connectors == null) {
                    connectors = new WeakHashSet<SocketLongTaskConnector>();
                    context.setAttribute(scope, connectorAttributeKey, connectors);
                }
                connectors.add(connector);
            }
        }
        return taskStatePacket;
    }

    @SuppressWarnings("unchecked")
    public void startLongTask(DoradoContext context, Map<String, Object> parameter) throws Exception {
        String taskName = (String) parameter.get("taskName");
        String socketId = (String) parameter.get("socketId");
        Object realParameter = parameter.get("parameter");

        ExposedServiceDefintion exposedService = exposedServiceManager.getService(taskName);
        if (exposedService == null) {
            throw new IllegalArgumentException("Unknown ExposedService [" + taskName + "].");
        }

        Socket socket = longPollingManager.getSocket(socketId);
        if (socket == null) {
            throw new IllegalArgumentException("Unrecognized socketId [" + socketId + "].");
        }

        String threadAttributeKey = LONG_TASK_KEY_PREFIX + taskName;
        LongTaskDefinition taskDefinition = (LongTaskDefinition) exposedService.getExDefinition();
        String scope = (taskDefinition != null && taskDefinition.getScope() == LongTaskScope.application)
                ? DoradoContext.APPLICATION
                : DoradoContext.SESSION;

        LongTaskThread currentTaskThread = (LongTaskThread) context.getAttribute(scope, threadAttributeKey);
        if (currentTaskThread != null) {
            if (currentTaskThread.getState() == Thread.State.TERMINATED) {
                currentTaskThread = null;
                context.removeAttribute(scope, threadAttributeKey);
            } else {
                throw new IllegalStateException(
                        resourceManager.getString("dorado.view/errorLongTaskRunning", taskName));
            }
        }

        LongTask task = getLongTask(context, exposedService, realParameter);

        LongTaskThread taskThread = new LongTaskThread(task);
        taskThread.setName(taskName);

        String connectorAttributeKey = TASK_CONNECTOR_KEY_PREFIX + taskName;
        Set<SocketLongTaskConnector> connectors = (Set<SocketLongTaskConnector>) context.getAttribute(scope,
                connectorAttributeKey);
        if (connectors != null) {
            Iterator<SocketLongTaskConnector> it = connectors.iterator();
            while (it.hasNext()) {
                SocketLongTaskConnector connector = it.next();
                if (connector.isClosed()) {
                    it.remove();
                } else {
                    connector.setTaskThread(taskThread);
                }
            }
        }

        TaskScheduler scheduler = getTaskScheduler(context, taskName, exposedService);
        scheduler.queueTask(context, taskThread);
        context.setAttribute(scope, threadAttributeKey, taskThread);
    }

    protected synchronized TaskScheduler getTaskScheduler(DoradoContext context, String taskName,
            ExposedServiceDefintion exposedService) throws Exception {
        String schedulerAttributeKey = TASK_SCHEDULER_KEY_PREFIX + taskName;
        TaskScheduler scheduler = (TaskScheduler) context.getAttribute(DoradoContext.APPLICATION,
                schedulerAttributeKey);
        if (scheduler == null) {
            LongTaskDefinition taskDefinition = (LongTaskDefinition) exposedService.getExDefinition();
            if (taskDefinition == null) {
                taskDefinition = new LongTaskDefinition(taskName);
            }

            String schedularImpl = taskDefinition.getSchedular();
            if (StringUtils.isNotEmpty(schedularImpl)) {
                scheduler = (TaskScheduler) BeanFactoryUtils.getBean(schedularImpl);
            } else {
                scheduler = new DefaultTaskScheduler();
            }
            scheduler.setTaskDefinition(taskDefinition);

            context.setAttribute(DoradoContext.APPLICATION, schedulerAttributeKey, scheduler);
        }
        return scheduler;
    }

    public void onClose(SocketLongTaskConnector connector) {
        Socket socket = connector.getSocket();
        if (socket.isConnected()) {
            connector.setTaskThread(null);
        } else {
            connectorMap.remove(socket.getId());
        }
    }

    protected LongTask getLongTask(DoradoContext context, ExposedServiceDefintion exposedService, Object parameter)
            throws Exception {
        Object serviceBean = BeanFactoryUtils.getBean(exposedService.getBean());
        String methodName = exposedService.getMethod();
        Method[] methods = MethodAutoMatchingUtils.getMethodsByName(serviceBean.getClass(), methodName);
        if (methods.length == 0) {
            throw new NoSuchMethodException(
                    "Method [" + methodName + "] not found in [" + exposedService.getBean() + "].");
        }

        LongTask returnValue = null;

        boolean methodInvoked = false;
        MethodAutoMatchingException[] exceptions = new MethodAutoMatchingException[4];
        int i = 0;
        try {
            try {
                returnValue = invokeByParameterName(serviceBean, methods, parameter, false);
                methodInvoked = true;
            } catch (MoreThanOneMethodsMatchsException e) {
                throw e;
            } catch (MethodAutoMatchingException e) {
                exceptions[i++] = e;
            } catch (AbortException e) {
                // do nothing
            }

            if (!methodInvoked) {
                try {
                    returnValue = invokeByParameterName(serviceBean, methods, parameter, true);
                    methodInvoked = true;
                } catch (MoreThanOneMethodsMatchsException e) {
                    throw e;
                } catch (MethodAutoMatchingException e) {
                    exceptions[i++] = e;
                } catch (AbortException e) {
                    // do nothing
                }
            }

            if (!methodInvoked) {
                try {
                    returnValue = invokeByParameterType(serviceBean, methods, parameter, false);
                    methodInvoked = true;
                } catch (MoreThanOneMethodsMatchsException e) {
                    throw e;
                } catch (MethodAutoMatchingException e) {
                    exceptions[i++] = e;
                } catch (AbortException e) {
                    // do nothing
                }
            }

            if (!methodInvoked) {
                try {
                    returnValue = invokeByParameterType(serviceBean, methods, parameter, true);
                    methodInvoked = true;
                } catch (MoreThanOneMethodsMatchsException e) {
                    throw e;
                } catch (MethodAutoMatchingException e) {
                    exceptions[i++] = e;
                } catch (AbortException e) {
                    // do nothing
                }
            }
        } catch (MethodAutoMatchingException e) {
            exceptions[i++] = e;
        }

        if (methodInvoked) {
            return returnValue;
        } else {
            for (MethodAutoMatchingException e : exceptions) {
                if (e == null) {
                    break;
                }
                logger.error(e.getMessage());
            }
            throw new IllegalArgumentException(resourceManager.getString("dorado.common/noMatchingMethodError",
                    serviceBean.getClass().getName(), methodName));
        }
    }

    protected LongTask invokeByParameterName(Object serviceBean, Method[] methods, Object parameter,
            boolean disassembleParameter) throws MethodAutoMatchingException, Exception {
        Map<String, Object> sysParameter = null;
        if (parameter instanceof ParameterWrapper) {
            ParameterWrapper parameterWrapper = (ParameterWrapper) parameter;
            parameter = parameterWrapper.getParameter();
            sysParameter = parameterWrapper.getSysParameter();
        }

        if (disassembleParameter && (parameter == null && !(parameter instanceof Map<?, ?>))) {
            throw new AbortException();
        }

        String[] parameterParameterNames = EMPTY_NAMES;
        Object[] parameterParameters = EMPTY_ARGS;
        if (parameter != null && parameter instanceof Map<?, ?>) {
            if (disassembleParameter) {
                Map<?, ?> map = (Map<?, ?>) parameter;
                parameterParameterNames = new String[map.size()];
                parameterParameters = new Object[parameterParameterNames.length];

                int i = 0;
                for (Map.Entry<?, ?> entry : map.entrySet()) {
                    parameterParameterNames[i] = (String) entry.getKey();
                    parameterParameters[i] = entry.getValue();
                    i++;
                }
            } else {
                parameterParameterNames = new String[] { "parameter" };
                parameterParameters = new Object[] { parameter };
            }
        } else {
            parameterParameterNames = new String[] { "parameter" };
            parameterParameters = new Object[] { parameter };
        }

        String[] optionalParameterNames = new String[parameterParameterNames.length];
        Object[] optionalParameters = new Object[optionalParameterNames.length];
        System.arraycopy(parameterParameterNames, 0, optionalParameterNames, 0, parameterParameterNames.length);
        System.arraycopy(parameterParameters, 0, optionalParameters, 0, parameterParameters.length);

        String[] extraParameterNames = null;
        Object[] extraParameters = null;
        if (sysParameter != null && !sysParameter.isEmpty()) {
            extraParameterNames = new String[sysParameter.size()];
            extraParameters = new Object[extraParameterNames.length];

            int i = 0;
            for (Map.Entry<?, ?> entry : sysParameter.entrySet()) {
                extraParameterNames[i] = (String) entry.getKey();
                extraParameters[i] = entry.getValue();
                i++;
            }
        }

        return (LongTask) MethodAutoMatchingUtils.invokeMethod(methods, serviceBean, null, null,
                optionalParameterNames, optionalParameters, extraParameterNames, extraParameters);
    }

    protected LongTask invokeByParameterType(Object serviceBean, Method[] methods, Object parameter,
            boolean disassembleParameter) throws MethodAutoMatchingException, Exception {
        Map<String, Object> sysParameter = null;
        if (parameter instanceof ParameterWrapper) {
            ParameterWrapper parameterWrapper = (ParameterWrapper) parameter;
            parameter = parameterWrapper.getParameter();
            sysParameter = parameterWrapper.getSysParameter();
        }

        if (disassembleParameter && (parameter == null && !(parameter instanceof Map<?, ?>))) {
            throw new AbortException();
        }

        Type[] optionalParameterTypes = EMPTY_TYPES;
        Object[] optionalParameters = EMPTY_ARGS;
        if (parameter != null) {
            if (parameter instanceof Map<?, ?>) {
                if (disassembleParameter) {
                    Map<?, ?> map = (Map<?, ?>) parameter;
                    optionalParameterTypes = new Class[map.size()];
                    optionalParameters = new Object[optionalParameterTypes.length];

                    int i = 0;
                    for (Object value : map.values()) {
                        if (value != null) {
                            optionalParameterTypes[i] = MethodAutoMatchingUtils.getTypeForMatching(value);
                            optionalParameters[i] = value;
                            i++;
                        }
                    }
                } else {
                    optionalParameterTypes = new Type[] { MethodAutoMatchingUtils.getTypeForMatching(parameter) };
                    optionalParameters = new Object[] { parameter };
                }
            } else {
                optionalParameterTypes = new Type[] { MethodAutoMatchingUtils.getTypeForMatching(parameter) };
                optionalParameters = new Object[] { parameter };
            }
        } else {
            optionalParameterTypes = new Type[] { Object.class };
            optionalParameters = new Object[] { null };
        }

        Type[] exactArgTypes = null;
        Object[] exactArgs = null;
        Map<Type, Object> extraArgMap = new HashMap<Type, Object>();
        if (sysParameter != null && !sysParameter.isEmpty()) {
            for (Map.Entry<?, ?> entry : sysParameter.entrySet()) {
                Object value = entry.getValue();
                if (value != null) {
                    extraArgMap.put(MethodAutoMatchingUtils.getTypeForMatching(value), value);
                }
            }

            if (!extraArgMap.isEmpty()) {
                exactArgTypes = new Class[extraArgMap.size()];
                exactArgs = new Object[exactArgTypes.length];
                int i = 0;
                for (Map.Entry<?, ?> entry : extraArgMap.entrySet()) {
                    exactArgTypes[i] = (Class<?>) entry.getKey();
                    exactArgs[i] = entry.getValue();
                    i++;
                }
            }
        }

        return (LongTask) MethodAutoMatchingUtils.invokeMethod(methods, serviceBean, null, null, exactArgTypes,
                exactArgs, optionalParameterTypes, optionalParameters, LongTask.class);
    }

}