mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
First cut of the AJAX framework
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3327 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
package org.alfresco.web.app.servlet.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Interface for all Ajax commands executed by this servlet.
|
||||
*
|
||||
* The method is responsible for invoking the underlying managed bean
|
||||
* and dealing with the response.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public interface AjaxCommand
|
||||
{
|
||||
/**
|
||||
* Invokes the relevant method on the bean represented by the given
|
||||
* expression. Parameters required to call the method can be retrieved
|
||||
* from the request.
|
||||
*
|
||||
* Currently the content type of the response will always be text/html, in the
|
||||
* future sublcasses may provide a mechanism to allow the content type to be set
|
||||
* dynamically.
|
||||
*
|
||||
* @param facesContext FacesContext
|
||||
* @param expression The binding expression
|
||||
* @param request The request
|
||||
* @param response The response
|
||||
*/
|
||||
public abstract void execute(FacesContext facesContext, String expression,
|
||||
HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException;
|
||||
}
|
176
source/java/org/alfresco/web/app/servlet/ajax/AjaxServlet.java
Normal file
176
source/java/org/alfresco/web/app/servlet/ajax/AjaxServlet.java
Normal file
@@ -0,0 +1,176 @@
|
||||
package org.alfresco.web.app.servlet.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.web.app.servlet.AuthenticationStatus;
|
||||
import org.alfresco.web.app.servlet.BaseServlet;
|
||||
import org.alfresco.web.app.servlet.FacesHelper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Servlet responsible for processing AJAX requests.
|
||||
*
|
||||
* The URL to the servlet should be in the form:
|
||||
* <pre>/alfresco/ajax/command/Bean.binding.expression</pre>
|
||||
* <p>
|
||||
* where 'command' is one of 'invoke', 'get' or 'set'.
|
||||
* <p>
|
||||
* TODO: Explain what the commands do...
|
||||
* <p>
|
||||
* Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication:
|
||||
* ?ticket=1234567890
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class AjaxServlet extends BaseServlet
|
||||
{
|
||||
public static final String AJAX_LOG_KEY = "alfresco.ajax";
|
||||
|
||||
protected enum Command { invoke, get, set};
|
||||
|
||||
private static final long serialVersionUID = -7654769105419391840L;
|
||||
private static Log logger = LogFactory.getLog(AJAX_LOG_KEY);
|
||||
private static Log headersLogger = LogFactory.getLog(AJAX_LOG_KEY + ".headers");
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
String uri = request.getRequestURI();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
String queryString = request.getQueryString();
|
||||
logger.debug("Processing URL: " + uri +
|
||||
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
|
||||
}
|
||||
|
||||
// dump the request headers
|
||||
if (headersLogger.isDebugEnabled())
|
||||
{
|
||||
Enumeration headers = request.getHeaderNames();
|
||||
while (headers.hasMoreElements())
|
||||
{
|
||||
String name = (String)headers.nextElement();
|
||||
headersLogger.debug(name + ": " + request.getHeader(name));
|
||||
}
|
||||
}
|
||||
|
||||
// ************
|
||||
// TODO: Need to send in a flag to method to stop it from redirecting
|
||||
// to login page, we can then throw an error in here!!
|
||||
|
||||
AuthenticationStatus status = servletAuthenticate(request, response);
|
||||
if (status == AuthenticationStatus.Failure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uri = uri.substring(request.getContextPath().length());
|
||||
StringTokenizer t = new StringTokenizer(uri, "/");
|
||||
int tokenCount = t.countTokens();
|
||||
if (tokenCount < 3)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Servlet URL did not contain all required args: " + uri);
|
||||
}
|
||||
|
||||
// skip the servlet name
|
||||
t.nextToken();
|
||||
|
||||
// retrieve the command from the URL
|
||||
String commandName = t.nextToken();
|
||||
|
||||
// retrieve the binding expression from the URL
|
||||
String expression = t.nextToken();
|
||||
|
||||
// setup the faces context
|
||||
FacesContext facesContext = FacesHelper.getFacesContext(request, response, getServletContext());
|
||||
|
||||
// instantiate the relevant command
|
||||
AjaxCommand command = null;
|
||||
if (Command.invoke.toString().equals(commandName))
|
||||
{
|
||||
command = new InvokeCommand();
|
||||
}
|
||||
else if (Command.get.toString().equals(commandName))
|
||||
{
|
||||
command = new GetCommand();
|
||||
}
|
||||
// else if (Command.set.toString().equals(commandName))
|
||||
// {
|
||||
// command = new SetCommand();
|
||||
// }
|
||||
else
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Unrecognised command received: " + commandName);
|
||||
}
|
||||
|
||||
// execute the command
|
||||
command.execute(facesContext, expression, request, response);
|
||||
}
|
||||
catch (RuntimeException error)
|
||||
{
|
||||
handleError(response, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles any error that occurs during the execution of the servlet
|
||||
*
|
||||
* @param response The response
|
||||
* @param cause The cause of the error
|
||||
*/
|
||||
protected void handleError(HttpServletResponse response, RuntimeException cause)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
// if we can send back the 500 error with the error from the top of the
|
||||
// stack as the error status message.
|
||||
|
||||
// NOTE: if we use the built in support for generating error pages for
|
||||
// 500 errors we can tailor the output for AJAX calls so that the
|
||||
// body of the response can be used to show the error details.
|
||||
|
||||
if (!response.isCommitted())
|
||||
{
|
||||
// dump the error if debugging is enabled
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.error(cause);
|
||||
Throwable theCause = cause.getCause();
|
||||
if (theCause != null)
|
||||
{
|
||||
logger.error("caused by: ", theCause);
|
||||
}
|
||||
}
|
||||
|
||||
// extract a message from the exception
|
||||
String msg = cause.getMessage();
|
||||
if (msg == null)
|
||||
{
|
||||
msg = cause.toString();
|
||||
}
|
||||
|
||||
// send the error
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the response has already been comitted, not much we can do but
|
||||
// let the error through and let the container deal with it
|
||||
throw cause;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package org.alfresco.web.app.servlet.ajax;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Base class for all Ajax based commands
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public abstract class BaseAjaxCommand implements AjaxCommand
|
||||
{
|
||||
protected static Log logger = LogFactory.getLog(AjaxServlet.AJAX_LOG_KEY);
|
||||
|
||||
public String makeBindingExpression(String expression)
|
||||
{
|
||||
return "#{" + expression + "}";
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package org.alfresco.web.app.servlet.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
|
||||
/**
|
||||
* Command that executes the given value binding expression.
|
||||
* <p>
|
||||
* This command is intended to be used for calling existing managed
|
||||
* bean methods. The result of the value binding is added to
|
||||
* the response as is i.e. by calling toString().
|
||||
* The content type of the response is always text/html.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class GetCommand extends BaseAjaxCommand
|
||||
{
|
||||
public void execute(FacesContext facesContext, String expression,
|
||||
HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
// create the JSF binding expression
|
||||
String bindingExpr = makeBindingExpression(expression);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Retrieving value from value binding: " + bindingExpr);
|
||||
|
||||
UserTransaction tx = null;
|
||||
try
|
||||
{
|
||||
// create the value binding
|
||||
ValueBinding binding = facesContext.getApplication().
|
||||
createValueBinding(bindingExpr);
|
||||
|
||||
if (binding != null)
|
||||
{
|
||||
// setup the transaction
|
||||
tx = Repository.getUserTransaction(facesContext, true);
|
||||
tx.begin();
|
||||
|
||||
// get the value from the value binding
|
||||
Object value = binding.getValue(facesContext);
|
||||
response.getWriter().write(value.toString());
|
||||
|
||||
// commit
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
// rollback the transaction
|
||||
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
|
||||
|
||||
throw new AlfrescoRuntimeException("Failed to retrieve value: " + err.getMessage(), err);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
package org.alfresco.web.app.servlet.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import javax.faces.FactoryFinder;
|
||||
import javax.faces.component.UIViewRoot;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.MethodBinding;
|
||||
import javax.faces.render.RenderKit;
|
||||
import javax.faces.render.RenderKitFactory;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
|
||||
/**
|
||||
* Command that invokes the method represented by the expression.
|
||||
* <p>
|
||||
* The managed bean method called is responsible for writing the response
|
||||
* by getting hold of the JSF ResponseWriter. Parameters can also be
|
||||
* retrieved via the JSF ExternalContext object.
|
||||
* <p>
|
||||
* In a future release (if required) annotations may be used to state
|
||||
* what content type to use for the response.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class InvokeCommand extends BaseAjaxCommand
|
||||
{
|
||||
public void execute(FacesContext facesContext, String expression,
|
||||
HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
// setup the JSF response writer.
|
||||
|
||||
// NOTE: it doesn't seem to matter what the content type of the response is (at least with Dojo),
|
||||
// it determines it's behaviour from the mimetype specified in the AJAX call on the client,
|
||||
// therefore, for now we will always return a content type of text/html.
|
||||
// In the future we may use annotations on the method to be called to specify what content
|
||||
// type should be used for the response.
|
||||
|
||||
OutputStream os = response.getOutputStream();
|
||||
UIViewRoot viewRoot = facesContext.getViewRoot();
|
||||
RenderKitFactory renderFactory = (RenderKitFactory)FactoryFinder.
|
||||
getFactory(FactoryFinder.RENDER_KIT_FACTORY);
|
||||
RenderKit renderKit = renderFactory.getRenderKit(facesContext,
|
||||
viewRoot.getRenderKitId());
|
||||
ResponseWriter writer = renderKit.createResponseWriter(
|
||||
new OutputStreamWriter(os), MimetypeMap.MIMETYPE_HTML, "UTF-8");
|
||||
facesContext.setResponseWriter(writer);
|
||||
response.setContentType(writer.getContentType());
|
||||
|
||||
// create the JSF binding expression
|
||||
String bindingExpr = makeBindingExpression(expression);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Invoking method represented by " + bindingExpr);
|
||||
|
||||
UserTransaction tx = null;
|
||||
try
|
||||
{
|
||||
// create the method binding from the expression
|
||||
MethodBinding binding = facesContext.getApplication().createMethodBinding(
|
||||
bindingExpr, new Class[] {});
|
||||
|
||||
if (binding != null)
|
||||
{
|
||||
// setup the transaction
|
||||
tx = Repository.getUserTransaction(facesContext);
|
||||
tx.begin();
|
||||
|
||||
// invoke the method
|
||||
binding.invoke(facesContext, new Object[] {});
|
||||
|
||||
// commit
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
// rollback the transaction
|
||||
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
|
||||
|
||||
throw new AlfrescoRuntimeException("Failed to execute method: " + err.getMessage(), err);
|
||||
}
|
||||
|
||||
// force the output back to the client
|
||||
writer.close();
|
||||
}
|
||||
}
|
51
source/java/org/alfresco/web/bean/ajax/BaseAjaxBean.java
Normal file
51
source/java/org/alfresco/web/bean/ajax/BaseAjaxBean.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package org.alfresco.web.bean.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
|
||||
/**
|
||||
* Base class for all Ajax managed beans.
|
||||
*
|
||||
* It is not necessary to extend this bean but it provides
|
||||
* helper methods.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public abstract class BaseAjaxBean
|
||||
{
|
||||
private ResponseWriter writer;
|
||||
|
||||
/**
|
||||
* Writes the given string to the response writer for this bean
|
||||
*
|
||||
* @param str The string to send back to the client
|
||||
*/
|
||||
public void write(String str)
|
||||
{
|
||||
try
|
||||
{
|
||||
getWriter().write(str);
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
// not much we can do here, ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ResponseWriter for this bean
|
||||
*
|
||||
* @return The JSF ResponseWriter
|
||||
*/
|
||||
public ResponseWriter getWriter()
|
||||
{
|
||||
if (this.writer == null)
|
||||
{
|
||||
this.writer = FacesContext.getCurrentInstance().getResponseWriter();
|
||||
}
|
||||
|
||||
return this.writer;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user