001 /* 002 * $Id: ServerAction.java 3197 2009-01-21 17:54:30Z kschaefe $ 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 @SuppressWarnings("unchecked") 109 private Map<String, String> getParams() { 110 return (Map)getValue(PARAMS); 111 } 112 113 private void setParams(Map<String, String> params) { 114 putValue(PARAMS, params); 115 } 116 117 /** 118 * Adds a name value pair which represents a url parameter in an http 119 * POST request. 120 */ 121 public void addParam(String name, String value) { 122 Map<String, String> params = getParams(); 123 if (params == null) { 124 params = new HashMap<String, String>(); 125 setParams(params); 126 } 127 params.put(name, value); 128 } 129 130 /** 131 * Return a parameter value corresponding to name or null if it doesn't exist. 132 */ 133 public String getParamValue(String name) { 134 Map<String, String> params = getParams(); 135 return params == null ? null : params.get(name); 136 } 137 138 /** 139 * Return a set of parameter names or null if there are no params 140 */ 141 public Set<String> getParamNames() { 142 Map<String, String> params = getParams(); 143 return params == null ? null : params.keySet(); 144 } 145 146 @SuppressWarnings("unchecked") 147 private Map<String, String> getHeaders() { 148 return (Map)getValue(HEADERS); 149 } 150 151 private void setHeaders(Map<String, String> headers) { 152 putValue(HEADERS, headers); 153 } 154 155 /** 156 * Adds a name value pair which represents a url connection request property. 157 * For example, name could be "Content-Type" and the value could be 158 * "application/x-www-form-urlencoded" 159 */ 160 public void addHeader(String name, String value) { 161 Map<String, String> map = getHeaders(); 162 if (map == null) { 163 map = new HashMap<String, String>(); 164 setHeaders(map); 165 } 166 map.put(name, value); 167 } 168 169 /** 170 * Return a header value corresponding to name or null if it doesn't exist. 171 */ 172 public String getHeaderValue(String name) { 173 Map<String, String> headers = getHeaders(); 174 return headers == null ? null : headers.get(name); 175 } 176 177 /** 178 * Return a set of parameter names or null if there are no params 179 */ 180 public Set<String> getHeaderNames() { 181 Map<String, String> headers = getHeaders(); 182 return headers == null ? null : headers.keySet(); 183 } 184 185 /** 186 * Invokes the server operation when the action has been invoked. 187 */ 188 public void actionPerformed(ActionEvent evt) { 189 URL execURL = (URL)getValue(URL_CACHE); 190 if (execURL == null && !"".equals(getURL())) { 191 try { 192 String url = getURL(); 193 if (url.startsWith("http")) { 194 execURL = new URL(url); 195 } else { 196 } 197 if (execURL == null) { 198 // XXX TODO: send a message 199 return; 200 } else { 201 // Cache this value. 202 putValue(URL_CACHE, execURL); 203 } 204 205 } catch (MalformedURLException ex) { 206 LOG.log(Level.WARNING, "something went wrong...", ex); 207 } 208 } 209 210 try { 211 URLConnection uc = execURL.openConnection(); 212 213 // Get all the header name/value pairs ans set the request headers 214 Set<String> headerNames = getHeaderNames(); 215 if (headerNames != null && !headerNames.isEmpty()) { 216 Iterator<String> iter = headerNames.iterator(); 217 while (iter.hasNext()) { 218 String name = (String)iter.next(); 219 uc.setRequestProperty(name, getHeaderValue(name)); 220 } 221 } 222 uc.setUseCaches(false); 223 uc.setDoOutput(true); 224 225 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(512); 226 PrintWriter out = new PrintWriter(byteStream, true); 227 out.print(getPostData()); 228 out.flush(); 229 230 // POST requests must have a content-length. 231 String length = String.valueOf(byteStream.size()); 232 uc.setRequestProperty("Content-length", length); 233 234 // Write POST data to real output stream. 235 byteStream.writeTo(uc.getOutputStream()); 236 237 BufferedReader buf = null; 238 if (uc instanceof HttpURLConnection) { 239 HttpURLConnection huc = (HttpURLConnection)uc; 240 int code = huc.getResponseCode(); 241 String message = huc.getResponseMessage(); 242 243 // Handle the result. 244 if (code < 400) { 245 // action succeeded send to status bar 246 // XXX TODO: setStatusMessage(createMessage(code, message)); 247 // Format the response 248 // TODO: This should load asychnonously 249 buf = new BufferedReader(new InputStreamReader(uc.getInputStream())); 250 251 } else { 252 // action has failed show dialog 253 // XXX TODO: setStatusMessage(createMessage(code, message)); 254 buf = new BufferedReader(new InputStreamReader(huc.getErrorStream())); 255 } 256 String line; 257 258 StringBuffer buffer = new StringBuffer(); 259 while ((line = buf.readLine()) != null) { 260 // RG: Fix for J2SE 5.0; Can't cascade append() calls because 261 // return type in StringBuffer and AbstractStringBuilder are different 262 buffer.append(line); 263 buffer.append('\n'); 264 } 265 // JW: this used the Debug - maybe use finest level? 266 LOG.finer("returned from connection\n" + buffer.toString()); 267 } 268 } catch (UnknownHostException ex) { 269 LOG.log(Level.WARNING, "UnknownHostException detected. Could it be a proxy issue?", ex); 270 271 } catch (AccessControlException ex) { 272 LOG.log(Level.WARNING, "AccessControlException detected", ex); 273 } catch (IOException ex) { 274 LOG.log(Level.WARNING, "IOException detected", ex); 275 } 276 } 277 278 /** 279 * Retrieves a string which represents the parameter data for a server action. 280 * @return a string of name value pairs prefixed by a '?' and delimited by an '&' 281 */ 282 private String getPostData() { 283 // Write the data into local buffer 284 StringBuffer postData = new StringBuffer(); 285 286 // TODO: the action should be configured to retrieve the data. 287 288 // Get all the param name/value pairs and build the data string 289 Set<String> paramNames = getParamNames(); 290 if (paramNames != null && !paramNames.isEmpty()) { 291 Iterator<String> iter = paramNames.iterator(); 292 try { 293 while (iter.hasNext()) { 294 String name = iter.next(); 295 postData.append('&').append(name).append('='); 296 postData.append(getParamValue(name)); 297 } 298 } 299 catch (Exception ex) { // RG: append(char) throws IOException in J2SE 5.0 300 /** @todo Log it */ 301 } 302 // Replace the first & with a ? 303 postData.setCharAt(0, '?'); 304 } 305 306 LOG.finer("ServerAction: POST data: " + postData.toString()); 307 return postData.toString(); 308 } 309 310 311 /** 312 * Creates a human readable message from the server code and message result. 313 * @param code an http error code. 314 * @param msg server message 315 */ 316 private String createMessage(int code, String msg) { 317 StringBuffer buffer = new StringBuffer("The action \""); 318 buffer.append(getValue(NAME)); 319 320 if (code < 400) { 321 buffer.append("\" has succeeded "); 322 } else { 323 buffer.append("\" has failed\nPlease check the Java console for more details.\n"); 324 } 325 // RG: Fix for J2SE 5.0; Can't cascade append() calls because 326 // return type in StringBuffer and AbstractStringBuilder are different 327 buffer.append("\nServer response:\nCode: "); 328 buffer.append(code); 329 buffer.append(" Message: "); 330 buffer.append(msg); 331 332 return buffer.toString(); 333 } 334 }