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 }