001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
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: package org.apache.catalina.ssi;
018:
019: import java.io.IOException;
020: import java.util.Collection;
021: import java.util.Date;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.Set;
025: import java.util.TimeZone;
026: import org.apache.catalina.util.DateTool;
027: import org.apache.catalina.util.Strftime;
028: import org.apache.catalina.util.URLEncoder;
029:
030: /**
031: * Allows the different SSICommand implementations to share data/talk to each
032: * other
033: *
034: * @author Bip Thelin
035: * @author Amy Roh
036: * @author Paul Speed
037: * @author Dan Sandberg
038: * @author David Becker
039: * @version $Revision: 531303 $, $Date: 2007-04-23 02:24:01 +0200 (lun., 23 avr. 2007) $
040: */
041: public class SSIMediator {
042: protected final static String DEFAULT_CONFIG_ERR_MSG = "[an error occurred while processing this directive]";
043: protected final static String DEFAULT_CONFIG_TIME_FMT = "%A, %d-%b-%Y %T %Z";
044: protected final static String DEFAULT_CONFIG_SIZE_FMT = "abbrev";
045: protected static URLEncoder urlEncoder;
046: protected String configErrMsg = DEFAULT_CONFIG_ERR_MSG;
047: protected String configTimeFmt = DEFAULT_CONFIG_TIME_FMT;
048: protected String configSizeFmt = DEFAULT_CONFIG_SIZE_FMT;
049: protected String className = getClass().getName();
050: protected SSIExternalResolver ssiExternalResolver;
051: protected long lastModifiedDate;
052: protected int debug;
053: protected Strftime strftime;
054: protected SSIConditionalState conditionalState = new SSIConditionalState();
055: static {
056: //We try to encode only the same characters that apache does
057: urlEncoder = new URLEncoder();
058: urlEncoder.addSafeCharacter(',');
059: urlEncoder.addSafeCharacter(':');
060: urlEncoder.addSafeCharacter('-');
061: urlEncoder.addSafeCharacter('_');
062: urlEncoder.addSafeCharacter('.');
063: urlEncoder.addSafeCharacter('*');
064: urlEncoder.addSafeCharacter('/');
065: urlEncoder.addSafeCharacter('!');
066: urlEncoder.addSafeCharacter('~');
067: urlEncoder.addSafeCharacter('\'');
068: urlEncoder.addSafeCharacter('(');
069: urlEncoder.addSafeCharacter(')');
070: }
071:
072: public SSIMediator(SSIExternalResolver ssiExternalResolver,
073: long lastModifiedDate, int debug) {
074: this .ssiExternalResolver = ssiExternalResolver;
075: this .lastModifiedDate = lastModifiedDate;
076: this .debug = debug;
077: setConfigTimeFmt(DEFAULT_CONFIG_TIME_FMT, true);
078: }
079:
080: public void setConfigErrMsg(String configErrMsg) {
081: this .configErrMsg = configErrMsg;
082: }
083:
084: public void setConfigTimeFmt(String configTimeFmt) {
085: setConfigTimeFmt(configTimeFmt, false);
086: }
087:
088: public void setConfigTimeFmt(String configTimeFmt,
089: boolean fromConstructor) {
090: this .configTimeFmt = configTimeFmt;
091: //What's the story here with DateTool.LOCALE_US?? Why??
092: this .strftime = new Strftime(configTimeFmt, DateTool.LOCALE_US);
093: //Variables like DATE_LOCAL, DATE_GMT, and LAST_MODIFIED need to be
094: // updated when
095: //the timefmt changes. This is what Apache SSI does.
096: setDateVariables(fromConstructor);
097: }
098:
099: public void setConfigSizeFmt(String configSizeFmt) {
100: this .configSizeFmt = configSizeFmt;
101: }
102:
103: public String getConfigErrMsg() {
104: return configErrMsg;
105: }
106:
107: public String getConfigTimeFmt() {
108: return configTimeFmt;
109: }
110:
111: public String getConfigSizeFmt() {
112: return configSizeFmt;
113: }
114:
115: public SSIConditionalState getConditionalState() {
116: return conditionalState;
117: }
118:
119: public Collection getVariableNames() {
120: Set variableNames = new HashSet();
121: //These built-in variables are supplied by the mediator ( if not
122: // over-written by
123: // the user ) and always exist
124: variableNames.add("DATE_GMT");
125: variableNames.add("DATE_LOCAL");
126: variableNames.add("LAST_MODIFIED");
127: ssiExternalResolver.addVariableNames(variableNames);
128: //Remove any variables that are reserved by this class
129: Iterator iter = variableNames.iterator();
130: while (iter.hasNext()) {
131: String name = (String) iter.next();
132: if (isNameReserved(name)) {
133: iter.remove();
134: }
135: }
136: return variableNames;
137: }
138:
139: public long getFileSize(String path, boolean virtual)
140: throws IOException {
141: return ssiExternalResolver.getFileSize(path, virtual);
142: }
143:
144: public long getFileLastModified(String path, boolean virtual)
145: throws IOException {
146: return ssiExternalResolver.getFileLastModified(path, virtual);
147: }
148:
149: public String getFileText(String path, boolean virtual)
150: throws IOException {
151: return ssiExternalResolver.getFileText(path, virtual);
152: }
153:
154: protected boolean isNameReserved(String name) {
155: return name.startsWith(className + ".");
156: }
157:
158: public String getVariableValue(String variableName) {
159: return getVariableValue(variableName, "none");
160: }
161:
162: public void setVariableValue(String variableName,
163: String variableValue) {
164: if (!isNameReserved(variableName)) {
165: ssiExternalResolver.setVariableValue(variableName,
166: variableValue);
167: }
168: }
169:
170: public String getVariableValue(String variableName, String encoding) {
171: String lowerCaseVariableName = variableName.toLowerCase();
172: String variableValue = null;
173: if (!isNameReserved(lowerCaseVariableName)) {
174: //Try getting it externally first, if it fails, try getting the
175: // 'built-in'
176: // value
177: variableValue = ssiExternalResolver
178: .getVariableValue(variableName);
179: if (variableValue == null) {
180: variableName = variableName.toUpperCase();
181: variableValue = (String) ssiExternalResolver
182: .getVariableValue(className + "."
183: + variableName);
184: }
185: if (variableValue != null) {
186: variableValue = encode(variableValue, encoding);
187: }
188: }
189: return variableValue;
190: }
191:
192: /**
193: * Applies variable substitution to the specified String and returns the
194: * new resolved string.
195: */
196: public String substituteVariables(String val) {
197: // If it has no variable references then no work
198: // need to be done
199: if (val.indexOf('$') < 0)
200: return val;
201: StringBuffer sb = new StringBuffer(val);
202: for (int i = 0; i < sb.length();) {
203: // Find the next $
204: for (; i < sb.length(); i++) {
205: if (sb.charAt(i) == '$') {
206: i++;
207: break;
208: }
209: }
210: if (i == sb.length())
211: break;
212: // Check to see if the $ is escaped
213: if (i > 1 && sb.charAt(i - 2) == '\\') {
214: sb.deleteCharAt(i - 2);
215: i--;
216: continue;
217: }
218: int nameStart = i;
219: int start = i - 1;
220: int end = -1;
221: int nameEnd = -1;
222: char endChar = ' ';
223: // Check for {} wrapped var
224: if (sb.charAt(i) == '{') {
225: nameStart++;
226: endChar = '}';
227: }
228: // Find the end of the var reference
229: for (; i < sb.length(); i++) {
230: if (sb.charAt(i) == endChar)
231: break;
232: }
233: end = i;
234: nameEnd = end;
235: if (endChar == '}')
236: end++;
237: // We should now have enough to extract the var name
238: String varName = sb.substring(nameStart, nameEnd);
239: String value = getVariableValue(varName);
240: if (value == null)
241: value = "";
242: // Replace the var name with its value
243: sb.replace(start, end, value);
244: // Start searching for the next $ after the value
245: // that was just substituted.
246: i = start + value.length();
247: }
248: return sb.toString();
249: }
250:
251: protected String formatDate(Date date, TimeZone timeZone) {
252: String retVal;
253: if (timeZone != null) {
254: //we temporarily change strftime. Since SSIMediator is inherently
255: // single-threaded, this
256: //isn't a problem
257: TimeZone oldTimeZone = strftime.getTimeZone();
258: strftime.setTimeZone(timeZone);
259: retVal = strftime.format(date);
260: strftime.setTimeZone(oldTimeZone);
261: } else {
262: retVal = strftime.format(date);
263: }
264: return retVal;
265: }
266:
267: protected String encode(String value, String encoding) {
268: String retVal = null;
269: if (encoding.equalsIgnoreCase("url")) {
270: retVal = urlEncoder.encode(value);
271: } else if (encoding.equalsIgnoreCase("none")) {
272: retVal = value;
273: } else if (encoding.equalsIgnoreCase("entity")) {
274: //Not sure how this is really different than none
275: retVal = value;
276: } else {
277: //This shouldn't be possible
278: throw new IllegalArgumentException("Unknown encoding: "
279: + encoding);
280: }
281: return retVal;
282: }
283:
284: public void log(String message) {
285: ssiExternalResolver.log(message, null);
286: }
287:
288: public void log(String message, Throwable throwable) {
289: ssiExternalResolver.log(message, throwable);
290: }
291:
292: protected void setDateVariables(boolean fromConstructor) {
293: boolean alreadySet = ssiExternalResolver
294: .getVariableValue(className + ".alreadyset") != null;
295: //skip this if we are being called from the constructor, and this has
296: // already
297: // been set
298: if (!(fromConstructor && alreadySet)) {
299: ssiExternalResolver.setVariableValue(className
300: + ".alreadyset", "true");
301: Date date = new Date();
302: TimeZone timeZone = TimeZone.getTimeZone("GMT");
303: String retVal = formatDate(date, timeZone);
304: //If we are setting on of the date variables, we want to remove
305: // them from the
306: // user
307: //defined list of variables, because this is what Apache does
308: setVariableValue("DATE_GMT", null);
309: ssiExternalResolver.setVariableValue(className
310: + ".DATE_GMT", retVal);
311: retVal = formatDate(date, null);
312: setVariableValue("DATE_LOCAL", null);
313: ssiExternalResolver.setVariableValue(className
314: + ".DATE_LOCAL", retVal);
315: retVal = formatDate(new Date(lastModifiedDate), null);
316: setVariableValue("LAST_MODIFIED", null);
317: ssiExternalResolver.setVariableValue(className
318: + ".LAST_MODIFIED", retVal);
319: }
320: }
321: }
|