001 /*
002 * $Id: ServerAction.java,v 1.7 2005/11/22 13:31:13 kleopatra Exp $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021 package org.jdesktop.swingx.action;
022
023 import java.awt.event.ActionEvent;
024 import java.io.BufferedReader;
025 import java.io.ByteArrayOutputStream;
026 import java.io.IOException;
027 import java.io.InputStreamReader;
028 import java.io.PrintWriter;
029 import java.net.HttpURLConnection;
030 import java.net.MalformedURLException;
031 import java.net.URL;
032 import java.net.URLConnection;
033 import java.net.UnknownHostException;
034 import java.security.AccessControlException;
035 import java.util.HashMap;
036 import java.util.Iterator;
037 import java.util.Map;
038 import java.util.Set;
039 import java.util.logging.Level;
040 import java.util.logging.Logger;
041
042 import javax.swing.AbstractAction;
043 import javax.swing.Action;
044 import javax.swing.Icon;
045
046 //import org.jdesktop.swing.Application;
047
048
049 /**
050 * An action which will invoke an http POST operation.
051 *
052 * @author Mark Davidson
053 */
054 public class ServerAction extends AbstractAction {
055 // Server action support
056 private static final Logger LOG = Logger.getLogger(ServerAction.class
057 .getName());
058 private static final String PARAMS = "action-params";
059 private static final String HEADERS = "action-headers";
060 private static final String URL = "action-url";
061
062 private static final String URL_CACHE = "_URL-CACHE__";
063
064 public ServerAction() {
065 this("action");
066 }
067
068 public ServerAction(String name) {
069 super(name);
070 }
071
072 /**
073 * @param name display name of the action
074 * @param command the value of the action command key
075 */
076 public ServerAction(String name, String command) {
077 this(name, command, null);
078 }
079
080 public ServerAction(String name, Icon icon) {
081 super(name, icon);
082 }
083
084 /**
085 * @param name display name of the action
086 * @param command the value of the action command key
087 * @param icon icon to display
088 */
089 public ServerAction(String name, String command, Icon icon) {
090 super(name, icon);
091 putValue(Action.ACTION_COMMAND_KEY, command);
092 }
093
094 /**
095 * Set the url for the action.
096 * <p>
097 * @param url a string representation of the url
098 */
099 public void setURL(String url) {
100 putValue(URL, url);
101 putValue(URL_CACHE, null);
102 }
103
104 public String getURL() {
105 return (String)getValue(URL);
106 }
107
108 private Map getParams() {
109 return (Map)getValue(PARAMS);
110 }
111
112 private void setParams(Map params) {
113 putValue(PARAMS, params);
114 }
115
116 /**
117 * Adds a name value pair which represents a url parameter in an http
118 * POST request.
119 */
120 public void addParam(String name, String value) {
121 Map params = getParams();
122 if (params == null) {
123 params = new HashMap();
124 setParams(params);
125 }
126 params.put(name, value);
127 }
128
129 /**
130 * Return a parameter value corresponding to name or null if it doesn't exist.
131 */
132 public String getParamValue(String name) {
133 Map params = getParams();
134 return params == null ? null : (String)params.get(name);
135 }
136
137 /**
138 * Return a set of parameter names or null if there are no params
139 */
140 public Set getParamNames() {
141 Map params = getParams();
142 return params == null ? null : params.keySet();
143 }
144
145 private Map getHeaders() {
146 return (Map)getValue(HEADERS);
147 }
148
149 private void setHeaders(Map headers) {
150 putValue(HEADERS, headers);
151 }
152
153 /**
154 * Adds a name value pair which represents a url connection request property.
155 * For example, name could be "Content-Type" and the value could be
156 * "application/x-www-form-urlencoded"
157 */
158 public void addHeader(String name, String value) {
159 Map map = getHeaders();
160 if (map == null) {
161 map = new HashMap();
162 setHeaders(map);
163 }
164 map.put(name, value);
165 }
166
167 /**
168 * Return a header value corresponding to name or null if it doesn't exist.
169 */
170 public String getHeaderValue(String name) {
171 Map headers = getHeaders();
172 return headers == null ? null : (String)headers.get(name);
173 }
174
175 /**
176 * Return a set of parameter names or null if there are no params
177 */
178 public Set getHeaderNames() {
179 Map headers = getHeaders();
180 return headers == null ? null : headers.keySet();
181 }
182
183 /**
184 * Invokes the server operation when the action has been invoked.
185 */
186 public void actionPerformed(ActionEvent evt) {
187 URL execURL = (URL)getValue(URL_CACHE);
188 if (execURL == null && !"".equals(getURL())) {
189 try {
190 String url = getURL();
191 if (url.startsWith("http")) {
192 execURL = new URL(url);
193 } else {
194 }
195 if (execURL == null) {
196 // XXX TODO: send a message
197 return;
198 } else {
199 // Cache this value.
200 putValue(URL_CACHE, execURL);
201 }
202
203 } catch (MalformedURLException ex) {
204 LOG.log(Level.WARNING, "something went wrong...", ex);
205 }
206 }
207
208 try {
209 URLConnection uc = execURL.openConnection();
210
211 // Get all the header name/value pairs ans set the request headers
212 Set headerNames = getHeaderNames();
213 if (headerNames != null && !headerNames.isEmpty()) {
214 Iterator iter = headerNames.iterator();
215 while (iter.hasNext()) {
216 String name = (String)iter.next();
217 uc.setRequestProperty(name, getHeaderValue(name));
218 }
219 }
220 uc.setUseCaches(false);
221 uc.setDoOutput(true);
222
223 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(512);
224 PrintWriter out = new PrintWriter(byteStream, true);
225 out.print(getPostData());
226 out.flush();
227
228 // POST requests must have a content-length.
229 String length = String.valueOf(byteStream.size());
230 uc.setRequestProperty("Content-length", length);
231
232 // Write POST data to real output stream.
233 byteStream.writeTo(uc.getOutputStream());
234
235 BufferedReader buf = null;
236 if (uc instanceof HttpURLConnection) {
237 HttpURLConnection huc = (HttpURLConnection)uc;
238 int code = huc.getResponseCode();
239 String message = huc.getResponseMessage();
240
241 // Handle the result.
242 if (code < 400) {
243 // action succeeded send to status bar
244 // XXX TODO: setStatusMessage(createMessage(code, message));
245 // Format the response
246 // TODO: This should load asychnonously
247 buf = new BufferedReader(new InputStreamReader(uc.getInputStream()));
248
249 } else {
250 // action has failed show dialog
251 // XXX TODO: setStatusMessage(createMessage(code, message));
252 buf = new BufferedReader(new InputStreamReader(huc.getErrorStream()));
253 }
254 String line;
255
256 StringBuffer buffer = new StringBuffer();
257 while ((line = buf.readLine()) != null) {
258 // RG: Fix for J2SE 5.0; Can't cascade append() calls because
259 // return type in StringBuffer and AbstractStringBuilder are different
260 buffer.append(line);
261 buffer.append('\n');
262 }
263 // JW: this used the Debug - maybe use finest level?
264 LOG.finer("returned from connection\n" + buffer.toString());
265 }
266 } catch (UnknownHostException ex) {
267 LOG.log(Level.WARNING, "UnknownHostException detected. Could it be a proxy issue?", ex);
268
269 } catch (AccessControlException ex) {
270 LOG.log(Level.WARNING, "AccessControlException detected", ex);
271 } catch (IOException ex) {
272 LOG.log(Level.WARNING, "IOException detected", ex);
273 }
274 }
275
276 /**
277 * Retrieves a string which represents the parameter data for a server action.
278 * @return a string of name value pairs prefixed by a '?' and delimited by an '&'
279 */
280 private String getPostData() {
281 // Write the data into local buffer
282 StringBuffer postData = new StringBuffer();
283
284 // TODO: the action should be configured to retrieve the data.
285
286 // Get all the param name/value pairs and build the data string
287 Set paramNames = getParamNames();
288 if (paramNames != null && !paramNames.isEmpty()) {
289 Iterator iter = paramNames.iterator();
290 try {
291 while (iter.hasNext()) {
292 String name = (String) iter.next();
293 postData.append('&').append(name).append('=');
294 postData.append(getParamValue(name));
295 }
296 }
297 catch (Exception ex) { // RG: append(char) throws IOException in J2SE 5.0
298 /** @todo Log it */
299 }
300 // Replace the first & with a ?
301 postData.setCharAt(0, '?');
302 }
303
304 LOG.finer("ServerAction: POST data: " + postData.toString());
305 return postData.toString();
306 }
307
308
309 /**
310 * Creates a human readable message from the server code and message result.
311 * @param code an http error code.
312 * @param msg server message
313 */
314 private String createMessage(int code, String msg) {
315 StringBuffer buffer = new StringBuffer("The action \"");
316 buffer.append(getValue(NAME));
317
318 if (code < 400) {
319 buffer.append("\" has succeeded ");
320 } else {
321 buffer.append("\" has failed\nPlease check the Java console for more details.\n");
322 }
323 // RG: Fix for J2SE 5.0; Can't cascade append() calls because
324 // return type in StringBuffer and AbstractStringBuilder are different
325 buffer.append("\nServer response:\nCode: ");
326 buffer.append(code);
327 buffer.append(" Message: ");
328 buffer.append(msg);
329
330 return buffer.toString();
331 }
332 }