/*******************************************************************************
* Copyright (c) 2012 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.modules.login;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openlegacy.exceptions.OpenLegacyRuntimeException;
import org.openlegacy.exceptions.RegistryException;
import org.openlegacy.exceptions.SessionEndedException;
import org.openlegacy.modules.login.Login;
import org.openlegacy.modules.login.LoginException;
import org.openlegacy.terminal.ScreenEntity;
import org.openlegacy.terminal.ScreenPojoFieldAccessor;
import org.openlegacy.terminal.actions.TerminalAction;
import org.openlegacy.terminal.actions.TerminalActions;
import org.openlegacy.terminal.definitions.NavigationDefinition;
import org.openlegacy.terminal.definitions.ScreenEntityDefinition;
import org.openlegacy.terminal.services.ScreenEntitiesRegistry;
import org.openlegacy.terminal.services.ScreensRecognizer;
import org.openlegacy.terminal.support.TerminalSessionModuleAdapter;
import org.openlegacy.terminal.support.wait_conditions.WaitForNonEmptyField;
import org.openlegacy.terminal.utils.SimpleScreenPojoFieldAccessor;
import org.openlegacy.terminal.wait_conditions.WaitConditionFactory;
import org.openlegacy.utils.ProxyUtil;
import org.openlegacy.utils.ReflectionUtil;
import java.io.Serializable;
import java.text.MessageFormat;
import javax.inject.Inject;
public class DefaultTerminalLoginModule extends TerminalSessionModuleAdapter implements Login, Serializable {
private static final long serialVersionUID = 1L;
@Inject
private LoginMetadata loginMetadata;
@Inject
private ScreensRecognizer screensRecognizer;
@Inject
private ScreenEntitiesRegistry screenEntitiesRegistry;
@Inject
private WaitConditionFactory waitConditionFactory;
private TerminalAction loginAction = TerminalActions.ENTER();
private String loggedInUser = null;
private TerminalAction defaultExitAction = TerminalActions.F3();
private long loginTimeout = 0;
// the maximum number of actions allowed in order to exit back to login screen
private int maxActionsToLogin = 7;
private final static Log logger = LogFactory.getLog(DefaultTerminalLoginModule.class);
private static final String LOGIN_FAILED = "Login failed";
private static final String USER_NAME = "User name";
public void login(String user, String password) throws LoginException {
if (loggedInUser != null) {
return;
}
lazyMetadataInit();
if (loginMetadata.getLoginScreenDefinition() == null) {
throw (new RegistryException(
"LoginModule entity doesn't contain a login screen definition. Verify one of your screen entity classes is defined as @ScreenEntity(screenType = Login.LoginEntity.class)"));
}
try {
ScreenEntity loginEntity = (ScreenEntity)loginMetadata.getLoginScreenDefinition().getEntityClass().newInstance();
ScreenPojoFieldAccessor fieldAccessor = new SimpleScreenPojoFieldAccessor(loginEntity);
fieldAccessor.setFieldValue(loginMetadata.getUserField().getName(), user);
fieldAccessor.setFieldValue(loginMetadata.getPasswordField().getName(), password);
login(loginEntity);
} catch (LoginException e) {
throw (e);
} catch (Exception e) {
throw (new IllegalStateException(e));
}
}
public void login(Object loginEntity) throws LoginException, RegistryException {
if (loggedInUser != null) {
throw (new LoginException("User is already logged in"));
}
lazyMetadataInit();
if (loginMetadata.getLoginScreenDefinition() == null) {
throw (new RegistryException(
"LoginModule entity doesn't contain a login screen definition. Verify one of your screen entity classes is defined as @ScreenEntity(screenType = Login.LoginEntity.class)"));
}
Class<?> registryLoginClass = loginMetadata.getLoginScreenDefinition().getEntityClass();
if (!ProxyUtil.isClassesMatch(loginEntity.getClass(), registryLoginClass)) {
throw (new RegistryException("LoginModule entity " + loginEntity.getClass() + " doesn't match registry login screen"
+ registryLoginClass));
}
ScreenPojoFieldAccessor fieldAccessor = new SimpleScreenPojoFieldAccessor(loginEntity);
String user = (String)fieldAccessor.getFieldValue(loginMetadata.getUserField().getName());
// construct a wait while login error message is NOT shown (or next screen whows up)
String errorFieldName = loginMetadata.getErrorField().getName();
WaitForNonEmptyField waitForNonEmptyLoginError = waitConditionFactory.create(WaitForNonEmptyField.class,
registryLoginClass, errorFieldName);
Object currentEntity = getSession().doAction(loginAction, (ScreenEntity)loginEntity, waitForNonEmptyLoginError);
Class<? extends Object> currentEntityClass = null;
if (currentEntity != null) {
currentEntityClass = currentEntity.getClass();
fieldAccessor = new SimpleScreenPojoFieldAccessor(currentEntity);
if (logger.isDebugEnabled()) {
logger.debug(MessageFormat.format("After performing login action current entity is:{0}", currentEntityClass));
}
}
if (currentEntityClass != null && ProxyUtil.isClassesMatch(currentEntityClass, registryLoginClass)) {
try {
Thread.sleep(loginTimeout);
} catch (InterruptedException e) {
throw (new OpenLegacyRuntimeException(e));
}
}
// throw exception if after login screen is still login
if (currentEntityClass != null && ProxyUtil.isClassesMatch(currentEntityClass, registryLoginClass)) {
Object value = fieldAccessor.getFieldValue(errorFieldName);
String message = value != null ? value.toString() : LOGIN_FAILED;
fieldAccessor.setFieldValue(errorFieldName, message);
throw (new LoginException(message));
} else {
loggedInUser = user;
getSession().getProperties().getProperties().put(USER_NAME, loggedInUser);
}
}
private void lazyMetadataInit() {
loginMetadata.initCache();
}
public boolean isLoggedIn() {
return loggedInUser != null;
}
public void logoff() {
logoffOnly();
getSession().disconnect();
}
private void logoffOnly() {
if (loggedInUser == null) {
return;
}
lazyMetadataInit();
if (loginMetadata.getLoginScreenDefinition() == null) {
return;
}
Class<?> loginClass = loginMetadata.getLoginScreenDefinition().getEntityClass();
if (getSession().isConnected()) {
try {
backToLogin(loginClass);
} catch (Exception e) {
// ok
logger.warn("Failed to return to login screen - " + e.getMessage(), e);
}
}
loggedInUser = null;
}
private void backToLogin(Class<?> loginClass) {
Class<?> currentEntityClass = screensRecognizer.match(getSession().getSnapshot());
int exitActionsCount = 0;
// while current entity is not login screen and haven't reach a maximum exit actions
while (!ProxyUtil.isClassesMatch(currentEntityClass, loginClass, true) && exitActionsCount < maxActionsToLogin) {
TerminalAction exitAction = defaultExitAction;
exitActionsCount++;
exitAction = findCurrentEntityExitAction(currentEntityClass, exitAction);
if (logger.isDebugEnabled()) {
logger.debug(MessageFormat.format("Exiting screen {0} using {1}", currentEntityClass, exitAction));
}
try {
getSession().doAction(exitAction);
} catch (SessionEndedException e) {
break;
}
currentEntityClass = screensRecognizer.match(getSession().getSnapshot());
}
}
private TerminalAction findCurrentEntityExitAction(Class<? extends Object> currentEntityClass, TerminalAction exitAction) {
if (currentEntityClass != null) {
ScreenEntityDefinition currentScreenDefinition = screenEntitiesRegistry.get(currentEntityClass);
NavigationDefinition navigationDefinition = currentScreenDefinition.getNavigationDefinition();
if (navigationDefinition != null && navigationDefinition.getExitAction() != null) {
exitAction = navigationDefinition.getExitAction();
}
}
return exitAction;
}
public void setLoginActionClass(Class<? extends TerminalAction> terminalAction) {
this.loginAction = ReflectionUtil.newInstance(terminalAction);
}
public void setDefaultExitActionClass(Class<? extends TerminalAction> defaultExitAction) {
this.defaultExitAction = ReflectionUtil.newInstance(defaultExitAction);
}
public void setMaxActionsToLogin(int maxActionsToLogin) {
this.maxActionsToLogin = maxActionsToLogin;
}
public void setLoginTimeout(long loginTimeout) {
this.loginTimeout = loginTimeout;
}
public String getLoggedInUser() {
return loggedInUser;
}
@Override
public void destroy() {
try {
logoffOnly();
} catch (Exception e) {
logger.warn("Disconnected with error", e);
}
}
}