Java tutorial
/** * Copyright 2005-2013 hdiv.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hdiv.dataComposer; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; import java.util.Stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hdiv.config.HDIVConfig; import org.hdiv.idGenerator.UidGenerator; import org.hdiv.session.ISession; import org.hdiv.state.IPage; import org.hdiv.state.IParameter; import org.hdiv.state.IState; import org.hdiv.state.Page; import org.hdiv.state.Parameter; import org.hdiv.util.Constants; import org.springframework.web.util.HtmlUtils; /** * <p> * It processes the data contributed by the HDIV custom tags. The aim of this class is to create an object of type * IState for each possible request (form or link) in every page processed by the HDIV custom tags. The IState object is * used to validate client's later requests. * </p> * <p> * The process of creating an IState object is as follows: Each time a link or a form processing begins, HDIV custom * tags set the request beginning by calling beginRequest method. Once the beginning is set, an IState object is created * and it is fill in with all the data of the request(parameter values, non editable values, parameter types) using the * compose method. After processing all the request data of the link or form, custom tags set the end of the processing * by calling endRequest method. * </p> * <p> * Depending on the strategy defined in HDIV configuration (memory, cipher or hash), the IState object is stored in the * user session or is sent to the client in the html code by adding an extra parameter called _HDIV_STATE_. * </p> * <p> * In the memory strategy IState objects are stored in the user session (HttpSession) while in the cipher and hash * strategies these objects are stored in the client. * </p> * * @author Roberto Velasco */ public abstract class AbstractDataComposer implements IDataComposer { /** * Commons Logging instance. */ private static Log log = LogFactory.getLog(AbstractDataComposer.class); /** * Dash character */ protected static final String DASH = "-"; /** * Http session wrapper */ protected ISession session; /** * Unique id generator */ protected UidGenerator uidGenerator; /** * Page with the possible requests or states */ protected IPage page; /** * States stack to store all states of the page <code>page</code> */ private Stack<IState> statesStack; /** * HDIV configuration object. */ protected HDIVConfig hdivConfig; /** * DataComposer initialization with new stack to store all states of the page <code>page</code>. */ public void init() { this.setPage(new Page()); this.statesStack = new Stack<IState>(); } /** * Obtains a new unique identifier for the page. */ public void initPage() { this.page = new Page(); int pageId = this.session.getPageId(); this.page.setId(pageId); } /** * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if * confidentiality indicator <code>confidentiality</code> is true. * * @param parameter * HTTP parameter name * @param value * value generated by server * @param editable * parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @return Codified value to send to the client */ public String compose(String parameter, String value, boolean editable) { return this.compose(parameter, value, editable, false); } /** * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if * confidentiality indicator <code>confidentiality</code> is true. * * @param action * target action * @param parameter * HTTP parameter name * @param value * value generated by server * @param editable * parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @return Codified value to send to the client */ public String compose(String action, String parameter, String value, boolean editable) { return this.compose(action, parameter, value, editable, false, Constants.ENCODING_UTF_8); } /** * Adds a new IParameter object, generated from the values passed as parameters, to the current state * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the * server for the parameter <code>parameter</code> in the cipher and memory strategies. * * @param parameter * HTTP parameter * @param value * value generated by server * @param editable * Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param isActionParam * parameter added in action attribute * @return Codified value to send to the client */ public String compose(String parameter, String value, boolean editable, boolean isActionParam) { return this.compose(parameter, value, editable, isActionParam, Constants.ENCODING_UTF_8); } /** * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if * confidentiality indicator <code>confidentiality</code> is true. * * @param parameter * HTTP parameter name * @param value * value generated by server * @param editable * parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param editableName * editable name (text or textarea) * @return Codified value to send to the client * @since HDIV 1.1 */ public String compose(String parameter, String value, boolean editable, String editableName) { return this.compose(parameter, value, editable, editableName, false, null, Constants.ENCODING_UTF_8); } /** * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if * confidentiality indicator <code>confidentiality</code> is true. * * @param action * target action * @param parameter * parameter name * @param value * value generated by server * @param editable * parameter type: editable(textbox, password,etc.) or non editable (hidden, select,...) * @param isActionParam * parameter added in action attribute * @param charEncoding * character encoding * @return Codified value to send to the client */ public String compose(String action, String parameter, String value, boolean editable, boolean isActionParam, String charEncoding) { // Get current IState IState state = this.getStatesStack().peek(); if (state.getAction() != null && state.getAction().trim().length() == 0) { state.setAction(action); } return this.compose(parameter, value, editable, isActionParam, charEncoding); } /** * Adds a new IParameter object, generated from the values passed as parameters, to the current state * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the * server for the parameter <code>parameter</code> in the cipher and memory strategies. * * @param parameter * HTTP parameter * @param value * value generated by server * @param editable * Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param isActionParam * parameter added in action attribute * @param charEncoding * character encoding * @return Codified value to send to the client */ public String compose(String parameter, String value, boolean editable, boolean isActionParam, String charEncoding) { return this.compose(parameter, value, editable, null, isActionParam, null, charEncoding); } /** * Adds a new IParameter object, generated from the values passed as parameters, to the current state * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the * server for the parameter <code>parameter</code> in the cipher and memory strategies. * * @param parameter * HTTP parameter * @param value * value generated by server * @param editable * Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param editableName * editable name (text or textarea) * @param isActionParam * parameter added in action attribute * @param method * http method, GET or POST * @return Codified value to send to the client * @since HDIV 2.1.5 */ public String compose(String parameter, String value, boolean editable, String editableName, boolean isActionParam, String method) { return this.compose(parameter, value, editable, editableName, isActionParam, method, Constants.ENCODING_UTF_8); } /* * (non-Javadoc) * * @see org.hdiv.dataComposer.IDataComposer#composeParams(java.lang.String, java.lang.String, java.lang.String) */ public String composeParams(String parameters, String method, String charEncoding) { if (parameters == null || parameters.length() == 0) { return null; } String decodedParams = this.getDecodedValue(parameters, charEncoding); // Get current IState IState state = this.getStatesStack().peek(); state.setParams(decodedParams); if (this.hdivConfig.getConfidentiality()) { // replace real values with confidential ones parameters = this.applyConfidentialityToParams(parameters, method); } return parameters; } /** * Apply confidentiality to parameters String. Replaces real values with confidential ones. * * @param parameters * parameters in query format * @param method * HTTP method * @return parameters in query format with confidential values */ private String applyConfidentialityToParams(String parameters, String method) { Map<String, Integer> pCount = new HashMap<String, Integer>(); parameters = parameters.replaceAll("&", "&"); String newParameters = parameters; // Init indexes int beginIndex = 0; int endIndex = parameters.indexOf("&") > 0 ? parameters.indexOf("&") : parameters.length(); do { String param = parameters.substring(beginIndex, endIndex); int index = param.indexOf("="); index = index < 0 ? param.length() : index; String name = param.substring(0, index); if (this.isConfidentialParam(name, method)) { // Parameter is not a start parameter Integer count = pCount.get(name); int num = (count == null) ? 0 : count + 1; pCount.put(name, num); // Replace parameter with confidential values newParameters = newParameters.replace(param, name + "=" + num); } // Update indexes beginIndex = endIndex + 1; endIndex = parameters.indexOf("&", endIndex + 1); if (endIndex < 0) { endIndex = parameters.length(); } } while (endIndex > beginIndex); return newParameters; } /** * Adds a new IParameter object, generated from the values passed as parameters, to the current state * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the * server for the parameter <code>parameter</code> in the cipher and memory strategies. * <p> * Custom method for form field. * * @param parameter * HTTP parameter * @param value * value generated by server * @param editable * Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param editableName * editable name (text or textarea) * @return Codified value to send to the client * @since HDIV 2.1.5 */ public String composeFormField(String parameter, String value, boolean editable, String editableName) { return this.compose(parameter, value, editable, editableName, false, "POST", Constants.ENCODING_UTF_8); } /** * Adds a new IParameter object, generated from the values passed as parameters, to the current state * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the * server for the parameter <code>parameter</code> in the cipher and memory strategies. * * @param parameterName * HTTP parameter * @param value * value generated by server * @param editable * Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param editableName * editable name (text or textarea) * @param isActionParam * parameter added in action attribute * @param method * http method, GET or POST * @param charEncoding * character encoding * @return Codified value to send to the client * @since HDIV 2.1.5 */ public String compose(String parameterName, String value, boolean editable, String editableName, boolean isActionParam, String method, String charEncoding) { if (!this.isRequestStarted()) { // If request not started, do nothing return value; } if (method == null || method.length() == 0) { // Default method is GET method = "GET"; } IParameter parameter = this.composeParameter(parameterName, value, editable, editableName, isActionParam, charEncoding); if (this.isConfidentialParam(parameterName, method)) { return parameter.getConfidentialValue(); } else { return value; } } /** * Returns true if the parameter requires confidentiality. False otherwise. * * @param parameterName * the name of the parameter * @param method * request HTTP method * @return boolean result * @since HDIV 2.1.6 */ protected boolean isConfidentialParam(String parameterName, String method) { if (!this.hdivConfig.getConfidentiality()) { return false; } if (this.hdivConfig.isStartParameter(parameterName)) { return false; } if (this.isUserDefinedNonValidationParameter(parameterName)) { return false; } if (this.hdivConfig.isParameterWithoutConfidentiality(parameterName)) { return false; } return true; } /** * Checks if the parameter <code>parameter</code> is defined by the user as a no required validation parameter for * the action <code>this.target</code>. * * @param parameter * parameter name * @return True If it is parameter that needs no validation. False otherwise. * @since HDIV 2.0.6 */ protected boolean isUserDefinedNonValidationParameter(String parameter) { // Get current IState IState state = this.getStatesStack().peek(); String action = state.getAction(); if (this.hdivConfig.isParameterWithoutValidation(action, parameter)) { if (log.isDebugEnabled()) { log.debug("parameter " + parameter + " doesn't need validation. It is user defined parameter."); } return true; } return false; } /** * Adds a new IParameter object, generated from the values passed as parameters, to the current state * <code>state</code>. * * @param parameterName * HTTP parameter * @param value * value generated by server * @param editable * Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param editableDataType * editable parameter name (text or textarea) * @param isActionParam * parameter added in action attribute * @param charEncoding * character encoding * @return Codified value to send to the client * @since HDIV 1.1 */ protected IParameter composeParameter(String parameterName, String value, boolean editable, String editableDataType, boolean isActionParam, String charEncoding) { // we decoded value before store it in state. String decodedValue = null; if (!editable) { decodedValue = this.getDecodedValue(value, charEncoding); } // Get current IState IState state = this.getStatesStack().peek(); IParameter parameter = state.getParameter(parameterName); if (parameter != null) { if (parameter.isEditable() != editable) { // A parameter can be created as editable but if a new non editable value is added, the parameter is // changed to non editable. This is required in some frameworks like Struts 2. parameter.setEditable(editable); } parameter.addValue(decodedValue); } else { // create a new parameter and add to the request parameter = createParameter(parameterName, decodedValue, editable, editableDataType, isActionParam, charEncoding); state.addParameter(parameter); } return parameter; } /** * Instantiates the parameter * * @param parameterName * name of the parameter * @param decodedValue * the decoded value of the parameter * @param editable * Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...) * @param editableDataType * editable parameter name (text or textarea) * @param isActionParam * parameter added in action attribute * @param charEncoding * character encoding * @return New IParameter object */ protected IParameter createParameter(String parameterName, String decodedValue, boolean editable, String editableDataType, boolean isActionParam, String charEncoding) { return new Parameter(parameterName, decodedValue, editable, editableDataType, isActionParam); } /** * Creates a new parameter called <code>newParameter</code> and adds all the values of <code>oldParameter</code> * stored in the state to it. * * @param oldParameter * name of the parameter stored in the state * @param newParameter * name of the new parameter */ public void mergeParameters(String oldParameter, String newParameter) { // Get current IState IState state = this.getStatesStack().peek(); IParameter storedParameter = state.getParameter(oldParameter); if (storedParameter.getValues().size() > 0) { IParameter parameter = this.composeParameter(newParameter, storedParameter.getValuePosition(0), false, "", false, Constants.ENCODING_UTF_8); String currentValue = null; // We check the parameters since the second position because the first // value has been used to create the parameter for (int i = 1; i < storedParameter.getValues().size(); i++) { currentValue = storedParameter.getValuePosition(i); parameter.addValue(currentValue); } } } /** * <p> * Decoded <code>value</code> using input <code>charEncoding</code>. * </p> * <p> * Removes Html Entity elements too. Like that: * </p> * <blockquote> &#<i>Entity</i>; - <i>(Example: &amp;) case sensitive</i> &#<i>Decimal</i>; - * <i>(Example: &#68;)</i><br> * &#x<i>Hex</i>; - <i>(Example: &#xE5;) case insensitive</i><br> * </blockquote> * <p> * Based on {@link HtmlUtils.htmlUnescape}. * </p> * * @param value * value to decode * @param charEncoding * character encoding * @return value decoded */ private String getDecodedValue(String value, String charEncoding) { if (value == null || value.length() == 0) { return ""; } String decodedValue = null; try { decodedValue = URLDecoder.decode(value, charEncoding); } catch (UnsupportedEncodingException e) { decodedValue = value; } catch (IllegalArgumentException e) { decodedValue = value; } // Remove escaped Html elements if (decodedValue.contains("&")) { // Can contain escaped characters decodedValue = HtmlUtils.htmlUnescape(decodedValue); } return (decodedValue == null) ? "" : decodedValue; } /** * True if beginRequest has been executed and endRequest not. * * @return boolean */ public boolean isRequestStarted() { return this.statesStack.size() > 0; } /** * Adds the flow identifier to the page of type <code>IPage</code>. * * @since HDIV 2.0.3 */ public void addFlowId(String id) { this.page.setFlowId(id); } /** * Obtains the suffix to add to the _HDIV_STATE_ parameter in the memory and hash strategy. * * @param method * HTTP method * * @return Returns suffix added to the _HDIV_STATE_ parameter. * @since 2.1.7 */ protected String getStateSuffix(String method) { String randomToken = this.page.getRandomToken(method); if (randomToken == null) { randomToken = this.uidGenerator.generateUid().toString(); this.page.setRandomToken(randomToken, method); } return randomToken; } /** * @param session * the session to set */ public void setSession(ISession session) { this.session = session; } /** * @return the page */ public IPage getPage() { return page; } /** * @param page * the page to set */ public void setPage(IPage page) { this.page = page; } /** * @param uidGenerator * the uidGenerator to set */ public void setUidGenerator(UidGenerator uidGenerator) { this.uidGenerator = uidGenerator; } /** * @return the statesStack */ public Stack<IState> getStatesStack() { return statesStack; } /** * @param hdivConfig * The HDIV configuration object to set. */ public void setHdivConfig(HDIVConfig hdivConfig) { this.hdivConfig = hdivConfig; } }