module org/webdsl/dsl/to-java-servlet/dispatch-helper

imports
libstratego-lib
libjava-front

imports
libwebdsl-front
org/webdsl/dsl/to-java-servlet/to-java-servlet
org/webdsl/dsl/to-java-servlet/core-to-java-servlet

rules

generate-code-java-servlet-once: _ ->
<emit-java-code; fail> compilation-unit|[
package utils;

import java.io.*;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.util.Date;

import javax.servlet.*;
import javax.servlet.http.*;

import pkgname.*;
import org.webdsl.logging.Logger;
import pkgtname2.RequestLogEntry;
import pkgtname2.SessionManager;

@SuppressWarnings({"unused","unchecked","rawtypes"})
public class DispatchServletHelper extends utils.AbstractDispatchServletHelper{
static{
utils.EntityReflectionHelper.init(); // in separate file to improve caching
}
pkgtname2.SessionManager sessionManager = null;
public pkgtname2.SessionManager getSessionManager(){
return sessionManager;
}
public boolean sessionHasChanges(){
return getSessionManager().getSessionHasChanges() != null && getSessionManager().getSessionHasChanges();
}
public void setSessionHasChanges(){
if(getSessionManager() != null){ getSessionManager().setSessionHasChangesNoEventsOrValidation(true); }
}
public void reloadSessionManagerFromExistingSessionId(org.hibernate.Session hses, java.util.UUID newId){
sessionId = newId;
SessionManager oldManager = getSessionManager();
reloadSessionManager( utils.HibernateUtil.getCurrentSession() );
if(getSessionManager() != null){
getSessionManager().setLastUseNoEventsOrValidation(null);
if(oldManager != null && oldManager != getSessionManager()){
getSessionManager().getMessages().addAll( oldManager.getMessages() );
oldManager.removeAllFromMessages();
}
}
}
public void reloadSessionManager(org.hibernate.Session hses){
sessionManager = null;
isNewSessionManager = false;
loadSessionManager(hses);
}
public void loadSessionManager(org.hibernate.Session hses){
loadSessionManager(hses, null);
}
private boolean isNewSessionManager;
protected static java.util.regex.Pattern domainPattern = java.util.regex.Pattern.compile("(^\\w{0,6})://([^/]+)");
public String getDomain(){
String url = getRequestURL();
if(url == null){ return null; }
java.util.regex.Matcher m = domainPattern.matcher(url);
m.find();
return m.group(2);
}
public void generateCookieValue(){
cookieValue = java.util.UUID.randomUUID();
sessionManager.setCookieValueNoEventsOrValidation(cookieValue);
}
public void generateCookieValueAndPersist(){
generateCookieValue();
sessionManager.setChanged("*new cookie value*");
}
public void loadSessionManager(org.hibernate.Session hses, String[] joins) {
String domain = getDomain();
/*if(joins == null || joins.length == 0) {*/
if(sessionManager == null){
if(sessionId != null){
sessionManager = (SessionManager)hses.get(SessionManager.class, sessionId); //from org.hibernate.Session documentation: you should not use load() to determine if an instance exists (use get() instead)
if(sessionManager != null && sessionManager.getDomain() == null){ // migration of old instances
sessionManager.setDomainNoEventsOrValidation(domain);
}
if(sessionManager != null && sessionManager.getCookieValue() == null){ // migration of old instances
renewCookieValue();
}
}
}
/*} else {
java.util.List<SessionManager> results = utils.QueryOptimization.addJoinsIfOptimizationEnabled(hses.createCriteria(SessionManager.class), joins)
.add( org.hibernate.criterion.Restrictions.idEq(sessionId) )
.list();
if(results.size() != 0){
sessionManager = results.get(0);
}
}*/
if(sessionManager == null || !sessionManager.getDomain().equals(domain) || !sessionManager.getCookieValue().equals(cookieValue) ){
~*<![];if-debug(!bstm*|[ org.webdsl.logging.Logger.info("session entity could not be loaded, creating new"); ]|)>
isNewSessionManager = true;
sessionManager = new SessionManager();
sessionId = java.util.UUID.randomUUID(); //generate a new sessionId, not using the one supplied in cookie (prevents session fixation)
sessionManager.setId(sessionId);
sessionManager.setLastUseNoEventsOrValidation(new java.util.Date());
sessionManager.setDomainNoEventsOrValidation(domain);
generateCookieValue();
}
}
public void setCookie(org.hibernate.Session hses){
//internalUpdateSessionManagerTimeout is defined in WebDSL code (built-in.app)
boolean sendCookie = false;

if( isNewSessionManager ){
if(( sessionHasChanges() || !getOutgoingSuccessMessages().isEmpty()) ){
//only persist session manager and send initial cookie when session data has changed
sessionManager.setVersion(1); // mark as persisted
sessionManager.setChanged("*new session*");
hses.save(sessionManager);
// new SessionManager means new Cookie
// response.setHeader(setCookie, basicCookieInfo);
webdsl.generated.functions.internalUpdateSessionManagerTimeout_.internalUpdateSessionManagerTimeout_();
sendCookie = true;
}
} else {
sendCookie = webdsl.generated.functions.internalUpdateSessionManagerTimeout_.internalUpdateSessionManagerTimeout_();
if( mustRenewCookieValue ){
sendCookie = true;
}
}



if(sendCookie){
String basicCookieInfo = "WEBDSLSESSIONID="
+ utils.UUIDUserType.persistUUIDString(sessionId)
+ utils.UUIDUserType.persistUUIDString(sessionManager.getCookieValue())
+ "; HttpOnly; path=/";
String setCookie = "SET-COOKIE";
if(sessionManager.getStayLoggedIn() != null && sessionManager.getStayLoggedIn()){
Date expdate = utils.DateType.addMonths(new Date(), 6);
DateFormat df = new SimpleDateFormat("dd MMM yyyy kk:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
basicCookieInfo += "; expires=" + df.format(expdate);
}
response.setHeader(setCookie, basicCookieInfo);
}
}
public void storeOutgoingMessagesInHttpSession(boolean dropOldMessages){
if(sessionManager.getMessages().size() > 0 && dropOldMessages){
sessionManager.removeAllFromMessages();
}
if(outgoingSuccessMessages.size() > 0){
//session.setAttribute("___messages___",outgoingSuccessMessages);
for(String s : outgoingSuccessMessages){
pkgtname2.SessionMessage m = ambname2.SessionMessage._static_createEmpty_();
m.setVersion(1); // mark as persisted
m.setTextNoEventsOrValidation(s);
sessionManager.getMessages().add(m);
}
}
}
public void retrieveIncomingMessagesFromHttpSession(){
/*java.util.List<String> temp = null;//(java.util.List<String>) session.getAttribute("___messages___");
if(temp != null){
incomingSuccessMessages = temp;
}*/
for(pkgtname2.SessionMessage m : sessionManager.getMessages()){
if(m != null) { incomingSuccessMessages.add(m.getText()); }
}
}
//java.util.List<String> messages;
RequestLogEntry requestLogEntry = null;
public RequestLogEntry getRequestLogEntry(){
return requestLogEntry;
}

static
{
try{
java.util.Properties props = new java.util.Properties();
props.load(utils.DispatchServletHelper.class.getResourceAsStream("/tomcat.properties"));
httpsPort = Integer.parseInt(props.getProperty("webdsl.tomcat.https.port"));
httpPort = Integer.parseInt(props.getProperty("webdsl.tomcat.http.port"));
}
catch(Exception ex){
org.webdsl.logging.Logger.error("Could not read tomcat.properties file, custom http(s) port settings are ignored in the currently deployed application.");
}
}

DispatchServlet servlet;

public DispatchServletHelper(utils.DispatchServlet servlet, boolean isPost, String contextPath) {
this.servlet = servlet;
this.isPostRequest = isPost;
this.contextPath = contextPath;
}

DispatchServlet getServlet() { return servlet; }

static{
bstm*
bstm1*
}


/**
* uses separate session/transaction to push log to database
*/
/*public void storeRequestLogEntryinSeparateSession(){
if(utils.BuildProperties.isRequestLoggingEnabled()){
org.hibernate.Session hibSession = utils.HibernateUtil.getCurrentSession();
try
{
hibSession.beginTransaction();
storeRequestLogEntry(hibSession);
hibSession.getTransaction().commit();
}
catch(Exception se)
{
org.webdsl.logging.Logger.error("Exception occurred while storing request log entry.");
org.webdsl.logging.Logger.error("EXCEPTION",se);
hibSession.getTransaction().rollback();
}
}
}*/
public void storeRequestLogEntry(org.hibernate.Session hibSession){
hibSession.saveOrUpdate(requestLogEntry);
}
public void setEndTimeAndStoreRequestLog(org.hibernate.Session hibSession){
if(utils.BuildProperties.isRequestLoggingEnabled()){
requestLogEntry.setEndNoEventsOrValidation(new java.util.Date());
storeRequestLogEntry(hibSession);
}
}

public void doServe(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException
{
requestLogEntry = RequestLogEntry._static_createEmpty_();
requestLogEntry.setStartNoEventsOrValidation(new java.util.Date());

if(request.getCookies() != null) {
for(Cookie c : request.getCookies()){

if("WEBDSLSESSIONID".equals(c.getName())){
try{
sessionId = utils.UUIDUserType.retrieveUUID(c.getValue().substring(0,32));
if( c.getValue().length() == 64 ){
cookieValue = utils.UUIDUserType.retrieveUUID(c.getValue().substring(32,64));
}
}
catch(Exception ex){
org.webdsl.logging.Logger.error("Could not create UUID from String, new session will be created");
}
}
}
}

this.request=request;
this.response=response;
response.setCharacterEncoding("UTF-8");

utils.ThreadLocalServlet.set(this);

//org.webdsl.logging.Logger.error("dispatch" + getRequestURL());

boolean showerrorpage = false;
boolean showvalidationerror = false;
utils.MultipleValidationExceptions validationExceptions = null;
try{
String url = getRequestURL();
this.baseUrl = url.substring(0, url.length() - getRequestURI().length()) + request.getContextPath() + "/";
String pageAndArgs = url.substring(baseUrl.length());
String[] req = pageAndArgs.split("/");

/*
for(int ci=0; ci < req.length; ci++) {
//it is an issue which replacements should be applied in input and output of URLS
//, since urlencoding interprets + as a space,
//but in the output of applications + signs seems not to be converted %2B....
// + to %2B
// + to ' '
// ' ' to %20
// + to '%20'
req[ci] = paramDecode(request, java.net.URLDecoder.decode(req[ci].replaceAll("\\+", "%2B"), "ISO-8859-1"));
}
*/
// int count;
// boolean first = false;

// if(this.contextPath.length() > 1){ // not deployed as ROOT.war
// for(count=0;count<req.length;count++)
// {
// if(req[count].equals(this.contextPath))
// {
// count++;
// break;
// }
// }
// }
// else{
// count = 3;
// }

String[] argnames;
String[] args;
urlComponents = new java.util.ArrayList<String>();

if(pageAndArgs.length() < 1) { // root page, no page name
requested = "root";
args = new String[0]; // root page takes no arguments
urlComponents.add("root");
} else {
requested = req[0];
args = java.util.Arrays.copyOfRange(req, 1, req.length);
urlComponents.add(requested);
urlComponents.addAll(java.util.Arrays.asList(args));
}

// page dispatch custom routing rules
java.util.List<String> customRoutingList = Routing.processRequest(urlComponents);
if(customRoutingList != null){
requested = customRoutingList.get(0);
customRoutingList.remove(0);
args = new String[customRoutingList.size()];
customRoutingList.toArray(args);
}

fileUploads = new java.util.HashMap<String, java.util.List<utils.File>>();
parammap = new java.util.HashMap<String, String>();
parammapvalues = new java.util.HashMap<String, java.util.List<String>>();

utils.PageDispatch pd = pages.get(requested);
if(pd == null) {
requested = "pagenotfound";
pd = pages.get(requested);
pc = pd.getPageClass(); //pagenotfound is always generated
}
else{
pc = pd.getPageClass();
}

// retrieve the page argument names to make one map of url arguments and GET parameters
argnames = pages.get(requested).getArgs();

int count2 = 0;
for(count2 = 0; count2<args.length;count2++)
{
if(count2 >= argnames.length){
/*
// previous message implementation appended messages at end of url, might still be useful for variable argcount
try {
messages.add(java.net.URLDecoder.decode(args[count2],"UTF-8"));
} catch (java.io.UnsupportedEncodingException uee) {
org.webdsl.logging.Logger.error("EXCEPTION",uee);
}*/
}
else
{
String decoded = utils.URLFilter.unfilter(args[count2]);
parammap.put(argnames[count2],decoded);
addToValues(argnames[count2],decoded,parammapvalues);
}
}

for(java.util.Enumeration en = request.getParameterNames();en.hasMoreElements(); ){
String parameterName = (String)en.nextElement();
parammap.put(parameterName,paramDecode(request, request.getParameter(parameterName)));
// parammap.put(parameterName,request.getParameter(parameterName));
for(String paramval : request.getParameterValues(parameterName)){
addToValues(parameterName, paramDecode(request,paramval), parammapvalues);
// addToValues(parameterName, paramval, parammapvalues);
}
}

if(count2 < argnames.length){
if(!isPostRequest && parammap.get("~<action-call-with-get-param-name>") == null){
//ajax calls, which are always post, only have the page name in the url
//action-call-with-get-param-name is used in imageOutput, to indicate a 'get' request containing an action call
if(unspecifiedArgumentsContainEntityTypes(argnames,count2)){
throw new utils.TooFewArgumentsException();
}
}
}

//http://commons.apache.org/fileupload/using.html
// Check that we have a file upload request
boolean isMultipart = org.apache.commons.fileupload.servlet.ServletFileUpload.isMultipartContent(request);
if(isMultipart){

// Create a factory for disk-based file items
org.apache.commons.fileupload.FileItemFactory factory = new org.apache.commons.fileupload.disk.DiskFileItemFactory();

// Set factory constraints
//factory.setSizeThreshold(yourMaxMemorySize);//make configurable
//factory.setRepository(yourTempDirectory);//make configurable

// Create a new file upload handler
org.apache.commons.fileupload.servlet.ServletFileUpload upload = new org.apache.commons.fileupload.servlet.ServletFileUpload(factory);

// Set overall request size constraint
//upload.setSizeMax(); //make configurable

// Parse the request
try {
java.util.List<org.apache.commons.fileupload.FileItem> items = (java.util.List<org.apache.commons.fileupload.FileItem>) upload.parseRequest(request);

// Process the uploaded items
java.util.Iterator<org.apache.commons.fileupload.FileItem> iter = items.iterator();
while (iter.hasNext()) {
org.apache.commons.fileupload.FileItem item = (org.apache.commons.fileupload.FileItem) iter.next();

if (item.isFormField()) {
String name = item.getFieldName();
String value = item.getString();
parammap.put(name, paramDecode(request,value));
addToValues(name, paramDecode(request,value), parammapvalues);
//parammap.put(name, value);
//addToValues(name, value, parammapvalues);
} else {
String fieldName = item.getFieldName();
String fileName = item.getName();
String contentType = item.getContentType();
boolean isInMemory = item.isInMemory();
long sizeInBytes = item.getSize();
utils.File temp = new utils.File();
temp.setFileName(fileName);
temp.setContentType(contentType);

temp.setContentStream(item.getInputStream());

java.util.List<utils.File> files = fileUploads.get(fieldName);
if(files == null){
files = new java.util.ArrayList<utils.File>();
fileUploads.put(fieldName, files);
}
files.add(temp);
}
}
} catch (org.apache.commons.fileupload.FileUploadException ex) {
org.webdsl.logging.Logger.error("exception occurred in file upload handling DispatchServlet");
org.webdsl.logging.Logger.error("EXCEPTION",ex);
}
}

if(utils.BuildProperties.isRequestLoggingEnabled()){
requestLogEntry.setRequestedURLNoEventsOrValidation(getRequestURL().toString());
requestLogEntry.setNameNoEventsOrValidation(requested);
requestLogEntry.setClientIPNoEventsOrValidation(request.getRemoteAddr());
requestLogEntry.setClientPortNoEventsOrValidation(request.getRemotePort());
if(this.isPostRequest){
requestLogEntry.setMethodNoEventsOrValidation("POST");
}
else{
requestLogEntry.setMethodNoEventsOrValidation("GET");
}
requestLogEntry.setUserAgentNoEventsOrValidation(request.getHeader("User-Agent"));
requestLogEntry.setRefererNoEventsOrValidation(request.getHeader("Referer"));
}
showerrorpage = !handlePage();
}
catch(utils.TooFewArgumentsException ex){
~*<![];if-debug(!bstm*|[
org.webdsl.logging.Logger.error("Too few arguments in page request, showing 404.");
]|)>
showerrorpage=true;
}
catch(utils.AjaxWithGetRequestException ex){
~*<![];if-debug(!bstm*|[
org.webdsl.logging.Logger.info("Ajax template request using 'GET' method is not allowed, showing 404.");
]|)>
showerrorpage=true;
}
catch(utils.MultipleValidationExceptions ex){
showvalidationerror = true;
validationExceptions = ex;
}
catch(Exception ex){
org.webdsl.logging.Logger.error("Exception in dispatch servlet: "+ex.getMessage());
org.webdsl.logging.Logger.error("EXCEPTION",ex);
response.setStatus(503);
response.getWriter().write("<h3>503 Service Unavailable</h3>");
}

if(showerrorpage){
if(parammap.get("__ajax_runtime_request__") == null){ // ajax runtime expects JSON, not regular page content
pc = pages.get("pagenotfound").getPageClass();

// disable page cache when rendering pagenotfound page, otherwise it is placed under the wrong key in the cache because request url is still requested page
disablePageCache = true;
try{
if(!handlePage()){
response.setStatus(404);
response.getWriter().write("<h3>404 Not Found</h3>");
}
}
catch(Exception ex){
org.webdsl.logging.Logger.error("couldn't render pagenotfound page", ex);
response.setStatus(404);
response.getWriter().write("<h3>404 Not Found</h3>");
}
}
} else if(showvalidationerror){
if(parammap.get("__ajax_runtime_request__") == null){ // ajax runtime expects JSON, not regular page content
response.setStatus(500);
response.getWriter().write( utils.ValidationErrorPopupCreator.getErrorPopup( validationExceptions ) );
}
}

cleanupThreadLocals();
}

private boolean handlePage() throws Exception{
//public int retries = 0; in AbstractDispatchServletHelper.java
while(retries <= AbstractDispatchServletHelper.maxRetries){
if(pc != null)
{
try
{
utils.PageServlet pageservlet = (utils.PageServlet)pc.newInstance();
if(disablePageCache){
pageservlet.isPageCacheDisabled = true;
}

//pageservlet.getIncomingSuccessMessages().addAll(messages);
sessionManager = null;
pageservlet.serve(request, response, parammap, parammapvalues, fileUploads);
bstm_buildid*
}
catch(IllegalAccessException iae)
{
org.webdsl.logging.Logger.error("Problem in dispatch servlet page lookup: " + iae.getMessage());
org.webdsl.logging.Logger.error("EXCEPTION",iae);
return false;
}
catch(InstantiationException ie)
{
org.webdsl.logging.Logger.error("Problem in dispatch servlet page lookup: " + ie.getMessage());
org.webdsl.logging.Logger.error("EXCEPTION",ie);
return false;
}
catch(utils.MultipleValidationExceptions mve ){
throw mve; //just rethrow so caller can handle validation exceptions
}
catch(utils.EntityNotFoundException enfe){
//already logged in AbstractPageServlet
return false;
}
catch (Exception re) {
Logger.warn(re);
retries++;
if(re instanceof org.hibernate.StaleObjectStateException && retries <= AbstractDispatchServletHelper.maxRetries){
Logger.warn("retry request attempt "+retries);
continue;
}
else{
throw re;
}
}
}
else
{
return false;
}
return true;
}
return false; //after retries
}
}
]|
with pkgname := <TemplatePackage>
; pkgtname2 := <DomainPackage>
; ambname2 := pkgtname2
; bstm* := <mapconcat({\
x -> bstm* |[
{
String[] s = { e* };
Boolean[] b = { e1* };
pages.put(x_key, new utils.PageDispatch(x_value, s, b, false));
}
]|
with x_key := <concat-strings> ["\"",<string-replace(|"$Override$","")>x,"\""]
; x_value := <concat-strings> [<java-servlet-page-class-name> x,".class"]
; e1*:= <TemplateFormalArguments;map(true-when-entity-type-arg)> x
; e* := <TemplateFormalArguments;map( \Arg(y,_)-> e|[ "~<get-original-name> y" ]|\ )> x
//TemplateFormalArguments is defined in back-end after rename of templates and variables, for vars must give unrenamed versions so get-original-name is used
\})>
<bagof-AllPageNames; handle-overrides-by-name>
; bstm1* := <mapconcat({\
x -> bstm* |[
{
String[] s = { e* };
Boolean[] b = { e1* };
pages.put(x_key, new utils.PageDispatch(x_value, s, b, true));
}
]|
with x_key := <concat-strings> ["\"",<string-replace(|"$Override$","")>x,"\""]
; x_value := <concat-strings> [<java-servlet-page-class-name> x,".class"]
; e1*:= <TemplateFormalArguments;map(true-when-entity-type-arg)> x
; e* := <TemplateFormalArguments;map( \Arg(y,_)-> e|[ "~<get-original-name> y" ]|\ )> x // must use unrenamed versions
\})>
<bagof-AllTopLevelTemplateNamesWithAjax; handle-overrides-by-name>
; if build-id := <BuildIdOption> then
build-id-request-var := <build-id-request-var-name>
; bstm_buildid* :=
bstm*|[
if(parammap.get("~build-id-request-var")!=null){
try {
response.getWriter().write("build-id:"+"~build-id");
} catch (java.io.IOException ioe) {
org.webdsl.logging.Logger.error("EXCEPTION",ioe);
}
}
]|
else
bstm_buildid* := []
end


true-when-entity-type-arg :
Arg(_,srt)-> e|[ true ]|
where <has-entity-anno> srt
true-when-entity-type-arg :
Arg(_,srt)-> e|[ false ]|
where not(<has-entity-anno> srt)

handle-overrides-by-name = string-sort-desc-annos // add the overrides to hashmap last to override original entry