001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: // Created on May 8, 2006
018: package edu.iu.uis.eden.test.web.framework;
019:
020: import java.lang.reflect.InvocationTargetException;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.regex.Pattern;
027:
028: import org.apache.commons.beanutils.PropertyUtils;
029: import org.apache.log4j.Logger;
030:
031: import edu.iu.uis.eden.test.web.ChainingFilter;
032:
033: /**
034: * Encapsulate script state, in a Map of variables which can be accessed using
035: * Jakarta Commons BeanUtils property syntax.
036: * There are a few "special" variables:
037: * <dl>
038: * <dt>USER</dt>
039: * <dd>The user as whom to submit requests</dd>
040: * <dt>BACKDOORID</dt>
041: * <dd>The backdoor id</dd>
042: * <dt>RESOURCE_PREFIX</dt>
043: * <dd>If set, this is prefixed to any unqualifed resource lookups</dd>
044: * <dt>REQUEST</dt>
045: * <dd>A Map of request parameters</dd>
046: * <dt>RESPONSE</dt>
047: * <dd>A Map of response variables</dd>
048: * <dt>CONTEXT</dt>
049: * <dd>The caller-supplied context of the script (if any)</dd>
050: * <dt>FILTERS</dt>
051: * <dd>Named filters (if any, by default: DUPLICATE_SPACES, LEADING_SPACES, TRAILING_SPACES, DUPLICATE_NEWLINES, TRANSIENT_OUTPUT, CANONICALIZE)</dd>
052: * </dl>
053: * This class provides access to "properties" which are abstract names that can be resolved in several ways: via
054: * the variable map, as a resource in the classloader, as a url, and as literal text.
055: * @see edu.iu.uis.eden.test.web.framework.actions.SubmitAction for information on RESPONSE Map contents
056: * @author Aaron Hamid (arh14 at cornell dot edu)
057: */
058: public class ScriptState {
059: public static final String USER = "USER";
060: public static final String BACKDOORID = "BACKDOORID";
061: public static final String RESOURCE_PREFIX = "RESOURCE_PREFIX";
062: public static final String REQUEST = "REQUEST";
063: public static final String RESPONSE = "RESPONSE";
064: public static final String CONTEXT = "CONTEXT";
065: public static final String FILTERS = "FILTERS";
066:
067: /* filters */
068:
069: public static final Pattern DUPLICATE_SPACES;
070: public static final Pattern LEADING_SPACES;
071: public static final Pattern TRAILING_SPACES;
072: public static final Pattern DUPLICATE_NEWLINES;
073: public static final Pattern TRANSIENT_OUTPUT;
074: static {
075: DUPLICATE_SPACES = Pattern.compile("[\\s&&[^\n]]+");
076: LEADING_SPACES = Pattern.compile("(?m)^\\s+");
077: TRAILING_SPACES = Pattern.compile("(?m)\\s+$");
078: DUPLICATE_NEWLINES = Pattern.compile("\n{2,}");
079: TRANSIENT_OUTPUT = Pattern
080: .compile("(?s)\\[transient start\\](.*?)\\[transient end\\]");
081: }
082:
083: private static final Logger LOG = Logger
084: .getLogger(ScriptState.class);
085:
086: private static final String DUPLICATE_SPACES_FILTER_NAME = "DUPLICATE_SPACES";
087: private static final String LEADING_SPACES_FILTER_NAME = "LEADING_SPACES";
088: private static final String TRAILING_SPACES_FILTER_NAME = "TRAILING_SPACES";
089: private static final String DUPLICATE_NEWLINES_FILTER_NAME = "DUPLICATE_NEWLINES";
090: private static final String TRANSIENT_OUTPUT_FILTER_NAME = "TRANSIENT_OUTPUT";
091: public static final String CANONICALIZE_FILTER_NAME = "CANONICALIZE";
092:
093: public static final Filter DUPLICATE_SPACES_FILTER;
094: public static final Filter LEADING_SPACES_FILTER;
095: public static final Filter TRAILING_SPACES_FILTER;
096: public static final Filter DUPLICATE_NEWLINES_FILTER;
097: public static final Filter TRANSIENT_OUTPUT_FILTER;
098: public static final Filter CANONICALIZE_FILTER;
099:
100: private static final Map DEFAULT_FILTERS = new HashMap();
101:
102: /* Initialize the default filters */
103: static {
104: DUPLICATE_SPACES_FILTER = new RegexReplacementFilter(
105: DUPLICATE_SPACES, " ");
106: LEADING_SPACES_FILTER = new RegexReplacementFilter(
107: LEADING_SPACES, "");
108: TRAILING_SPACES_FILTER = new RegexReplacementFilter(
109: TRAILING_SPACES, "");
110: DUPLICATE_NEWLINES_FILTER = new RegexReplacementFilter(
111: DUPLICATE_NEWLINES, "\n");
112: TRANSIENT_OUTPUT_FILTER = new RegexReplacementFilter(
113: TRANSIENT_OUTPUT, "REDACTED TRANSIENT DATA");
114:
115: List list = new ArrayList();
116: list.add(DUPLICATE_SPACES_FILTER);
117: list.add(LEADING_SPACES_FILTER);
118: list.add(TRAILING_SPACES_FILTER);
119: list.add(DUPLICATE_NEWLINES_FILTER);
120: list.add(TRANSIENT_OUTPUT_FILTER);
121:
122: CANONICALIZE_FILTER = new ChainingFilter(list);
123:
124: DEFAULT_FILTERS.put(DUPLICATE_SPACES_FILTER_NAME,
125: DUPLICATE_SPACES_FILTER);
126: DEFAULT_FILTERS.put(LEADING_SPACES_FILTER_NAME,
127: LEADING_SPACES_FILTER);
128: DEFAULT_FILTERS.put(TRAILING_SPACES_FILTER_NAME,
129: TRAILING_SPACES_FILTER);
130: DEFAULT_FILTERS.put(DUPLICATE_NEWLINES_FILTER_NAME,
131: DUPLICATE_NEWLINES_FILTER);
132: DEFAULT_FILTERS.put(TRANSIENT_OUTPUT_FILTER_NAME,
133: TRANSIENT_OUTPUT_FILTER);
134: DEFAULT_FILTERS.put(CANONICALIZE_FILTER_NAME,
135: CANONICALIZE_FILTER);
136: }
137:
138: private Map state = new HashMap();
139:
140: public ScriptState() {
141: reset();
142: }
143:
144: public void reset() {
145: state.clear();
146: state.put(USER, null); // the end user
147: state.put(BACKDOORID, null); // the backdoorid
148: state.put(REQUEST, new HashMap()); // the parameter map
149: state.put(RESPONSE, new HashMap()); // the response map
150: state.put(CONTEXT, null); // the context map
151: state.put(FILTERS, new HashMap(DEFAULT_FILTERS)); // the filters map
152: }
153:
154: public String getUser() {
155: return (String) state.get(USER);
156: }
157:
158: public void setUser(String user) {
159: state.put(USER, user);
160: }
161:
162: public String getBackdoorId() {
163: return (String) state.get(BACKDOORID);
164: }
165:
166: public void setBackdoorId(String backdoorId) {
167: state.put(BACKDOORID, backdoorId);
168: }
169:
170: public String getResourcePrefix() {
171: return (String) state.get(RESOURCE_PREFIX);
172: }
173:
174: public void setResourcePrefix(String prefix) {
175: state.put(RESOURCE_PREFIX, prefix);
176: }
177:
178: public Map getRequest() {
179: return (Map) state.get(REQUEST);
180: }
181:
182: public Map getResponse() {
183: return (Map) state.get(RESPONSE);
184: }
185:
186: public Map getContext() {
187: return (Map) state.get(CONTEXT);
188: }
189:
190: public void setContext(Map context) {
191: state.put(CONTEXT, context);
192: }
193:
194: public Map getFilters() {
195: return (Map) state.get(FILTERS);
196: }
197:
198: public Map getVariables() {
199: return state;
200: }
201:
202: /**
203: * Sets a key/value pair in the state map root
204: * @param name name of the variable
205: * @param value value of the variable
206: */
207: public void setVariable(String name, Object value) {
208: try {
209: PropertyUtils.setProperty(state, name, value);
210: } catch (InvocationTargetException ite) {
211: throw new RuntimeException("Error setting property: '"
212: + name + "'", ite);
213: } catch (IllegalAccessException iae) {
214: throw new RuntimeException("Error setting property: '"
215: + name + "'", iae);
216: } catch (NoSuchMethodException nsme) {
217: throw new RuntimeException("Error setting property: '"
218: + name + "'", nsme);
219: }
220: }
221:
222: /**
223: * Resolves the specified name as a variable
224: * @param name the variable name
225: * @return variable value if found, null otherwise
226: */
227: public Object getVariable(String name) {
228: return retrieveProperty(new Property(
229: PropertyScheme.VARIABLE_SCHEME.getName(), name));
230: }
231:
232: /**
233: * Resolves the specified name as a literal (just returns it)
234: * @param name the literal name
235: * @return the literal name
236: */
237: public Object getLiteral(String name) {
238: return retrieveProperty(new Property(
239: PropertyScheme.LITERAL_SCHEME.getName(), name));
240: }
241:
242: /**
243: * Resolves the specified name as a resource
244: * @param name the resource name
245: * @return resource contents if found, null otherwise
246: */
247: public Object getResource(String name) {
248: return retrieveProperty(new Property(
249: PropertyScheme.RESOURCE_SCHEME.getName(), name));
250: }
251:
252: /**
253: * Resolves the specified name as a url
254: * @param name the url
255: * @return url contents if found, null otherwise
256: */
257: public Object getURL(String name) {
258: return retrieveProperty(new Property(PropertyScheme.URL_SCHEME
259: .getName(), name));
260: }
261:
262: /**
263: * Resolves the specified name as a qualified property
264: * @param name the qualified property name
265: * @return value if found, null otherwise
266: */
267: public Object retrieveProperty(String name) {
268: return retrieveProperty(new Property(name));
269: }
270:
271: /**
272: * Resolves the specified name as an unqualified property
273: * @param name the potentially unqualified property name
274: * @param defaultScheme the default scheme to use if the property is unqualified
275: * @return value if found, null otherwise
276: */
277: public Object retrieveProperty(String name,
278: PropertyScheme defaultScheme) {
279: return retrieveProperty(new Property(name), defaultScheme);
280: }
281:
282: /**
283: * Resolves the specified name as an unqualified property
284: * @param prop the potentially unqualified property
285: * @param defaultScheme the default scheme to use if the property is unqualified
286: * @return value if found, null otherwise
287: */
288: public Object retrieveProperty(Property prop,
289: PropertyScheme defaultScheme) {
290: if (prop.scheme == null && defaultScheme != null) {
291: prop.scheme = defaultScheme.getName();
292: }
293: return retrieveProperty(prop);
294: }
295:
296: /**
297: * Resolves the specified name as a qualified property
298: * @param prop the qualified property
299: * @return value if found, null otherwise
300: */
301: public Object retrieveProperty(Property prop) {
302: Iterator schemes = PropertyScheme.SCHEMES.iterator();
303: while (schemes.hasNext()) {
304: PropertyScheme scheme = (PropertyScheme) schemes.next();
305: if (scheme.getName().equals(prop.scheme)
306: || scheme.getShortName().equals(prop.scheme)) {
307: return scheme.load(prop, this );
308: }
309: }
310: String message = "Invalid property scheme: '" + prop.scheme
311: + "'";
312: LOG.error(message);
313: throw new RuntimeException(message);
314: }
315: }
|