001 /*
002 * LAPIS lightweight structured text processing system
003 *
004 * Copyright (C) 1998-2002 Carnegie Mellon University,
005 * Copyright (C) 2003 Massachusetts Institute of Technology.
006 * All rights reserved.
007 *
008 * This library is free software; you can redistribute it
009 * and/or modify it under the terms of the GNU General
010 * Public License as published by the Free Software
011 * Foundation, version 2.
012 *
013 * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/
014 */
015
016 package lapisx.net;
017
018 import java.net.*;
019 import java.io.*;
020 import java.util.*;
021 import java.lang.reflect.*;
022
023 // FIX: support multipart/form-data as well
024 public class FormURL {
025 String method;
026 URL action;
027 URL url;
028 String queryString;
029 Vector queryVector;
030
031 /**
032 * Create a query using a given method (get or post) and query data.
033 * @param method "get" or "post" (case-insensitive).
034 * @param url URL to send query to. Must be http:
035 * @param query Name/value pairs to simulate submitting a form. Names and values may be arbitrary objects; this method uses toString() to convert them to strings, then URL-encodes them before sending to the web server. Pass null if no form data is desired.
036 */
037 public FormURL (String method, URL url, Vector query) {
038 this (method, url, (query != null) ? encodeQuery (query) : null);
039 queryVector = query;
040 }
041
042 /**
043 * Open a URL using a given method (get or post) and query data.
044 * @param method "get" or "post" (case-insensitive).
045 * @param url URL to open. Must be http:
046 * @param query URL-encoded name/value pairs, or null if no query desired
047 */
048 public FormURL (String method, URL url, String query) {
049 this.method = method;
050 this.action = url;
051 this.url = url;
052 this.queryString = (query != null) ? query : "";
053
054 if (method == null || "get".equalsIgnoreCase (method)) {
055 try {
056 this.url = new URL (URLUtil.getServiceURL (url) + "?" + query);
057 } catch (MalformedURLException e) {
058 throw new RuntimeException (e.toString ());// shouldn't happen
059 }
060 }
061 }
062
063 // FIX: constructor from a String (assuming GET), or String + method
064
065 public String getMethod () {
066 return method;
067 }
068
069 public URL getAction () {
070 return url;
071 }
072
073 public URL getURL () {
074 return url;
075 }
076
077 public String getQueryString () {
078 return queryString;
079 }
080
081 // FIX: write decodeQuery, so that this can work even when
082 // FormURL is constructed from a string, not a hash
083 public Vector getQueryVector () {
084 return queryVector;
085 }
086
087 public String toString () {
088 return url.toString ();
089 }
090
091 /**
092 * Open a connection ready to submit query and retrieve its results.
093 * @return URLConnection (set up for connection but not yet connected)
094 */
095 public URLConnection openConnection () throws IOException {
096 if (!"http".equals (url.getProtocol ()))
097 throw new IOException ("query URL must be http:");
098
099 URLConnection conn = url.openConnection ();
100
101 // handle POST method
102 if ("post".equalsIgnoreCase (method)) {
103 conn.setDoOutput (true);
104 conn.setUseCaches (false);
105 conn.setRequestProperty ("Content-type",
106 "application/x-www-form-urlencoded");
107 conn.setRequestProperty ("Content-length", String.valueOf (queryString.length()));
108
109 // commence request
110 PrintWriter out = null;
111 try {
112 out = new PrintWriter (new OutputStreamWriter
113 (conn.getOutputStream ()));
114 // System.err.println ("Sending by POST:\n" + queryString + "\ndone");
115 out.print (queryString);
116 out.flush ();
117 } finally {
118 if (out != null)
119 out.close ();
120 }
121 }
122
123 return conn;
124 }
125
126 /**
127 * URL-encode a sequence of name-value pairs.
128 * @param query Name/value pairs to encode; even indexes are names, odd indexes are values
129 * @return String of URL-encoded name/value pairs
130 */
131 public static String encodeQuery (Vector query) {
132 StringBuffer result = new StringBuffer ();
133
134 for (int i = 0, n = query.size (); i < n; i+=2) {
135 String name = (String)query.elementAt (i);
136 String value = (String)query.elementAt (i+1);
137
138 if (result.length () > 0)
139 result.append ('&');
140
141 result.append (ENCODER.encode (name));
142 result.append ('=');
143 result.append (ENCODER.encode (value));
144 }
145 return result.toString ();
146 }
147
148 //
149 // The following craziness calls the deprecated URLEncoder.encode(string)
150 // only when we have to, in Java 1.2 and 1.3. In Java 1.4 and later,
151 // we call the preferred URLEncoder.encode(string, character-encoding).
152 //
153 private static Encoder ENCODER;
154
155 private interface Encoder {
156 public String encode (String s);
157 }
158
159 /**
160 * Wrapper for deprecated Java 1.2/1.3 URLEncoder.encode().
161 */
162 private static class Encoder2 implements Encoder {
163 Class cls;
164 Method method;
165 public Encoder2 () throws Exception {
166 cls = Class.forName ("java.net.URLEncoder");
167 method = cls.getMethod ("encode", new Class[] {String.class});
168 }
169 public String encode (String s) {
170 try {
171 return (String) method.invoke (cls, new Object[] {s});
172 } catch (Exception e) {
173 throw new RuntimeException (e.toString ());
174 }
175 }
176 }
177
178 /**
179 * Wrapper for preferred Java 1.4 & later URLEncoder.encode().
180 */
181 private static class Encoder4 implements Encoder {
182 Class cls;
183 Method method;
184 public Encoder4 () throws Exception {
185 cls = Class.forName ("java.net.URLEncoder");
186 method = cls.getMethod ("encode",
187 new Class[] {String.class, String.class});
188 }
189 public String encode (String s) {
190 try {
191 return (String) method.invoke (cls, new Object[] {s, "UTF-8"});
192 } catch (Exception e) {
193 throw new RuntimeException (e.toString ());
194 }
195 }
196 }
197
198 /**
199 * Determine which version of Java is running and
200 * set ENCODER appropriately.
201 */
202 static {
203 String javaVersion = System.getProperty ("java.version");
204 try {
205 if (javaVersion.startsWith ("1.2")
206 || javaVersion.startsWith ("1.3"))
207 ENCODER = new Encoder2 ();
208 else
209 ENCODER = new Encoder4 ();
210 } catch (Exception e) {
211 throw new RuntimeException (e.toString ());
212 }
213 }
214 }