1 package net.trajano.twiff.internal.servlet;
2
3 import java.beans.BeanInfo;
4 import java.beans.Introspector;
5 import java.beans.PropertyDescriptor;
6 import java.io.IOException;
7 import java.io.Serializable;
8 import java.net.URL;
9 import javax.servlet.ServletException;
10 import javax.servlet.http.HttpServlet;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13 import net.trajano.twiff.ActionPageLookup;
14 import net.trajano.twiff.ActionResultProcessor;
15 import net.trajano.twiff.Configuration;
16 import net.trajano.twiff.InstantiationException;
17 import net.trajano.twiff.UnprocessedException;
18 import net.trajano.twiff.internal.ServletContextAdapterImpl;
19 import net.trajano.twiff.internal.token.dao.SessionTokenDao;
20 import net.trajano.twiff.internal.webxml.WebXmlAdapter;
21 import org.apache.commons.beanutils.BeanUtils;
22 import org.hibernate.Session;
23 import org.hibernate.SessionFactory;
24 import org.hibernate.Transaction;
25
26 /***
27 * This implements a form handler that does not do the post-redirect-get
28 * pattern. This should not be used normally unless it is not possible to have a
29 * datastore on the web tier.
30 *
31 * @author Archimedes Trajano
32 */
33 public final class ActionServlet extends HttpServlet {
34 /***
35 * Location of web.xml in servlet context.
36 */
37 private static final String WEB_XML = "/WEB-INF/web.xml";
38
39 /***
40 * @param configuration
41 * @param pageLookup
42 * @param actionResultProcessors
43 * @param webXmlAdapter
44 */
45 public ActionServlet(final Configuration configuration, final ActionPageLookup pageLookup, final ActionResultProcessor[] actionResultProcessors, final WebXmlAdapter webXmlAdapter) {
46 this.pageLookup = pageLookup;
47 this.configuration = configuration;
48 this.actionResultProcessors = actionResultProcessors;
49 this.webXmlAdapter = webXmlAdapter;
50 }
51
52 /***
53 * The webXml adapter.
54 */
55 private final WebXmlAdapter webXmlAdapter;
56
57 /***
58 * Action processors.
59 */
60 private final ActionResultProcessor[] actionResultProcessors;
61
62 /***
63 * Configuration.
64 */
65 private final Configuration configuration;
66
67 /***
68 * Lookup.
69 */
70 private final ActionPageLookup pageLookup;
71
72 /***
73 * Handles POST requests.
74 *
75 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
76 * javax.servlet.http.HttpServletResponse)
77 */
78 protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
79 ServletContextAdapterImpl adapter = new ServletContextAdapterImpl(getServletContext());
80 SessionFactory sessionFactory = adapter.getSessionFactory();
81 Session session = null;
82 try {
83 ActionServletRequestAdapter requestAdapter = new ActionServletRequestAdapter(request, configuration.getTokenFieldName());
84 session = sessionFactory.openSession();
85 if (isTokenAvailable(session, requestAdapter.getSessionId(), requestAdapter.getTokenId(), request, response)) {
86 return;
87 }
88 Class pageClass;
89 try {
90 pageClass = pageLookup.getPageBeanClass(request);
91 } catch (ClassNotFoundException e) {
92 throw new InstantiationException(e);
93 }
94 RendererUtil.registerPageBean(request, pageClass);
95 Serializable bean = (Serializable) requestAdapter.getContainer().getComponentInstanceOfType(pageClass);
96 BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
97 for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
98 if (descriptor.getWriteMethod() != null) {
99 final Object[] objects = requestAdapter.getPostData().get(descriptor.getName());
100 if (objects == null) {
101 continue;
102 } else if (objects.length == 1) {
103 BeanUtils.setProperty(bean, descriptor.getName(), objects[0]);
104 } else {
105 BeanUtils.setProperty(bean, descriptor.getName(), objects);
106 }
107 }
108 }
109 Object result = pageLookup.getActionMethod(request).invoke(bean);
110 for (ActionResultProcessor resultProcessor : actionResultProcessors) {
111 if (resultProcessor.isResultProcessable(result, request)) {
112 String newUrl = resultProcessor.processActionResult(result, request);
113 Transaction tx = session.beginTransaction();
114 SessionTokenDao dao = new SessionTokenDao(session);
115 dao.updateTokenInfo(requestAdapter.getSessionId(), requestAdapter.getTokenId(), new URL(new URL(request.getRequestURL().toString()), newUrl.toString()), bean);
116 tx.commit();
117 response.sendRedirect(response.encodeRedirectURL(newUrl));
118 return;
119 }
120 }
121 throw new UnprocessedException(result);
122 } catch (ServletException e) {
123 throw e;
124 } catch (IOException e) {
125 throw e;
126 } catch (Exception e) {
127 throw new ServletException(e);
128 } finally {
129 if (session != null) {
130 session.close();
131 }
132 }
133 }
134
135 /***
136 * This will return true if there is a token available in the database
137 * already. If not it will create one. It will also put in the redirect
138 * URLs.
139 *
140 * @param session
141 * Hibernate session
142 * @param sessionId
143 * HTTP session ID
144 * @param tokenId
145 * token ID
146 * @param request
147 * servlet request to get the context and dispatcher from
148 * @param response
149 * servlet response to put the redirect in
150 * @return true if a token was already available in the database
151 * @throws IOException
152 * @throws ServletException
153 */
154 private boolean isTokenAvailable(final Session session, final String sessionId, final String tokenId, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException {
155 Transaction tx = session.beginTransaction();
156 SessionTokenDao dao = new SessionTokenDao(session);
157 boolean ret = false;
158 if (dao.isTokenAvailable(sessionId, tokenId)) {
159 if (dao.isTokenValid(sessionId, tokenId)) {
160 response.sendRedirect(response.encodeRedirectURL(dao.getRedirectUrl(sessionId, tokenId).toExternalForm()));
161 } else {
162 StringBuffer redirectUrl = new StringBuffer();
163 redirectUrl.append(webXmlAdapter.getMappingForServlet(RedirectServlet.class));
164 redirectUrl.append('?');
165 redirectUrl.append(configuration.getTokenFieldName());
166 redirectUrl.append('=');
167 redirectUrl.append(tokenId);
168 response.sendRedirect(response.encodeRedirectURL(redirectUrl.toString()));
169 }
170 ret = true;
171 } else {
172 dao.addToken(sessionId, tokenId);
173 }
174 tx.commit();
175 return ret;
176 }
177 }