001: /*
002: * Created on Jan 7, 2005
003: */
004: package uk.org.ponder.servletutil;
005:
006: import java.io.InputStream;
007: import java.io.OutputStream;
008: import java.io.OutputStreamWriter;
009: import java.net.HttpURLConnection;
010: import java.net.URL;
011: import java.util.Collections;
012: import java.util.Enumeration;
013: import java.util.HashMap;
014: import java.util.Iterator;
015: import java.util.Map;
016:
017: import javax.servlet.RequestDispatcher;
018: import javax.servlet.http.HttpServletRequest;
019: import javax.servlet.http.HttpServletRequestWrapper;
020: import javax.servlet.http.HttpServletResponse;
021:
022: import uk.org.ponder.streamutil.StreamCopier;
023: import uk.org.ponder.streamutil.StreamCopyUtil;
024: import uk.org.ponder.streamutil.StreamCloseUtil;
025: import uk.org.ponder.stringutil.CharWrap;
026: import uk.org.ponder.stringutil.URLEncoder;
027: import uk.org.ponder.util.Logger;
028: import uk.org.ponder.util.UniversalRuntimeException;
029:
030: /**
031: * Automates the process of forwarding a servlet request to a remote server.
032: * After a call to <code>populate</code> the request parameters are available
033: * in modifiable form in <code>parametermap</code> prior to dispatch via a
034: * call to <code>dispatchTo</code>.
035: * <p>
036: * Alternatively it may be used to conveniently forward a request to a local
037: * servlet, with the parameters as modified in the parametermap.
038: * <p>
039: * ServletForwardPackages are highly non-threadsafe and should be destroyed
040: * after use.
041: *
042: * @author Antranig Basman (antranig@caret.cam.ac.uk)
043: *
044: */
045: public class ServletForwardPackage {
046: public static final String CONTENT_TYPE = "Content-Type";
047: public HttpServletRequest req;
048: public HttpServletResponse res;
049: public HashMap parametermap = new HashMap();
050: public String targeturl;
051: //public String localurlbase;
052: public StreamCopier streamcopier;
053: public String charencoding = "UTF-8";
054: public ServletRequestWrapper wrapper;
055:
056: // assume no multiple-valued parameters from US.
057: public void addParameter(String key, String value) {
058: parametermap.put(key, new String[] { value });
059: }
060:
061: private static ThreadLocal copybuffer = new ThreadLocal();
062:
063: private static byte[] getBuffer() {
064: byte[] togo = (byte[]) copybuffer.get();
065: if (togo == null) {
066: togo = new byte[8192];
067: copybuffer.set(togo);
068: }
069: return togo;
070: }
071:
072: private static StreamCopier defaultcopier = new StreamCopier() {
073: public void copyStream(InputStream in, OutputStream out) {
074: StreamCopyUtil.inputToOutput(in, out, getBuffer());
075: }
076: };
077: private boolean unwrapredirect;
078:
079: /** Initialise this ForwardPackage with details taken from the specified
080: * request and response objects.
081: */
082: public void populate(HttpServletRequest req, HttpServletResponse res) {
083: this .req = req;
084: this .res = res;
085: this .streamcopier = defaultcopier;
086: parametermap.clear();
087: parametermap.putAll(req.getParameterMap());
088: }
089:
090: /** <it>Forward</it> the request request that this package represents
091: * WITHIN this servlet container via a RequestDispatcher to the supplied
092: * URL.
093: */
094: public void forwardTo(String baseurl) {
095: Logger.log
096: .info("**ServletForwardPackage beginning LOCAL forward to baseurl "
097: + baseurl);
098:
099: try {
100: RequestDispatcher rd = req.getRequestDispatcher(baseurl);
101: HttpServletRequestWrapper hsrw = new HttpServletRequestWrapper(
102: req) {
103: public Map getParameterMap() {
104: return parametermap;
105: }
106:
107: public String getParameter(String key) {
108: String[] paramvals = getParameterValues(key);
109: return (paramvals == null || paramvals.length == 0 ? null
110: : paramvals[0]);
111: }
112:
113: public String[] getParameterValues(String key) {
114: return (String[]) parametermap.get(key);
115: }
116:
117: public Enumeration getParameterNames() {
118: return Collections.enumeration(parametermap
119: .keySet());
120: }
121: };
122: if (streamcopier == defaultcopier) {
123: try {
124: if (wrapper != null) {
125: wrapper.startRequest(hsrw, res);
126: }
127: rd.forward(hsrw, res);
128: } finally {
129: if (wrapper != null) {
130: wrapper.endRequest();
131: }
132: }
133: } else
134: throw new UniversalRuntimeException(
135: "Filtered local forward not yet implemented");
136: } catch (Throwable t) {
137: throw UniversalRuntimeException.accumulate(t,
138: "Error forwarding servlet request to " + baseurl);
139: }
140: }
141:
142: public void setUnwrapRedirect(boolean unwrapredirect) {
143: this .unwrapredirect = unwrapredirect;
144: }
145:
146: /** Perform a <it>dispatch</it> of this request to a remote servlet using raw
147: * HTTP. Return the target URL if the remote servlet issues a redirect,
148: * otherwise null.
149: */
150: public String dispatchTo(String baseurl) {
151: String location = null;
152: Logger.log
153: .info("**ServletForwardPackage beginning REMOTE dispatch to baseurl "
154: + baseurl);
155: CharWrap tobuild = new CharWrap();
156: boolean first = true;
157: for (Iterator it = parametermap.keySet().iterator(); it
158: .hasNext();) {
159: String key = (String) it.next();
160: String[] value = (String[]) parametermap.get(key);
161: for (int i = 0; i < value.length; ++i) {
162: tobuild.append(first ? "" : "&").append(
163: URLEncoder.encode(key)).append('=').append(
164: URLEncoder.encode(value[i]));
165: first = false;
166: }
167: }
168: String parameters = tobuild.toString();
169: Logger.log
170: .info("**ServletForwardPackage composed parameter string "
171: + parameters);
172:
173: String method = req.getMethod();
174: boolean ispost = method.equals("POST");
175: String fullurl = baseurl + (ispost ? "" : "?" + parameters);
176: try {
177: Logger.log.info("URL length: " + fullurl.length()
178: + " characters");
179: URL URL = new URL(fullurl);
180: HttpURLConnection huc = (HttpURLConnection) URL
181: .openConnection();
182: huc.setInstanceFollowRedirects(!unwrapredirect);
183: if (ispost) {
184: huc.setRequestMethod("POST");
185: huc.setDoOutput(true);
186: huc
187: .setRequestProperty(CONTENT_TYPE,
188: "application/x-www-form-urlencoded; charset=UTF-8");
189: OutputStreamWriter osw = null;
190: try {
191: OutputStream os = huc.getOutputStream();
192: osw = new OutputStreamWriter(os, charencoding);
193: osw.write(parameters);
194: } finally {
195: StreamCloseUtil.closeWriter(osw);
196: }
197: }
198: InputStream is = null;
199: OutputStream clientout = null;
200: try {
201: int response = huc.getResponseCode();
202:
203: if (unwrapredirect && response >= 300 && response < 400) {
204: Logger.log
205: .info("Received redirect response with message: "
206: + huc.getResponseMessage());
207: location = huc.getHeaderField("Location");
208: Logger.log
209: .info("Issuing client redirect to location "
210: + location);
211: res.sendRedirect(location);
212: } else {
213: String contenttype = huc.getContentType();
214: Logger.log
215: .info("Forwarding for received content type "
216: + contenttype);
217: res.setContentType(contenttype);
218: clientout = res.getOutputStream();
219: is = huc.getInputStream();
220: //is = StreamCopyUtil.bottleToDisk(is, "e:\\courseworkpage.txt");
221: streamcopier.copyStream(is, clientout);
222: }
223: } finally {
224: StreamCloseUtil.closeInputStream(is);
225: StreamCloseUtil.closeOutputStream(clientout);
226: }
227: } catch (Throwable t) {
228: throw UniversalRuntimeException.accumulate(t,
229: "Error dispatching servlet request to " + baseurl);
230: }
231: return location;
232: }
233: }
|