0001: /* Copyright 2002, 2005 The JA-SIG Collaborative. All rights reserved.
0002: * See license distributed with this file and
0003: * available online at http://www.uportal.org/license.html
0004: */
0005:
0006: package org.jasig.portal.channels.webproxy;
0007:
0008: import java.io.BufferedOutputStream;
0009: import java.io.ByteArrayOutputStream;
0010: import java.io.FileOutputStream;
0011: import java.io.FileNotFoundException;
0012: import java.io.IOException;
0013: import java.io.InputStream;
0014: import java.io.OutputStream;
0015: import java.io.PrintWriter;
0016: import java.io.UnsupportedEncodingException;
0017: import java.net.HttpURLConnection;
0018: import java.net.URI;
0019: import java.net.URISyntaxException;
0020: import java.net.URL;
0021: import java.net.URLConnection;
0022: import java.net.URLEncoder;
0023: import java.util.Date;
0024: import java.util.Enumeration;
0025: import java.util.HashMap;
0026: import java.util.HashSet;
0027: import java.util.Map;
0028: import java.util.StringTokenizer;
0029:
0030: import javax.xml.parsers.DocumentBuilder;
0031: import javax.xml.parsers.DocumentBuilderFactory;
0032:
0033: import org.jasig.portal.ChannelCacheKey;
0034: import org.jasig.portal.ChannelRuntimeData;
0035: import org.jasig.portal.ChannelRuntimeProperties;
0036: import org.jasig.portal.ChannelStaticData;
0037: import org.jasig.portal.GeneralRenderingException;
0038: import org.jasig.portal.IChannel;
0039: import org.jasig.portal.IMimeResponse;
0040: import org.jasig.portal.ICacheable;
0041: import org.jasig.portal.MediaManager;
0042: import org.jasig.portal.PortalEvent;
0043: import org.jasig.portal.PortalException;
0044: import org.jasig.portal.ResourceMissingException;
0045: import org.jasig.portal.properties.PropertiesManager;
0046: import org.jasig.portal.security.IPerson;
0047: import org.jasig.portal.security.LocalConnectionContext;
0048: import org.apache.commons.logging.Log;
0049: import org.apache.commons.logging.LogFactory;
0050: import org.jasig.portal.utils.AbsoluteURLFilter;
0051: import org.jasig.portal.utils.CookieCutter;
0052: import org.jasig.portal.utils.DTDResolver;
0053: import org.jasig.portal.utils.ResourceLoader;
0054: import org.jasig.portal.utils.XSLT;
0055: import org.jasig.portal.utils.uri.BlockedUriException;
0056: import org.jasig.portal.utils.uri.IUriScrutinizer;
0057: import org.jasig.portal.utils.uri.PrefixUriScrutinizer;
0058: import org.w3c.dom.Document;
0059: import org.w3c.tidy.Tidy;
0060: import org.xml.sax.ContentHandler;
0061:
0062: /**
0063: * <p>A channel which transforms and interacts with dynamic XML or HTML.
0064: * See docs/website/developers/channel_docs/reference/CwebProxy.html
0065: * for full documentation.
0066: * </p>
0067: *
0068: *<p>Static and Runtime Channel Parameters:
0069: * These parameters can be configured both as ChannelStaticData parameters and as
0070: * ChannelRuntimeData parameters.
0071: * These static parameters can be updated by equivalent
0072: * Runtime parameters. Caching parameters can also be changed temporarily.
0073: * Cache defaults and IPerson restrictions are loaded first from properties,
0074: * and overridden by static data if there.
0075: *</p>
0076: *<ol>
0077: * <li>"cw_xml" - a URI for the source XML document. By default, must be an
0078: * http:// or https:// URI, though this requirement is configurable via
0079: * ChannelStaticData parameters.
0080: * <li>"cw_xslTitle" - a title representing the stylesheet (optional)
0081: * <i>If no title parameter is specified, a default
0082: * stylesheet will be chosen according to the media</i>
0083: * <li>"cw_cacheDefaultMode" - Default caching mode.
0084: * <i>May be <code>none</code> (normally don't cache), or
0085: * <code>all</code> (cache everything).</i>
0086: * <li>"cw_cacheDefaultTimeout" - Default timeout in seconds.
0087: * <li>"cw_cacheMode" - override default for this request only.
0088: * <i>Primarily intended as a runtime parameter, but can
0089: * used statically to override the first instance.</i>
0090: * <li>"cw_cacheTimeout" - override default for this request only.
0091: * <i>Primarily intended as a runtime parameter, but can
0092: * be used statically to override the first instance.</i>
0093: *</ol>
0094: *
0095: * <p>Static Channel Parameters:
0096: These parameters can be configured only as ChannelStaticData parameters.
0097: They can no longer (as of uPortal 2.5.1) be changed at runtime. This closes
0098: some serious security vulnerabilities wherein the Adversary would manipulate
0099: these parameters at runtime to access resources on the local filesystem.
0100: * </p>
0101: * <ol>
0102: * <li>"cw_ssl" - a URI specifying the corresponding .ssl (stylesheet list) file
0103: * <li>"cw_xsl" - a URI specifying the stylesheet to use
0104: * <i>If <code>cw_xsl</code> is supplied, <code>cw_ssl</code>
0105: * and <code>cw_xslTitle</code> will be ignored.</i>
0106: * <li>"cw_passThrough" - indicates how RunTimeData is to be passed through.
0107: * <i>If <code>cw_passThrough</code> is supplied, and not set
0108: * to "all" or "application", additional RunTimeData
0109: * parameters not starting with "cw_" or "upc_" will be
0110: * passed as request parameters to the XML URI. If
0111: * <code>cw_passThrough</code> is set to "marked", this will
0112: * happen only if there is also a RunTimeData parameter of
0113: * <code>cw_inChannelLink</code>. "application" is intended
0114: * to keep application-specific links in the channel, while
0115: * "all" should keep all links in the channel. This
0116: * distinction is handled entirely in the URL Filters.</i>
0117: * <li>"cw_tidy" - output from <code>xmlUri</code> will be passed though Jtidy
0118: * <li>"cw_info" - a URI to be called for the <code>info</code> event.
0119: * <li>"cw_help" - a URI to be called for the <code>help</code> event.
0120: * <li>"cw_edit" - a URI to be called for the <code>edit</code> event.
0121: * <li>"cw_person" - IPerson attributes to pass.
0122: * <i>A comma-separated list of IPerson attributes to
0123: * pass to the back end application. The static data
0124: * value will be passed on </i>all<i> requests except some
0125: * refresh requests.</i>
0126: * <li>"cw_personAllow" - Restrict IPerson attribute passing to this list.
0127: * <i>A comma-separated list of IPerson attributes that
0128: * may be passed via cw_person. An empty or non-existent
0129: * value means use the default value from the corresponding
0130: * property. The special value "*" means all attributes
0131: * are allowed. The value "!*" means none are allowed.
0132: * Static data only.</i>
0133: * <li>"upc_localConnContext" - LocalConnectionContext implementation class.
0134: * <i>The name of a class to use when data sent to the
0135: * backend application needs to be modified or added
0136: * to suit local needs. Static data only.</i>
0137: * <li>"cw_allow_uri_prefixes" - permitted URI prefixes.
0138: * <i>Optional static data only parameter specifying allowable prefixes
0139: * for URIs accessed by this channel. This channel will only use
0140: * URIs with these prefixes for obtaining XML and XSLT for use in
0141: * rendering. Whitespace delimit allowed prefixes. Effectively
0142: * defaults to "http:// https://"; do not allow "file:/" lightly.</i>
0143: * </li>
0144: * <li>"cw_block_uri_prefixes" - blocked URI prefixes.
0145: * <i>Optional static data only parameter further restricting which
0146: * URIs this channel will use to obtain XML and XSLT. This channel
0147: * will not use URIs matching prefixes specified in this whitespace-
0148: * delimited parameter. Effectively defaults to "".</i>
0149: * </li>
0150: * <li>"cw_restrict_xmlUri_inStaticData" - Optional parameter specifying whether
0151: * the xmlUri should be restricted according to the allow and
0152: * deny prefix rules above as presented in ChannelStaticData
0153: * or just as presented in ChannelRuntimeData. "true" means
0154: * both ChannelStaticData and ChannelRuntimeData will be restricted.
0155: * Any other value or the parameter not being present means
0156: * only ChannelRuntimeData will be restricted. It is important
0157: * to set this value to true when using subscribe-time
0158: * channel parameter configuration of the xmlUri.
0159: * </li>
0160: * </ol>
0161: * <p>Runtime Channel Parameters:</p>
0162: * The following parameters are runtime-only.
0163: * </p>
0164: * <ol>
0165: * <li>"cw_reset" - an instruction to return to reset internal variables.
0166: * <i>The value <code>return</code> resets <code>cw_xml</code>
0167: * to its last value before changed by button events. The
0168: * value "reset" returns all variables to the static data
0169: * values.</i>
0170: * <li>"cw_download" - use download worker for this link or form
0171: * <i>any link or form that contains this parameter will be
0172: * handled by the download worker, if the pass-through mode
0173: * is set to rewrite the link or form. This allows downloads
0174: * from the proxied site to be delivered via the portal,
0175: * primarily useful if the download requires verification
0176: * of a session referenced by a proxied cookie</i>
0177: *
0178: * </ol>
0179: * <p>This channel can be used for all XML formats with appropriate stylesheets.
0180: * All static data parameters as well as additional runtime data parameters
0181: * passed to this channel via HttpRequest will in turn be passed on to the
0182: * XSLT stylesheet as stylesheet parameters. They can be read in the
0183: * stylesheet as follows:
0184: * <code><xsl:param
0185: * name="yourParamName">aDefaultValue</xsl:param></code>
0186: * </p>
0187: * @author Andrew Draskoy, andrew@mun.ca
0188: * @author Sarah Arnott, sarnott@mun.ca
0189: * @version $Revision: 42283 $
0190: */
0191: public class CWebProxy implements IChannel, ICacheable, IMimeResponse
0192:
0193: {
0194: private static final Log log = LogFactory.getLog(CWebProxy.class);
0195: private static final Log accessLog = LogFactory
0196: .getLog(CWebProxy.class + ".access");
0197:
0198: /**
0199: * The name of the optional ChannelStaticData parameter the value of
0200: * which will be a String containing a whitespace-delimited list of allowable
0201: * URI prefixes for URIs from which the configured CWebProxy instance will
0202: * obtain XML and XSLT. Defaults to allowing all URIs of the http://
0203: * and https:// methods.
0204: */
0205: public static final String ALLOW_URI_PREFIXES_PARAM = "cw_allow_uri_prefixes";
0206:
0207: /**
0208: * The name of the optional ChannelStaticData parameter the value of which
0209: * will be a String containing a whitespace-delimited list of explicitly
0210: * blocked URI prefixes for URIs from which the configured CWebProxy
0211: * instance will not obtain XML and XSLT. URIs matching these prefixes
0212: * will not be used even if they also match an allow prefix specified in
0213: * ALLOW_URI_PREFIXES_PARAM.
0214: */
0215: public static final String BLOCK_URI_PREFIXES_PARAM = "cw_block_uri_prefixes";
0216:
0217: /**
0218: * The name of the optional ChannelStaticData parameter the value of which
0219: * will be the String "true" to convey that the xmlUri should be restricted
0220: * as received from both ChannelStaticData and CHannelRuntimeData and
0221: * any other value to convey that it should be restricted only as received
0222: * from ChannelRuntimeData.
0223: *
0224: * CWebProxy should be configured to restrict xmlUri from ChannelStaticData
0225: * in the case where the xmlUri is a subscribe-time configurable parameter.
0226: */
0227: public static final String RESTRICT_STATIC_XMLURI_PREFIXES_PARAM = "cw_restrict_xmlUri_inStaticData";
0228:
0229: private static final MediaManager MEDIAMANAGER = MediaManager
0230: .getMediaManager();
0231: // to prepend to the system-wide cache key
0232: static final String systemCacheId = "org.jasig.portal.channels.webproxy.CWebProxy";
0233: static PrintWriter devNull;
0234:
0235: ChannelState chanState;
0236:
0237: static {
0238: try {
0239: devNull = getErrout();
0240: } catch (FileNotFoundException fnfe) {
0241: /* Ignore */
0242: }
0243: }
0244:
0245: /**
0246: * All state variables are stored in this inner class.
0247: * It would probably be an improvement to extract this inner class into its own
0248: * fully fledged class in the cwebproxy package, thereby enforcing that its
0249: * properties are only accessed via getter and setter methods that would
0250: * need to be added.
0251: */
0252: private class ChannelState {
0253: private String publishId;
0254: private IPerson iperson;
0255: private String person;
0256: private String personAllow;
0257: private HashSet personAllow_set;
0258: private String fullxmlUri;
0259:
0260: /**
0261: * URI of the source of XML this CWebProxy instance will render in response
0262: * to a recent button press (channel control button, e.g. "help").
0263: */
0264: private String buttonxmlUri;
0265:
0266: /**
0267: * URI of the source of XML this CWebProxy instance will render.
0268: * Do not set this field directly. Instead, access it via the setter
0269: * method.
0270: */
0271: private String xmlUri;
0272: private String key;
0273: private String passThrough;
0274: private String tidy;
0275:
0276: /**
0277: * URI of the stylesheet selector this channel will use to select its
0278: * XSLT.
0279: */
0280: private String sslUri;
0281: private String xslTitle;
0282:
0283: /**
0284: * URI of the XSLT this channel will use to select its XSLT.
0285: */
0286: private String xslUri;
0287:
0288: /**
0289: * URI of the XML this channel will use to render its info mode.
0290: */
0291: private String infoUri;
0292:
0293: /**
0294: * URI of the XML this channel will use to render its help mode.
0295: */
0296: private String helpUri;
0297:
0298: /**
0299: * URI of the XML this channel will use to render its edit mode.
0300: */
0301: private String editUri;
0302:
0303: private String cacheDefaultMode;
0304: private String cacheMode;
0305: private String reqParameters;
0306: private long cacheDefaultTimeout;
0307: private long cacheTimeout;
0308: private ChannelRuntimeData runtimeData;
0309: private CookieCutter cookieCutter;
0310: private URLConnection connHolder;
0311: private LocalConnectionContext localConnContext;
0312: private int refresh;
0313:
0314: /**
0315: * The default scrutinizer is one that allows only http: and https: URIs.
0316: * Non-null. Constructor enforces initialization to a non-null.
0317: */
0318: private final IUriScrutinizer uriScrutinizer;
0319:
0320: public ChannelState(IUriScrutinizer uriScrutinizerArg) {
0321: if (uriScrutinizerArg == null) {
0322: throw new IllegalArgumentException(
0323: "Cannot instantiate CWebProxy ChannelState with a null URI srutinizer.");
0324: }
0325: this .uriScrutinizer = uriScrutinizerArg;
0326:
0327: fullxmlUri = buttonxmlUri = xmlUri = key = passThrough = sslUri = null;
0328: xslTitle = xslUri = infoUri = helpUri = editUri = tidy = null;
0329: cacheMode = null;
0330: iperson = null;
0331: publishId = null;
0332: refresh = -1;
0333: cacheTimeout = cacheDefaultTimeout = PropertiesManager
0334: .getPropertyAsLong("org.jasig.portal.channels.webproxy.CWebProxy.cache_default_timeout");
0335: cacheMode = cacheDefaultMode = PropertiesManager
0336: .getProperty("org.jasig.portal.channels.webproxy.CWebProxy.cache_default_mode");
0337: personAllow = PropertiesManager
0338: .getProperty("org.jasig.portal.channels.webproxy.CWebProxy.person_allow");
0339: runtimeData = null;
0340: cookieCutter = new CookieCutter();
0341: localConnContext = null;
0342: }
0343:
0344: /**
0345: * Set the xmlUri channel state property, applying URI acceptance logic
0346: * before accepting the parameter.
0347: * @param uriArg URI of XML source, or null
0348: * @throws IllegalArgumentException if the uriArg is not in URI syntax or is
0349: * a non-URI classpath-relative path which doesn't map to an actually existing resource.
0350: * @throws BlockedUriException if the URI is unacceptable for reasons of policy
0351: * @since uPortal 2.5.1
0352: */
0353: public void setXmlUri(String uriArg) {
0354: if (uriArg != null && !"".equals(uriArg)) {
0355: // resolve partial relative path fragments per ResourceLoader
0356: try {
0357: URL resourceUrl = ResourceLoader.getResourceAsURL(
0358: this .getClass(), uriArg);
0359: uriArg = resourceUrl.toExternalForm();
0360: } catch (ResourceMissingException rme) {
0361: IllegalArgumentException iae = new IllegalArgumentException(
0362: "Resource [" + uriArg + "] missing.");
0363: iae.initCause(rme);
0364: throw iae;
0365: }
0366:
0367: try {
0368: this .uriScrutinizer.scrutinize(new URI(uriArg));
0369: this .xmlUri = uriArg;
0370: } catch (URISyntaxException e) {
0371: IllegalArgumentException iae = new IllegalArgumentException(
0372: "Value [" + uriArg + "]had bad URI syntax.");
0373: iae.initCause(e);
0374: throw iae;
0375: }
0376: }
0377: // if uriArg was null do nothing.
0378:
0379: }
0380:
0381: }
0382:
0383: public CWebProxy() {
0384: }
0385:
0386: /**
0387: * Passes ChannelStaticData to the channel.
0388: * This is done during channel instantiation time.
0389: * see org.jasig.portal.ChannelStaticData
0390: * @param sd channel static data
0391: * @see ChannelStaticData
0392: */
0393: public void setStaticData(ChannelStaticData sd)
0394: throws PortalException {
0395:
0396: // detect static data configuration for URI scrutinizer
0397:
0398: String allowUriPrefixesParam = sd
0399: .getParameter(ALLOW_URI_PREFIXES_PARAM);
0400: String denyUriPrefixesParam = sd
0401: .getParameter(BLOCK_URI_PREFIXES_PARAM);
0402:
0403: // store the scrutinizer into channel state
0404: IUriScrutinizer uriScrutinizer = PrefixUriScrutinizer
0405: .instanceFromParameters(allowUriPrefixesParam,
0406: denyUriPrefixesParam);
0407: ChannelState state = new ChannelState(uriScrutinizer);
0408:
0409: // determine whether we should restrict what URIs we accept as the xmlUri from
0410: // ChannelStaticData
0411: String scrutinizeXmlUriAsStaticDataString = sd
0412: .getParameter(RESTRICT_STATIC_XMLURI_PREFIXES_PARAM);
0413: boolean scrutinizeXmlUriAsStaticData = "true"
0414: .equals(scrutinizeXmlUriAsStaticDataString);
0415:
0416: String xmlUriParam = sd.getParameter("cw_xml");
0417: if (scrutinizeXmlUriAsStaticData) {
0418: // apply configured xmlUri restrictions
0419: state.setXmlUri(xmlUriParam);
0420: } else {
0421: // set the field directly to avoid applying xmlUri restrictions
0422: state.xmlUri = xmlUriParam;
0423: }
0424:
0425: state.iperson = sd.getPerson();
0426: state.publishId = sd.getChannelPublishId();
0427: state.person = sd.getParameter("cw_person");
0428: String personAllow = sd.getParameter("cw_personAllow");
0429: if (personAllow != null && (!personAllow.trim().equals("")))
0430: state.personAllow = personAllow;
0431: // state.personAllow could have been set by a property or static data
0432: if (state.personAllow != null
0433: && (!state.personAllow.trim().equals("!*"))) {
0434: state.personAllow_set = new HashSet();
0435: StringTokenizer st = new StringTokenizer(state.personAllow,
0436: ",");
0437: if (st != null) {
0438: while (st.hasMoreElements()) {
0439: String pName = st.nextToken();
0440: if (pName != null) {
0441: pName = pName.trim();
0442: if (!pName.equals(""))
0443: state.personAllow_set.add(pName);
0444: }
0445: }
0446: }
0447: }
0448:
0449: state.sslUri = sd.getParameter("cw_ssl");
0450: state.xslTitle = sd.getParameter("cw_xslTitle");
0451: state.xslUri = sd.getParameter("cw_xsl");
0452: state.fullxmlUri = sd.getParameter("cw_xml");
0453:
0454: state.passThrough = sd.getParameter("cw_passThrough");
0455: state.tidy = sd.getParameter("cw_tidy");
0456:
0457: state.infoUri = sd.getParameter("cw_info");
0458: state.helpUri = sd.getParameter("cw_help");
0459: state.editUri = sd.getParameter("cw_edit");
0460:
0461: state.key = state.xmlUri;
0462:
0463: String cacheMode = sd.getParameter("cw_cacheDefaultMode");
0464: if (cacheMode != null && !cacheMode.trim().equals(""))
0465: state.cacheDefaultMode = cacheMode;
0466: cacheMode = sd.getParameter("cw_cacheMode");
0467: if (cacheMode != null && !cacheMode.trim().equals(""))
0468: state.cacheMode = cacheMode;
0469: else
0470: state.cacheMode = state.cacheDefaultMode;
0471:
0472: String cacheTimeout = sd.getParameter("cw_cacheDefaultTimeout");
0473: if (cacheTimeout != null && !cacheTimeout.trim().equals(""))
0474: state.cacheDefaultTimeout = Long.parseLong(cacheTimeout);
0475: cacheTimeout = sd.getParameter("cw_cacheTimeout");
0476: if (cacheTimeout != null && !cacheTimeout.trim().equals(""))
0477: state.cacheTimeout = Long.parseLong(cacheTimeout);
0478: else
0479: state.cacheTimeout = state.cacheDefaultTimeout;
0480:
0481: String connContext = sd.getParameter("upc_localConnContext");
0482: if (connContext != null && !connContext.trim().equals("")) {
0483: try {
0484: state.localConnContext = (LocalConnectionContext) Class
0485: .forName(connContext).newInstance();
0486: state.localConnContext.init(sd);
0487: } catch (Exception e) {
0488: log
0489: .error(
0490: "CWebProxy: Cannot initialize LocalConnectionContext: ",
0491: e);
0492: }
0493: }
0494:
0495: chanState = state;
0496: }
0497:
0498: /**
0499: * Passes ChannelRuntimeData to the channel.
0500: * This function is called prior to the renderXML() call.
0501: * @param rd channel runtime data
0502: * @see ChannelRuntimeData
0503: */
0504: public void setRuntimeData(ChannelRuntimeData rd) {
0505: ChannelState state = chanState;
0506: if (state == null) {
0507: log.debug("CWebProxy:setRuntimeData() : no entry in state");
0508: } else {
0509: state.runtimeData = rd;
0510: if (rd.isEmpty() && (state.refresh != -1)) {
0511: // A refresh-- State remains the same.
0512: if (state.buttonxmlUri != null) {
0513: state.key = state.buttonxmlUri;
0514: state.fullxmlUri = state.buttonxmlUri;
0515: state.refresh = 0;
0516: } else {
0517: if (state.refresh == 0)
0518: state.key = state.fullxmlUri;
0519: state.fullxmlUri = state.xmlUri;
0520: state.refresh = 1;
0521: }
0522: } else {
0523:
0524: state.refresh = 0;
0525:
0526: String xmlUri = state.runtimeData
0527: .getParameter("cw_xml");
0528: if (xmlUri != null) {
0529: state.setXmlUri(xmlUri);
0530: // don't need an explicit reset if a new URI is provided.
0531: state.buttonxmlUri = null;
0532: }
0533:
0534: // prior to uPortal 2.5.1, CWebProxy allowed several other parameters
0535: // to be set via channel runtime data. These features were removed
0536: // to close security vulnerabilities. Some (very few) uPortal deployments
0537: // will need to add these features back in or otherwise address them.
0538:
0539: String xslTitle = state.runtimeData
0540: .getParameter("cw_xslTitle");
0541: if (xslTitle != null)
0542: state.xslTitle = xslTitle;
0543:
0544: String passThrough = state.runtimeData
0545: .getParameter("cw_passThrough");
0546: if (passThrough != null)
0547: state.passThrough = passThrough;
0548:
0549: String cacheTimeout = state.runtimeData
0550: .getParameter("cw_cacheDefaultTimeout");
0551: if (cacheTimeout != null)
0552: state.cacheDefaultTimeout = Long
0553: .parseLong(cacheTimeout);
0554:
0555: cacheTimeout = state.runtimeData
0556: .getParameter("cw_cacheTimeout");
0557: if (cacheTimeout != null)
0558: state.cacheTimeout = Long.parseLong(cacheTimeout);
0559: else
0560: state.cacheTimeout = state.cacheDefaultTimeout;
0561:
0562: String cacheDefaultMode = state.runtimeData
0563: .getParameter("cw_cacheDefaultMode");
0564: if (cacheDefaultMode != null) {
0565: state.cacheDefaultMode = cacheDefaultMode;
0566: }
0567:
0568: String cacheMode = state.runtimeData
0569: .getParameter("cw_cacheMode");
0570: if (cacheMode != null) {
0571: state.cacheMode = cacheMode;
0572: } else
0573: state.cacheMode = state.cacheDefaultMode;
0574:
0575: // reset is a one-time thing.
0576: String reset = state.runtimeData
0577: .getParameter("cw_reset");
0578: if (reset != null) {
0579: if (reset.equalsIgnoreCase("return")) {
0580: state.buttonxmlUri = null;
0581: }
0582: }
0583:
0584: if (state.buttonxmlUri != null)
0585: state.fullxmlUri = state.buttonxmlUri;
0586: else {
0587: //log.debug("CWebProxy: xmlUri is " + state.xmlUri);
0588:
0589: // pass IPerson atts independent of the value of cw_passThrough
0590: StringBuffer newXML = new StringBuffer();
0591: String appendchar = "";
0592:
0593: // here add in attributes according to cw_person
0594: if (state.person != null
0595: && state.personAllow_set != null) {
0596: StringTokenizer st = new StringTokenizer(
0597: state.person, ",");
0598: if (st != null) {
0599: while (st.hasMoreElements()) {
0600: String pName = st.nextToken();
0601: if ((pName != null)
0602: && (!pName.trim().equals(""))) {
0603: if (state.personAllow.trim()
0604: .equals("*")
0605: || state.personAllow_set
0606: .contains(pName)) {
0607: newXML.append(appendchar);
0608: appendchar = "&";
0609: newXML.append(pName);
0610: newXML.append("=");
0611: // note, this only gets the first one if it's a
0612: // java.util.Vector. Should check
0613: String pVal = (String) state.iperson
0614: .getAttribute(pName);
0615: if (pVal != null)
0616: try {
0617: newXML
0618: .append(URLEncoder
0619: .encode(
0620: pVal,
0621: "UTF-8"));
0622: } catch (UnsupportedEncodingException e) {
0623: throw new RuntimeException(
0624: e);
0625: }
0626: } else {
0627: if (log.isInfoEnabled())
0628: log
0629: .info("CWebProxy: request to pass "
0630: + pName
0631: + " denied.");
0632: }
0633: }
0634: }
0635: }
0636: }
0637: // end cw_person code
0638:
0639: // Is this a case where we need to pass request parameters to the xmlURI?
0640: if (state.passThrough != null
0641: && !state.passThrough
0642: .equalsIgnoreCase("none")
0643: && (state.passThrough
0644: .equalsIgnoreCase("all")
0645: || state.passThrough
0646: .equalsIgnoreCase("application") || rd
0647: .getParameter("cw_inChannelLink") != null)) {
0648: // keyword and parameter processing
0649: // NOTE: if both exist, only keywords are appended
0650: String keywords = rd.getKeywords();
0651: if (keywords != null) {
0652: if (appendchar.equals("&"))
0653: newXML.append("&keywords=" + keywords);
0654: else
0655: newXML.append(keywords);
0656: } else {
0657: // want all runtime parameters not specific to WebProxy
0658: Enumeration e = rd.getParameterNames();
0659: if (e != null) {
0660: while (e.hasMoreElements()) {
0661: String pName = (String) e
0662: .nextElement();
0663: if (!pName.startsWith("cw_")
0664: && !pName
0665: .startsWith("upc_")
0666: && !pName.trim().equals("")) {
0667: String[] value_array = rd
0668: .getParameterValues(pName);
0669: int i = 0;
0670: while (i < value_array.length) {
0671: newXML.append(appendchar);
0672: appendchar = "&";
0673: newXML.append(pName);
0674: newXML.append("=");
0675: try {
0676: newXML
0677: .append(URLEncoder
0678: .encode(
0679: value_array[i++]
0680: .trim(),
0681: "UTF-8"));
0682: } catch (UnsupportedEncodingException e1) {
0683: throw new RuntimeException(
0684: e1);
0685: }
0686: }
0687: }
0688: }
0689: }
0690: }
0691: }
0692:
0693: state.reqParameters = newXML.toString();
0694: state.fullxmlUri = state.xmlUri;
0695: if (!state.runtimeData.getHttpRequestMethod()
0696: .equals("POST")) {
0697: if ((state.reqParameters != null)
0698: && (!state.reqParameters.trim().equals(
0699: ""))) {
0700: appendchar = (state.xmlUri.indexOf('?') == -1) ? "?"
0701: : "&";
0702: state.fullxmlUri = state.fullxmlUri
0703: + appendchar + state.reqParameters;
0704: }
0705: state.reqParameters = null;
0706: }
0707:
0708: //log.debug("CWebProxy: fullxmlUri now: " + state.fullxmlUri);
0709: }
0710:
0711: // set key for cache based on request parameters
0712: // NOTE: POST requests are not idempotent and therefore are not
0713: // retrievable from the cache
0714: if (!state.runtimeData.getHttpRequestMethod().equals(
0715: "POST"))
0716: state.key = state.fullxmlUri;
0717: else
0718: //generate a unique string as key
0719: state.key = String.valueOf((new Date()).getTime());
0720:
0721: }
0722: }
0723: }
0724:
0725: /**
0726: * Process portal events. Currently supported events are
0727: * EDIT_BUTTON_EVENT, HELP_BUTTON_EVENT, ABOUT_BUTTON_EVENT,
0728: * and SESSION_DONE. The button events work by changing the xmlUri.
0729: * The new Uri's content should contain a link that will refer back
0730: * to the old one at the end of its task.
0731: * @param ev the event
0732: */
0733: public void receiveEvent(PortalEvent ev) {
0734: ChannelState state = chanState;
0735: if (state == null) {
0736: log.debug("CWebProxy:receiveEvent() : no entry in state");
0737: } else {
0738: int evnum = ev.getEventNumber();
0739:
0740: switch (evnum) {
0741: case PortalEvent.EDIT_BUTTON_EVENT:
0742: if (state.editUri != null)
0743: state.buttonxmlUri = state.editUri;
0744: break;
0745: case PortalEvent.HELP_BUTTON_EVENT:
0746: if (state.helpUri != null)
0747: state.buttonxmlUri = state.helpUri;
0748: break;
0749: case PortalEvent.ABOUT_BUTTON_EVENT:
0750: if (state.infoUri != null)
0751: state.buttonxmlUri = state.infoUri;
0752: break;
0753: default:
0754: break;
0755: }
0756: }
0757: }
0758:
0759: /**
0760: * Acquires ChannelRuntimeProperites from the channel.
0761: * This function may be called by the portal framework throughout the session.
0762: * @see ChannelRuntimeProperties
0763: */
0764: public ChannelRuntimeProperties getRuntimeProperties() {
0765: ChannelRuntimeProperties rp = new ChannelRuntimeProperties();
0766:
0767: // determine if such channel is registered
0768: if (chanState == null) {
0769: rp.setWillRender(false);
0770: log
0771: .debug("CWebProxy:getRuntimeProperties() : no entry in state");
0772: }
0773: return rp;
0774: }
0775:
0776: /**
0777: * Ask channel to render its content.
0778: * @param out the SAX ContentHandler to output content to
0779: */
0780: public void renderXML(ContentHandler out) throws PortalException {
0781: ChannelState state = chanState;
0782: if (state == null) {
0783: log.debug("CWebProxy:renderXML() : no entry in state");
0784: } else {
0785: Document xml = null;
0786: String tidiedXml = null;
0787: try {
0788: if (state.tidy != null && state.tidy.equals("on"))
0789: tidiedXml = getTidiedXml(state.fullxmlUri, state);
0790: else
0791: xml = getXml(state.fullxmlUri, state);
0792: } catch (Exception e) {
0793: throw new GeneralRenderingException(
0794: "Problem retrieving contents of "
0795: + state.fullxmlUri
0796: + ". Please restart channel. ", e,
0797: false, true);
0798: }
0799:
0800: state.runtimeData.put("baseActionURL", state.runtimeData
0801: .getBaseActionURL());
0802: state.runtimeData.put("downloadActionURL",
0803: state.runtimeData.getBaseWorkerURL("download"));
0804:
0805: // Runtime data parameters are handed to the stylesheet.
0806: // Add any static data parameters so it gets a full set of variables.
0807: // We may wish to remove this feature since we don't need it for
0808: // the default stylesheets now.
0809: if (state.xmlUri != null)
0810: state.runtimeData.put("cw_xml", state.xmlUri);
0811: if (state.sslUri != null)
0812: state.runtimeData.put("cw_ssl", state.sslUri);
0813: if (state.xslTitle != null)
0814: state.runtimeData.put("cw_xslTitle", state.xslTitle);
0815: if (state.xslUri != null)
0816: state.runtimeData.put("cw_xsl", state.xslUri);
0817: if (state.passThrough != null)
0818: state.runtimeData.put("cw_passThrough",
0819: state.passThrough);
0820: if (state.tidy != null)
0821: state.runtimeData.put("cw_tidy", state.tidy);
0822: if (state.infoUri != null)
0823: state.runtimeData.put("cw_info", state.infoUri);
0824: if (state.helpUri != null)
0825: state.runtimeData.put("cw_help", state.helpUri);
0826: if (state.editUri != null)
0827: state.runtimeData.put("cw_edit", state.editUri);
0828: if (state.person != null)
0829: state.runtimeData.put("cw_person", state.person);
0830: if (state.personAllow != null)
0831: state.runtimeData.put("cw_personAllow",
0832: state.personAllow);
0833:
0834: XSLT xslt = XSLT.getTransformer(this , state.runtimeData
0835: .getLocales());
0836: if (tidiedXml != null)
0837: xslt.setXML(tidiedXml);
0838: else
0839: xslt.setXML(xml);
0840: if (state.xslUri != null
0841: && (!state.xslUri.trim().equals("")))
0842: xslt.setXSL(state.xslUri);
0843: else
0844: xslt.setXSL(state.sslUri, state.xslTitle,
0845: state.runtimeData.getBrowserInfo());
0846:
0847: // Determine mime type
0848: MediaManager mm = MEDIAMANAGER;
0849: String media = mm.getMedia(state.runtimeData
0850: .getBrowserInfo());
0851: String mimeType = mm.getReturnMimeType(media);
0852: if (MediaManager.UNKNOWN.equals(mimeType)) {
0853: String accept = state.runtimeData.getBrowserInfo()
0854: .getHeader("accept");
0855: if (accept != null && accept.indexOf("text/html") != -1) {
0856: mimeType = "text/html";
0857: }
0858: }
0859:
0860: CWebProxyURLFilter filter2 = CWebProxyURLFilter
0861: .newCWebProxyURLFilter(mimeType, state.runtimeData,
0862: out);
0863: AbsoluteURLFilter filter1 = AbsoluteURLFilter
0864: .newAbsoluteURLFilter(mimeType, state.xmlUri,
0865: filter2);
0866:
0867: xslt.setTarget(filter1);
0868:
0869: xslt.setStylesheetParameters(state.runtimeData);
0870: xslt.transform();
0871: }
0872: }
0873:
0874: /**
0875: * Get the contents of a URI as a Document object. This is used if tidy
0876: * is not set or equals 'off'.
0877: * Also includes support for cookies.
0878: * @param uri the URI
0879: * @return the data pointed to by a URI as a Document object
0880: */
0881: private Document getXml(String uri, ChannelState state)
0882: throws Exception {
0883: long start = System.currentTimeMillis();
0884: int status = 0;
0885:
0886: Document doc = null;
0887: try {
0888: URLConnection urlConnect = getConnection(uri, state);
0889: if (urlConnect instanceof HttpURLConnection) {
0890: status = ((HttpURLConnection) urlConnect)
0891: .getResponseCode();
0892: }
0893: DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
0894: .newInstance();
0895: docBuilderFactory.setNamespaceAware(false);
0896: DocumentBuilder docBuilder = docBuilderFactory
0897: .newDocumentBuilder();
0898: DTDResolver dtdResolver = new DTDResolver();
0899: docBuilder.setEntityResolver(dtdResolver);
0900: InputStream is = null;
0901: try {
0902: is = urlConnect.getInputStream();
0903: doc = docBuilder.parse(is);
0904: } finally {
0905: try {
0906: is.close();
0907: } catch (Exception e) {
0908: // ignore
0909: }
0910: }
0911:
0912: } finally {
0913: long elapsedTimeMillis = System.currentTimeMillis() - start;
0914: logAccess(uri, state, status, elapsedTimeMillis);
0915: }
0916: return doc;
0917: }
0918:
0919: private void logAccess(String uri, ChannelState state, int status,
0920: long elapsedTimeMillis) {
0921: // trim off request parameters
0922: int index = uri.indexOf("?");
0923: if (index >= 0) {
0924: uri = uri.substring(0, index);
0925: }
0926: // trim off extra spaces and replace needed spaces with %20
0927: uri = uri.trim().replaceAll(" ", "%20");
0928: accessLog.info("logAccess: " + state.publishId + " "
0929: + state.iperson.getAttribute(IPerson.USERNAME) + " "
0930: + uri + " " + status + " " + elapsedTimeMillis / 1000F);
0931: }
0932:
0933: /**
0934: * Get the contents of a URI as a String but send it through tidy first.
0935: * Also includes support for cookies.
0936: * @param uri the URI
0937: * @return the data pointed to by a URI as a String
0938: */
0939: private String getTidiedXml(String uri, ChannelState state)
0940: throws Exception {
0941: long start = System.currentTimeMillis();
0942: String tidiedXml = null;
0943: int status = 0;
0944:
0945: try {
0946: URLConnection urlConnect = getConnection(uri, state);
0947: if (urlConnect instanceof HttpURLConnection) {
0948: status = ((HttpURLConnection) urlConnect)
0949: .getResponseCode();
0950: }
0951:
0952: // get character encoding from Content-Type header
0953: String encoding = null;
0954: String ct = urlConnect.getContentType();
0955: int i;
0956: if (ct != null && (i = ct.indexOf("charset=")) != -1) {
0957: encoding = ct.substring(i + 8).trim();
0958: if ((i = encoding.indexOf(";")) != -1)
0959: encoding = encoding.substring(0, i).trim();
0960: if (encoding.indexOf("\"") != -1)
0961: encoding = encoding.substring(1,
0962: encoding.length() + 1);
0963: }
0964:
0965: Tidy tidy = new Tidy();
0966:
0967: tidy.setXHTML(true);
0968: tidy.setDocType("omit");
0969: tidy.setQuiet(true);
0970: tidy.setShowWarnings(false);
0971: tidy.setNumEntities(true);
0972: tidy.setWord2000(true);
0973:
0974: // If charset is specified in header, set JTidy's
0975: // character encoding to either UTF-8, ISO-8859-1
0976: // or ISO-2022 accordingly (NOTE that these are
0977: // the only character encoding sets that are supported in
0978: // JTidy). If character encoding is not specified,
0979: // UTF-8 is the default.
0980: if (encoding != null) {
0981: if (encoding.toLowerCase().equals("iso-8859-1"))
0982: tidy
0983: .setCharEncoding(org.w3c.tidy.Configuration.LATIN1);
0984: else if (encoding.toLowerCase().equals("iso-2022-jp"))
0985: tidy
0986: .setCharEncoding(org.w3c.tidy.Configuration.ISO2022);
0987: else
0988: tidy
0989: .setCharEncoding(org.w3c.tidy.Configuration.UTF8);
0990: } else {
0991: tidy.setCharEncoding(org.w3c.tidy.Configuration.UTF8);
0992: }
0993:
0994: tidy.setErrout(devNull);
0995:
0996: ByteArrayOutputStream stream = new ByteArrayOutputStream(
0997: 1024);
0998: BufferedOutputStream out = new BufferedOutputStream(stream);
0999:
1000: tidy.parse(urlConnect.getInputStream(), out);
1001: tidiedXml = stream.toString();
1002: stream.close();
1003: out.close();
1004:
1005: if (tidy.getParseErrors() > 0)
1006: throw new GeneralRenderingException(
1007: "Unable to convert input document to XHTML");
1008: } finally {
1009: long elapsedTimeMillis = System.currentTimeMillis() - start;
1010: logAccess(uri, state, status, elapsedTimeMillis);
1011: }
1012: return tidiedXml;
1013: }
1014:
1015: private URLConnection getConnection(String uri, ChannelState state)
1016: throws Exception {
1017: // before making the connection, ensure all spaces in the URI are encoded
1018: // (Note that URLEncoder.encode(String uri) cannot be used because
1019: // this method encodes everything, including forward slashes and
1020: // forward slashes are used for determining if the URL is
1021: // relative or absolute)
1022:
1023: uri = uri.trim().replaceAll(" ", "%20");
1024:
1025: URL url;
1026: if (state.localConnContext != null)
1027: url = ResourceLoader.getResourceAsURL(this .getClass(),
1028: state.localConnContext.getDescriptor(uri,
1029: state.runtimeData));
1030: else
1031: url = ResourceLoader.getResourceAsURL(this .getClass(), uri);
1032:
1033: // get info from url for cookies
1034: String domain = url.getHost().trim();
1035: String path = url.getPath();
1036: if (path.indexOf("/") != -1) {
1037: if (path.lastIndexOf("/") != 0)
1038: path = path.substring(0, path.lastIndexOf("/"));
1039: }
1040: String port = Integer.toString(url.getPort());
1041:
1042: //get connection
1043: URLConnection urlConnect = url.openConnection();
1044: String protocol = url.getProtocol();
1045:
1046: if (protocol.equals("http") || protocol.equals("https")) {
1047: if (domain != null && path != null) {
1048: //prepare the connection by setting properties and sending data
1049: HttpURLConnection httpUrlConnect = (HttpURLConnection) urlConnect;
1050: httpUrlConnect.setInstanceFollowRedirects(false);
1051: //send any cookie headers to proxied application
1052: if (state.cookieCutter.cookiesExist())
1053: state.cookieCutter.sendCookieHeader(httpUrlConnect,
1054: domain, path, port);
1055: //set connection properties if request method was post
1056: if (state.runtimeData.getHttpRequestMethod().equals(
1057: "POST")) {
1058: if ((state.reqParameters != null)
1059: && (!state.reqParameters.trim().equals(""))) {
1060: httpUrlConnect.setRequestMethod("POST");
1061: httpUrlConnect.setAllowUserInteraction(false);
1062: httpUrlConnect.setDoOutput(true);
1063: }
1064: }
1065:
1066: //send local data, if required
1067: //can call getOutputStream in sendLocalData (ie. to send post params)
1068: //(getOutputStream can be called twice on an HttpURLConnection)
1069: if (state.localConnContext != null) {
1070: try {
1071: state.localConnContext.sendLocalData(
1072: httpUrlConnect, state.runtimeData);
1073: } catch (Exception e) {
1074: log
1075: .error(
1076: "CWebProxy: Unable to send data through "
1077: + state.runtimeData
1078: .getParameter("upc_localConnContext"),
1079: e);
1080: }
1081: }
1082:
1083: //send the request parameters by post, if required
1084: //at this point, set or send methods cannot be called on the connection
1085: //object (they must be called before sendLocalData)
1086: if (state.runtimeData.getHttpRequestMethod().equals(
1087: "POST")) {
1088: if ((state.reqParameters != null)
1089: && (!state.reqParameters.trim().equals(""))) {
1090: PrintWriter post = new PrintWriter(
1091: httpUrlConnect.getOutputStream());
1092: post.print(state.reqParameters);
1093: post.flush();
1094: post.close();
1095: state.reqParameters = null;
1096: }
1097: }
1098:
1099: //receive cookie headers
1100: state.cookieCutter.storeCookieHeader(httpUrlConnect,
1101: domain, path, port);
1102:
1103: int status = httpUrlConnect.getResponseCode();
1104: String location = httpUrlConnect
1105: .getHeaderField("Location");
1106: switch (status) {
1107: case HttpURLConnection.HTTP_NOT_FOUND:
1108: throw new ResourceMissingException(httpUrlConnect
1109: .getURL().toExternalForm(), "",
1110: "HTTP Status-Code 404: Not Found");
1111: case HttpURLConnection.HTTP_FORBIDDEN:
1112: throw new ResourceMissingException(httpUrlConnect
1113: .getURL().toExternalForm(), "",
1114: "HTTP Status-Code 403: Forbidden");
1115: case HttpURLConnection.HTTP_INTERNAL_ERROR:
1116: throw new ResourceMissingException(httpUrlConnect
1117: .getURL().toExternalForm(), "",
1118: "HTTP Status-Code 500: Internal Server Error");
1119: case HttpURLConnection.HTTP_NO_CONTENT:
1120: throw new ResourceMissingException(httpUrlConnect
1121: .getURL().toExternalForm(), "",
1122: "HTTP Status-Code 204: No Content");
1123: /*
1124: * Note: these cases apply to http status codes 302 and 303
1125: * this will handle automatic redirection to a new GET URL
1126: */
1127: case HttpURLConnection.HTTP_MOVED_TEMP:
1128: httpUrlConnect.disconnect();
1129: httpUrlConnect = (HttpURLConnection) getConnection(
1130: location, state);
1131: break;
1132: case HttpURLConnection.HTTP_SEE_OTHER:
1133: httpUrlConnect.disconnect();
1134: httpUrlConnect = (HttpURLConnection) getConnection(
1135: location, state);
1136: break;
1137: /*
1138: * Note: this cases apply to http status code 301
1139: * it will handle the automatic redirection of GET requests.
1140: * The spec calls for a POST redirect to be verified manually by the user
1141: * Rather than bypass this security restriction, we will throw an exception
1142: */
1143: case HttpURLConnection.HTTP_MOVED_PERM:
1144: if (state.runtimeData.getHttpRequestMethod()
1145: .equals("GET")) {
1146: httpUrlConnect.disconnect();
1147: httpUrlConnect = (HttpURLConnection) getConnection(
1148: location, state);
1149: } else {
1150: throw new ResourceMissingException(
1151: httpUrlConnect.getURL()
1152: .toExternalForm(), "",
1153: "HTTP Status-Code 301: POST Redirection currently not supported");
1154: }
1155: break;
1156: default:
1157: break;
1158: }
1159:
1160: return (URLConnection) httpUrlConnect;
1161: }
1162: }
1163: return urlConnect;
1164: }
1165:
1166: public ChannelCacheKey generateKey() {
1167: ChannelState state = chanState;
1168:
1169: if (state == null) {
1170: log.debug("CWebProxy:generateKey() : no entry in state");
1171: return null;
1172: }
1173:
1174: if (state.cacheMode.equalsIgnoreCase("none"))
1175: return null;
1176: // else if http see first if caching is on or off. if it's on,
1177: // store the validity time in the state, cache it, and further
1178: // resolve later with isValid.
1179: // check cache-control, no-cache, must-revalidate, max-age,
1180: // Date & Expires, expiry in past
1181: // for 1.0 check pragma for no-cache
1182: // add a warning to docs about not a full http 1.1 impl.
1183:
1184: ChannelCacheKey k = new ChannelCacheKey();
1185: StringBuffer sbKey = new StringBuffer(1024);
1186:
1187: // Only INSTANCE scope is currently supported.
1188: k.setKeyScope(ChannelCacheKey.INSTANCE_KEY_SCOPE);
1189:
1190: sbKey.append("sslUri:").append(state.sslUri).append(", ");
1191:
1192: // xslUri may either be specified as a parameter to this channel
1193: // or we will get it by looking in the stylesheet list file
1194: String xslUriForKey = state.xslUri;
1195: try {
1196: if (xslUriForKey == null) {
1197: String sslUri = ResourceLoader.getResourceAsURLString(
1198: this .getClass(), state.sslUri);
1199: xslUriForKey = XSLT.getStylesheetURI(sslUri,
1200: state.runtimeData.getBrowserInfo());
1201: }
1202: } catch (Exception e) {
1203: xslUriForKey = "Not attainable: " + e;
1204: }
1205:
1206: sbKey.append("xslUri:").append(xslUriForKey).append(", ");
1207: sbKey.append("key:").append(state.key).append(", ");
1208: sbKey.append("passThrough:").append(state.passThrough).append(
1209: ", ");
1210: sbKey.append("tidy:").append(state.tidy).append(", ");
1211: sbKey.append("person:").append(state.person);
1212: k.setKey(sbKey.toString());
1213: k.setKeyValidity(new Long(System.currentTimeMillis()));
1214: //log.debug("CWebProxy:generateKey("
1215: // + uid + ") : cachekey=\"" + sbKey.toString() + "\"");
1216: return k;
1217: }
1218:
1219: static PrintWriter getErrout() throws FileNotFoundException {
1220: if (System.getProperty("os.name").indexOf("Windows") != -1)
1221: return new PrintWriter(new FileOutputStream("nul"));
1222: else
1223: return new PrintWriter(new FileOutputStream("/dev/null"));
1224: }
1225:
1226: public boolean isCacheValid(Object validity) {
1227: if (!(validity instanceof Long))
1228: return false;
1229:
1230: ChannelState state = chanState;
1231:
1232: if (state == null) {
1233: log.debug("CWebProxy:isCacheValid() : no entry in state");
1234: return false;
1235: } else {
1236: return (System.currentTimeMillis()
1237: - ((Long) validity).longValue() < state.cacheTimeout * 1000);
1238: }
1239: }
1240:
1241: public String getContentType() {
1242: return chanState.connHolder.getContentType();
1243: }
1244:
1245: public InputStream getInputStream() throws IOException {
1246: InputStream rs = chanState.connHolder.getInputStream();
1247: chanState.connHolder = null;
1248: return rs;
1249: }
1250:
1251: public void downloadData(OutputStream out) throws IOException {
1252: throw (new IOException(
1253: "CWebProxy: downloadData method not supported - use getInputStream only"));
1254: }
1255:
1256: public String getName() {
1257: return "proxyDL";
1258: }
1259:
1260: public Map getHeaders() {
1261: ChannelState state = chanState;
1262: try {
1263: state.connHolder = getConnection(state.fullxmlUri, state);
1264: } catch (Exception e) {
1265: log.error(e, e);
1266: }
1267: Map rhdrs = new HashMap();
1268: int i = 0;
1269: while (state.connHolder.getHeaderFieldKey(i) != null) {
1270: rhdrs.put(state.connHolder.getHeaderFieldKey(i),
1271: state.connHolder.getHeaderField(i));
1272: i++;
1273: }
1274: return rhdrs;
1275: }
1276:
1277: public void reportDownloadError(Exception e) {
1278: // We really should report this to the user somehow??
1279: log.error(e.getMessage(), e);
1280: }
1281:
1282: }
|