org.openlegacy.terminal.support.DefaultTerminalSession.java Source code

Java tutorial

Introduction

Here is the source code for org.openlegacy.terminal.support.DefaultTerminalSession.java

Source

/*******************************************************************************
 * Copyright (c) 2014 OpenLegacy Inc.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     OpenLegacy Inc. - initial API and implementation
 *******************************************************************************/
package org.openlegacy.terminal.support;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openlegacy.ApplicationConnectionListener;
import org.openlegacy.OpenLegacyProperties;
import org.openlegacy.authorization.AuthorizationService;
import org.openlegacy.exceptions.EntityNotAccessibleException;
import org.openlegacy.exceptions.EntityNotFoundException;
import org.openlegacy.exceptions.OpenLegacyRuntimeException;
import org.openlegacy.modules.SessionModule;
import org.openlegacy.modules.login.Login;
import org.openlegacy.modules.login.Login.LoginEntity;
import org.openlegacy.modules.login.LoginException;
import org.openlegacy.modules.login.User;
import org.openlegacy.support.AbstractSession;
import org.openlegacy.terminal.ConnectionProperties;
import org.openlegacy.terminal.ConnectionPropertiesProvider;
import org.openlegacy.terminal.DeviceAllocator;
import org.openlegacy.terminal.ScreenEntity;
import org.openlegacy.terminal.ScreenEntityBinder;
import org.openlegacy.terminal.TerminalActionMapper;
import org.openlegacy.terminal.TerminalConnection;
import org.openlegacy.terminal.TerminalField;
import org.openlegacy.terminal.TerminalSendAction;
import org.openlegacy.terminal.TerminalSession;
import org.openlegacy.terminal.TerminalSessionPropertiesConsts;
import org.openlegacy.terminal.TerminalSnapshot;
import org.openlegacy.terminal.actions.TerminalAction;
import org.openlegacy.terminal.definitions.ScreenEntityDefinition;
import org.openlegacy.terminal.definitions.TerminalActionDefinition;
import org.openlegacy.terminal.exceptions.ScreenEntityNotAccessibleException;
import org.openlegacy.terminal.modules.table.TerminalDrilldownActions.SimpleDrilldownAction;
import org.openlegacy.terminal.services.ScreenEntitiesRegistry;
import org.openlegacy.terminal.services.ScreensRecognizer;
import org.openlegacy.terminal.services.SessionNavigator;
import org.openlegacy.terminal.support.proxy.ScreenEntityMethodInterceptor;
import org.openlegacy.terminal.utils.ScreenEntityUtils;
import org.openlegacy.terminal.wait_conditions.WaitCondition;
import org.openlegacy.utils.ProxyUtil;
import org.openlegacy.utils.ReflectionUtil;
import org.openlegacy.utils.SpringUtil;
import org.springframework.context.ApplicationContext;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.inject.Inject;

/**
 * A default session class exposes screenEntity building and sending
 * 
 * 
 */
public class DefaultTerminalSession extends AbstractSession
        implements TerminalSession, Serializable, ConnectionPropertiesProvider {

    private static final long serialVersionUID = 1L;

    @Inject
    private List<ScreenEntityBinder> screenEntityBinders;

    private TerminalConnection terminalConnection;

    private ScreenEntity entity;

    @Inject
    private SessionNavigator sessionNavigator;

    @Inject
    private ScreensRecognizer screensRecognizer;

    @Inject
    private TerminalActionMapper terminalActionMapper;

    @Inject
    private ScreenEntityUtils screenEntityUtils;

    @Inject
    private OpenLegacyProperties openLegacyProperties;

    @Inject
    private transient ApplicationContext applicationContext;

    private ScreenEntityMethodInterceptor interceptor;

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

    private boolean useProxyForEntities = true;

    private ConnectionProperties connectionProperties;

    @Inject
    private ScreenEntitiesRegistry screenEntitiesRegistry;

    @Inject
    private DeviceAllocator deviceAllocator;

    @Inject
    private AuthorizationService authorizationService;

    private Integer lastSequence = 0;

    private boolean forceAuthorization = true;

    private boolean enableConnectWithoutDevice = true;

    @Override
    @SuppressWarnings("unchecked")
    public synchronized <S> S getEntity(Class<S> screenEntityClass, Object... keys) throws EntityNotFoundException {

        authorize(screenEntityClass);

        if (keys.length == 1 && keys[0] == null) {
            keys = new Object[0];
        }

        checkRegistryDirty();

        ScreenEntityDefinition definitions = getScreenEntitiesRegistry().get(screenEntityClass);

        if (keys.length > definitions.getKeys().size()) {
            throw (new EntityNotAccessibleException(MessageFormat.format(
                    "Requested entity {0} with keys {1} doesnt matches the defined entity keys count: {2}. Verify key is defined for {3}",
                    screenEntityClass, ArrayUtils.toString(keys), definitions.getKeys().size(),
                    screenEntityClass.getName())));
        }
        sessionNavigator.navigate(this, screenEntityClass, keys);
        return (S) getEntity();
    }

    private <S> void authorize(Class<S> screenEntityClass) {
        if (!forceAuthorization) {
            return;
        }
        ScreenEntityDefinition definitions = getScreenEntitiesRegistry().get(screenEntityClass);
        User loggedInUser = getModule(Login.class).getLoggedInUser();
        if (definitions.getType() != LoginEntity.class
                && !authorizationService.isAuthorized(loggedInUser, screenEntityClass)) {
            throw (new EntityNotAccessibleException(
                    MessageFormat.format("Logged in user {0} has no permission for entity {1}",
                            loggedInUser.getUserName(), screenEntityClass.getName())));
        }
    }

    private void checkRegistryDirty() {
        if (getScreenEntitiesRegistry().isDirty()) {
            resetEntity();
        }
    }

    private <S> ScreenEntity getEntityInner() {
        resetEntity();
        TerminalSnapshot terminalSnapshot = getSnapshot();

        Class<?> matchedScreenEntity = screensRecognizer.match(terminalSnapshot);

        if (matchedScreenEntity == null) {
            logger.debug("Current snapshot is not recognized");
            return null;
        }

        ScreenEntity screenEntity = null;
        if (useProxyForEntities) {
            screenEntity = (ScreenEntity) ProxyUtil.createPojoProxy(matchedScreenEntity, ScreenEntity.class,
                    interceptor);
        } else {
            screenEntity = (ScreenEntity) ReflectionUtil.newInstance(matchedScreenEntity);
        }

        ScreenEntityDefinition screenEntityDefinition = getScreenEntitiesRegistry().get(matchedScreenEntity);

        if (screenEntityDefinition.isRightToLeft() != getConnection().isRightToLeftState()) {
            flip();
            terminalSnapshot = getSnapshot();
        }

        if (screenEntityDefinition.isPerformDefaultBinding()) {
            for (ScreenEntityBinder screenEntityBinder : screenEntityBinders) {
                screenEntityBinder.populateEntity(screenEntity, terminalSnapshot);
            }
        }

        List<ScreenEntityBinder> binders = screenEntityDefinition.getBinders();
        if (binders != null) {
            for (ScreenEntityBinder screenEntityBinder : binders) {
                screenEntityBinder.populateEntity(screenEntity, terminalSnapshot);
            }
        }

        return screenEntity;
    }

    protected void resetEntity() {
        if (entity != null) {
            entity = null;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized <R extends ScreenEntity> R getEntity() {
        checkRegistryDirty();
        getSnapshot();
        if (!lastSequence.equals(getSequence())) {
            resetEntity();
        }
        if (entity == null) {
            entity = getEntityInner();
            if (entity == null) {
                entity = new ScreenEntity.NONE();
                lastSequence = getSequence();
            }
        }
        if (entity != null && entity instanceof ScreenEntity.NONE) {
            return null;
        }
        return (R) entity;
    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized <R extends ScreenEntity> R doAction(TerminalAction action,
            WaitCondition... waitConditions) {
        return (R) doAction(action, null, waitConditions);
    }

    @Override
    public synchronized <S extends ScreenEntity, R extends ScreenEntity> R doAction(TerminalAction terminalAction,
            S screenEntity, Class<R> expectedEntity) {
        try {
            @SuppressWarnings("unchecked")
            R object = (R) doAction(terminalAction, screenEntity);
            return object;
        } catch (ClassCastException e) {
            throw (new ScreenEntityNotAccessibleException(e, expectedEntity.getSimpleName()));
        }

    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized <S extends ScreenEntity, R extends ScreenEntity> R doAction(TerminalAction terminalAction,
            S screenEntity, WaitCondition... waitConditions) {

        // verify screens are synch
        if (screenEntity != null) {
            getEntity(screenEntity.getClass());
        }

        if (terminalAction instanceof SimpleDrilldownAction) {
            terminalAction = ((SimpleDrilldownAction) terminalAction).getAction();
        }

        Object command = terminalActionMapper.getCommand(terminalAction);
        if (command == null) {
            terminalAction.perform(this, screenEntity);
        } else {
            SimpleTerminalSendAction sendAction = new SimpleTerminalSendAction(command);

            int sleep = 0;
            if (screenEntity != null) {
                ScreenEntityDefinition screenEntityDefinition = getScreenEntitiesRegistry()
                        .get(screenEntity.getClass());
                TerminalActionDefinition actionDefinition = screenEntityUtils.findAction(screenEntity,
                        terminalAction);
                sleep = (actionDefinition != null ? actionDefinition.getSleep() : 0);
                if (screenEntityDefinition.isPerformDefaultBinding()) {
                    for (ScreenEntityBinder screenEntityBinder : screenEntityBinders) {
                        screenEntityBinder.populateAction(sendAction, getSnapshot(), screenEntity);
                    }
                }
                List<ScreenEntityBinder> binders = screenEntityDefinition.getBinders();
                if (binders != null) {
                    for (ScreenEntityBinder screenEntityBinder : binders) {
                        screenEntityBinder.populateAction(sendAction, getSnapshot(), screenEntity);

                    }
                }
            }

            sendAction.setSleep(sleep);
            doAction(sendAction, waitConditions);
        }

        return (R) getEntity();
    }

    protected void notifyModulesBeforeSend(TerminalSendAction terminalSendAction) {
        Collection<? extends SessionModule> modulesList = getSessionModules().getModules();
        for (SessionModule sessionModule : modulesList) {
            if (sessionModule instanceof ApplicationConnectionListener) {
                ((ApplicationConnectionListener) sessionModule).beforeAction(terminalConnection,
                        terminalSendAction);
            }
        }
    }

    protected void notifyModulesAfterAction(TerminalSendAction sendAction) {
        Collection<? extends SessionModule> modulesList = getSessionModules().getModules();
        for (SessionModule sessionModule : modulesList) {
            if (sessionModule instanceof ApplicationConnectionListener) {
                ((ApplicationConnectionListener) sessionModule).afterAction(terminalConnection, sendAction,
                        getSnapshot());
            }
        }
    }

    @Override
    public synchronized TerminalSnapshot getSnapshot() {
        boolean newSession = false;
        if (!terminalConnection.isConnected()) {
            notifyModulesBeforeConnect();
            newSession = true;

            if (!enableConnectWithoutDevice && getConnectionProperties().getDeviceName() == null) {
                throw (new LoginException("Device cannot be empty"));
            }
        }
        TerminalSnapshot snapshot = terminalConnection.getSnapshot();

        if (newSession) {
            notifyModulesAfterConnect();
        }
        return snapshot;
    }

    @Override
    public Object getDelegate() {
        return terminalConnection.getDelegate();
    }

    public void setConnection(TerminalConnection terminalConnection) {
        this.terminalConnection = terminalConnection;
    }

    @Override
    public void disconnect() {
        List<? extends SessionModule> sessionModulesList = getSessionModules().getModules();
        for (SessionModule sessionModule : sessionModulesList) {
            sessionModule.destroy();
        }
        setProperties(null);
        resetEntity();
        terminalConnection.disconnect();
    }

    @Override
    public boolean isConnected() {
        return terminalConnection.isConnected();
    }

    public void setInterceptor(ScreenEntityMethodInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();
        interceptor.setTerminalSession(this);
    }

    @Override
    public synchronized void doAction(TerminalSendAction sendAction, WaitCondition... waitConditions) {

        doTerminalAction(sendAction, waitConditions);
    }

    private void performWait(WaitCondition... waitConditions) {
        if (waitConditions == null || waitConditions.length == 0) {
            return;
        }

        int totalWait = 0;

        for (WaitCondition waitCondition : waitConditions) {
            fetchSnapshot();
            resetEntity();
            while (waitCondition.continueWait(this) && totalWait < waitCondition.getWaitTimeout()) {
                if (logger.isDebugEnabled()) {
                    logger.debug(MessageFormat.format("Waiting for {0}ms. Current screen is:{1}",
                            waitCondition.getWaitInterval(), fetchSnapshot()));
                }
                try {
                    Thread.sleep(waitCondition.getWaitInterval());
                } catch (InterruptedException e) {
                    throw (new OpenLegacyRuntimeException(e));
                }

                totalWait += waitCondition.getWaitInterval();
                fetchSnapshot();
                resetEntity();
            }
        }
    }

    protected void doTerminalAction(TerminalSendAction sendAction, WaitCondition... waitConditions) {

        formatSendAction(sendAction);

        logScreenBefore(sendAction);

        notifyModulesBeforeSend(sendAction);

        terminalConnection.doAction(sendAction);
        lastSequence = getSequence();
        resetEntity();

        performWait(waitConditions);

        logScreenAfter();

        notifyModulesAfterAction(sendAction);
    }

    protected void formatSendAction(TerminalSendAction sendAction) {
        List<TerminalField> fields = sendAction.getFields();
        for (TerminalField terminalField : fields) {
            if (openLegacyProperties.isUppercaseInput() || terminalField.isUppercase()) {
                terminalField.setValue(terminalField.getValue().toUpperCase());
            }
        }

        // sort the modified fields by position
        Collections.sort(sendAction.getFields(), TerminalPositionContainerComparator.instance());

    }

    private void logScreenBefore(TerminalSendAction sendAction) {
        if (logger.isDebugEnabled()) {
            logger.debug(MessageFormat.format("\nAction:{0}, Cursor:{1}\n", sendAction.getCommand(),
                    sendAction.getCursorPosition()));
            logger.debug(
                    "\nScreen before\n(* abc * marks a modified field, [ abc ] mark an input field, # mark cursor):\n\n"
                            + getSnapshot());
        }
    }

    protected void logScreenAfter() {
        if (logger.isDebugEnabled()) {
            logger.debug(
                    MessageFormat.format("\n\nScreen after ([ abc ] indicates a input field):\nCursor: {0}\n\n{1}",
                            getSnapshot().getCursorPosition(), getSnapshot()));
        }
    }

    @Override
    public synchronized TerminalSnapshot fetchSnapshot() {
        return terminalConnection.fetchSnapshot();
    }

    @Override
    protected TerminalConnection getConnection() {
        return terminalConnection;
    }

    protected ScreensRecognizer getScreensRecognizer() {
        return screensRecognizer;
    }

    @Override
    public synchronized Object getEntity(String entityName, Object... keys) throws EntityNotFoundException {
        Class<?> entityClass = getScreenEntitiesRegistry().getEntityClass(entityName);
        if (entityClass == null) {
            throw (new EntityNotFoundException(
                    MessageFormat.format("Screen entity \"{0}\" not found", entityName)));
        }
        return getEntity(entityClass, keys);
    }

    protected ScreenEntitiesRegistry getScreenEntitiesRegistry() {
        // Fetch registry from application context. Since session is proxied in web apps, the new registry bean is not available
        // after refresh, and required a fresh copy to support new entity classes loading during development
        if (openLegacyProperties.isDesigntime()) {
            return SpringUtil.getBean(applicationContext, ScreenEntitiesRegistry.class);
        }

        if (this.screenEntitiesRegistry == null) {
            screenEntitiesRegistry = SpringUtil.getBean(applicationContext, ScreenEntitiesRegistry.class);
        }
        return screenEntitiesRegistry;
    }

    @Override
    public Integer getSequence() {
        Integer seq = terminalConnection.getSequence();
        return seq;
    }

    public <R extends ScreenEntity> void setEntity(R entity) {
        this.entity = entity;
    }

    public boolean isUseProxyForEntities() {
        return useProxyForEntities;
    }

    public void setUseProxyForEntities(boolean useProxyForEntities) {
        this.useProxyForEntities = useProxyForEntities;
    }

    @Override
    public ConnectionProperties getConnectionProperties() {
        if (connectionProperties == null) {
            connectionProperties = new ConnectionProperties() {

                @Override
                public String getDeviceName() {
                    String device = (String) getProperties()
                            .getProperty(TerminalSessionPropertiesConsts.DEVICE_NAME);
                    // treat the result property device as pool name
                    device = deviceAllocator.allocate(device, getProperties());
                    if (device != null) {
                        getProperties().setProperty(TerminalSessionPropertiesConsts.DEVICE_NAME, device);
                    }
                    return device;
                }

            };
        }
        return connectionProperties;
    }

    @Override
    public void flip() {
        terminalConnection.flip();
        // force update
        terminalConnection.fetchSnapshot();
        lastSequence = getSequence();
        notifyModulesAfterAction(null);
    }

    public void setForceAuthorization(boolean forceAuthorization) {
        this.forceAuthorization = forceAuthorization;
    }

    protected void setLastSequence(Integer lastSequence) {
        this.lastSequence = lastSequence;
    }

    public void setEnableConnectWithoutDevice(boolean enableConnectWithoutDevice) {
        this.enableConnectWithoutDevice = enableConnectWithoutDevice;
    }
}