//{HEADER
/**
* This class is part of jnex 'Nexirius Application Framework for Java'
*
* Copyright (C) Nexirius GmbH, CH-4450 Sissach, Switzerland (www.nexirius.ch)
*
* <p>This library is free software; you can redistribute it and/or<br>
* modify it under the terms of the GNU Lesser General Public<br>
* License as published by the Free Software Foundation; either<br>
* version 2.1 of the License, or (at your option) any later version.</p>
*
* <p>This library is distributed in the hope that it will be useful,<br>
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
* Lesser General Public License for more details.</p>
*
* <p>You should have received a copy of the GNU Lesser General Public<br>
* License along with this library; if not, write to the Free Software<br>
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
* </blockquote>
*
* <p>
* Nexirius GmbH, hereby disclaims all copyright interest in<br>
* the library jnex' 'Nexirius Application Framework for Java' written<br>
* by Marcel Baumann.</p>
*/
//}HEADER
package com.nexirius.framework.htmlview;
import com.nexirius.framework.FWLog;
import com.nexirius.framework.Logger;
import com.nexirius.framework.datamodel.BooleanModel;
import com.nexirius.framework.datamodel.DataModel;
import com.nexirius.framework.htmlview.application.HTMLApplication;
import com.nexirius.framework.htmlview.function.HTMLFunction;
import com.nexirius.framework.htmlview.function.HTMLTransition;
import com.nexirius.framework.htmlview.function.BackButtonHTMLFunction;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Enumeration;
/**
* This servlet is connected to a state machine which holds HTMLState items. Each action holds an event parameter
* which is used to step to the next state in the state machine. The actual model and template which is used to create
* the next HTML page come from the HTMLState (unless the event holds specific paramters which override that information)
*/
public abstract class JnexServlet extends HttpServlet {
public static final String SESSION_VARIABLE_PREFIX = "nexirius_";
public static final String BOOLEAN_PARAMETER_PREFIX = "boolean__";
private HTMLApplication htmlApplication;
public JnexServlet() {
FWLog.setLogger(new ServletLogger());
}
class ServletLogger implements Logger {
protected boolean on = false;
public void debug(String info) {
log("[DEBUG] " + info);
}
public void debug(String info, Exception ex) {
if (on) {
JnexServlet.this.log("[DEBUG] " + info, ex);
}
}
public void log(String info) {
if (on) {
JnexServlet.this.log(info);
}
}
public void setDebugging(boolean on) {
this.on = on;
}
}
/**
* This is the only method which needs to implemented in a sub-class. The servlet always uses the same instance of the
* specified HTMLApplication (for all users).
*/
public abstract HTMLApplication createHTMLApplication();
public HTMLApplication getHTMLApplication() {
if (htmlApplication == null) {
htmlApplication = createHTMLApplication();
}
return htmlApplication;
}
/**
* When the user session is started, then a new HTMLSessionVariable instance is generated (method:init()) and stored in the HTTP session.
* The session variable is the only instance, which is allowed to hold user specific data.
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HTMLApplication application = getHTMLApplication();
HttpSession session = application.getSession(request);
String sessionVariableName = getSessionVariableName(application);
HTMLSessionVariable sessionVariable = (HTMLSessionVariable) session.getAttribute(sessionVariableName);
try {
if (sessionVariable == null) {
htmlApplication.preInit();
sessionVariable = application.init();
FWLog.debug("NEW SESSION CREATED");
session.setAttribute(sessionVariableName, sessionVariable);
htmlApplication.postInit(sessionVariable);
}
synchronized (sessionVariable) {
sessionVariable.clearSelectedChildren();
sessionVariable.init(request, response);
HTMLState oldState = null;
HTMLState actState = null;
oldState = sessionVariable.getActState();
if (sessionVariable.getEvent() != null && sessionVariable.getEvent().equals(BackButtonHTMLFunction.BACK_EVENT)) {
oldState = sessionVariable.getActState();
sessionVariable.goBackOneState();
} else {
if (sessionVariable.getState() != null && sessionVariable.getState().length() > 0 && !sessionVariable.getState().equals(oldState.getName()))
{
// try to walk back to the requested state
oldState = sessionVariable.goBackwardState();
} else if (sessionVariable.getState() == null) {
sessionVariable.resetStateMachine();
oldState = sessionVariable.getActState();
}
}
FWLog.debug("OLD STATE = " + oldState.getName());
HTMLCommand command = application.getHTMLCommand(oldState.getName(), sessionVariable.getEvent());
boolean doSwitchState = true;
if (command == null) {
// check whether there is a default command registered with no specifc start state
command = application.getDefaultHTMLCommand(sessionVariable.getEvent());
}
if (command != null) {
if (command.requiresMapping()) {
DataModel actModel = sessionVariable.getActState().getModel();
if (actModel != null) {
mapRequestParametersToModel(request, actModel);
}
}
try {
FWLog.debug("execute command " + command.getClass());
doSwitchState = command.execute(sessionVariable);
// do not try to create a response, if the command handles the response itself
if (command.handlesResponse()) {
return;
}
} catch (Exception e) {
application.handleException(sessionVariable, e);
}
}
if (doSwitchState) {
HTMLTransition defaultTransition = application.getDefaultHTMLTransition(sessionVariable.getEvent());
HTMLState targetState = defaultTransition == null ? null : defaultTransition.getTargetState();
if (BackButtonHTMLFunction.BACK_EVENT.equals(sessionVariable.getEvent())) {
targetState = sessionVariable.getActState();
FWLog.debug("targetState = " + targetState.getName());
}
if (targetState == null) {
sessionVariable.doStateTransition();
} else {
sessionVariable.getStateMachine().setActState(targetState);
}
String childName = sessionVariable.getChild();
if (childName != null) {
if (oldState.getModel() != null) {
try {
DataModel child = oldState.getModel().getChild(childName);
sessionVariable.getActState().setModel(child);
if (sessionVariable.getDuplicate()) {
sessionVariable.getActState().startDuplicatePopup();
}
} catch (Exception e) {
//ignore
}
}
}
}
actState = sessionVariable.getActState();
DataModel actModel = actState.getModel();
FWLog.debug("ACT MODEL = " + (actModel == null ? "null" : actModel.getFieldName()));
sessionVariable.storeActState();
sessionVariable.getResolver().getVariableStore().setVariable(VariableStore.STATE, actState.getName());
sessionVariable.getResolver().getVariableStore().setVariable(VariableStore.REQUEST_URL, request.getRequestURL().toString());
sessionVariable.getResolver().setRootModel(actModel);
String template = sessionVariable.getTemplate();
if (template == null) {
template = actState.getTemplate();
}
byte reply[] = sessionVariable.getResolver().resolve(sessionVariable, actModel, template, actState.isEditor());
response.getOutputStream().write(reply);
}
} catch (Exception e) {
e.printStackTrace();
throw new ServletException(e);
}
response.getOutputStream().close();
}
public static String getSessionVariableName(HTMLApplication application) {
return SESSION_VARIABLE_PREFIX + application.getApplicationName();
}
/**
* Map the request parameters to the actual DataModel which is held in the current state (sessionVariable.getActState().getModel()).
* In general the name and values of the parameters are used like this: actModel.setChildText(parameterName, value);
*
* @param request
* @param actModel
*/
public static void mapRequestParametersToModel(HttpServletRequest request, DataModel actModel) {
Enumeration enumVar = request.getParameterNames();
String falseParameter = null;
while (enumVar.hasMoreElements()) {
String parameterName = (String) enumVar.nextElement();
String value = request.getParameter(parameterName);
if (falseParameter != null && !falseParameter.equals(parameterName)) {
try {
((BooleanModel) actModel.getChild(falseParameter)).setBoolean(false);
falseParameter = null;
} catch (Throwable ex) {
// ignore
}
}
if (parameterName.startsWith(HTMLFunction.PARAMETER_BUTTON)) {
continue;
} else if (parameterName.equals("this")) {
parameterName = null;
} else if (parameterName.startsWith(BOOLEAN_PARAMETER_PREFIX)) {
falseParameter = parameterName.substring(BOOLEAN_PARAMETER_PREFIX.length());
continue;
}
// check whether child is a BooleanModel
try {
DataModel child = actModel.getChild(parameterName);
if (child instanceof BooleanModel) {
((BooleanModel) child).setBoolean(true);
} else {
actModel.setChildText(parameterName, value);
}
} catch (Exception ex) {
// ignore
}
FWLog.debug("Setting child value " + parameterName + "=" + value);
}
}
}