001: /*
002: *
003: * Copyright (c) 2007, Sun Microsystems, Inc.
004: *
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * * Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * * Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * * Neither the name of Sun Microsystems nor the names of its contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032: package example.http;
033:
034: import java.io.*;
035:
036: import java.util.*;
037:
038: import javax.microedition.io.*;
039: import javax.microedition.lcdui.*;
040: import javax.microedition.midlet.*;
041: import javax.microedition.pki.*;
042:
043: /**
044: * An example MIDlet to fetch a page using an HttpConnection.
045: * Refer to the startApp, pauseApp, and destroyApp
046: * methods so see how it handles each requested transition.
047: *
048: * Note: if you run this inside POSE using a multi-homed PC (with more
049: * than one network connections), POSE doesn't know how to resolve
050: * host names not connected to the first network card. To solve this,
051: * add a line like this in your c:/WINNT/system32/drivers/etc/hosts
052: * file:
053: *
054: * XXX.XXX.XXX.XXX www.sun.com
055: * where XXX.XXX.XXX.XXX == IP address
056: */
057: public class HttpTest extends MIDlet implements CommandListener,
058: Runnable {
059: /** User interface command to exit the current application. */
060: private Command exitCommand = new Command("Exit", Command.EXIT, 2);
061:
062: /** User interface command to issue an HTTP GET request. */
063: private Command getCommand = new Command("Get", Command.SCREEN, 1);
064:
065: /** User interface command to issue an HTTP POST request. */
066: private Command postCommand = new Command("Post", Command.SCREEN, 1);
067:
068: /** User interface command to issue an HTTP HEAD request. */
069: private Command headCommand = new Command("Head", Command.SCREEN, 1);
070:
071: /** User interface command to choose a test. */
072: private Command chooseCommand = new Command("Choose",
073: Command.SCREEN, 2);
074:
075: /** User interface command to Add a new location. */
076: private Command addCommand = new Command("Add", Command.SCREEN, 1);
077:
078: /** User interface command to save a new location. */
079: private Command addSaveCommand = new Command("OK", Command.SCREEN,
080: 1);
081:
082: /** User interface command to confirm current operation. */
083: private Command okCommand = new Command("OK", Command.OK, 1);
084:
085: /** User interface command to abort current operation. */
086: private Command cancelCommand = new Command("Cancel",
087: Command.CANCEL, 1);
088:
089: /** The current display object. */
090: private Display display;
091:
092: /** The url to GET from the network. */
093: private String url;
094:
095: /** Array of target locations. */
096: private Vector urls;
097:
098: /** User interface list for selection. */
099: private List list;
100:
101: /** Message area for user entered URL. */
102: private TextBox addTextBox;
103:
104: /** Current command to process. */
105: private Command currentCommand;
106:
107: /** The current command processing thread. */
108: private Thread commandThread;
109:
110: /** Current attempt count. */
111: private int attempt;
112: private TextBox t;
113: private boolean firstTime;
114:
115: /** Initialize the MIDlet with a handle to the current display */
116: public HttpTest() {
117: urls = new Vector();
118: urls
119: .addElement("http://cds.cmsg.sun.com:80/serverscript/serverscript");
120: urls.addElement("http://www.sun.com/");
121: urls.addElement("https://java.com");
122: urls.addElement("-----------------------");
123: urls.addElement("http://localhost:8080/");
124: urls.addElement("-----------------------");
125: urls.addElement("shttp://host/notsupportedprotocol");
126: urls.addElement("http://:8080/missinghost");
127: urls.addElement("http://mal\\formed:axyt/url???");
128: urls.addElement("http://www.sun.com/no/such/page/");
129: urls.addElement("http://www.sun.com:29999/no/such/port/");
130: urls.addElement("http://no.such.site/");
131: urls.addElement("http://www.sun.com/bad_proxy/");
132:
133: url = (String) urls.elementAt(0);
134: display = Display.getDisplay(this );
135: firstTime = true;
136: }
137:
138: /**
139: * Debug output routine.
140: * @param s string to be printed.
141: */
142: static final void DEBUG(String s) {
143: if (true) {
144: System.out.println(s);
145: }
146: }
147:
148: /**
149: * Converts a time to a string containing the corresponding
150: * date.<br />
151: * <b>NOTE:</b> This is here only because the J2ME date class does not
152: * implement toString() in any meaningful way.
153: * <p />
154: * @param time time to be converted
155: * @return a string representation of the time in
156: * the form "dayOfWeek, day mon year hour:min:sec GMT"
157: */
158: private String time2str(long time) {
159: Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
160: c.setTime(new Date(time));
161:
162: return c.toString();
163: }
164:
165: /**
166: * Start creates the thread to do the timing.
167: * It should return immediately to keep the dispatcher
168: * from hanging.
169: */
170: public void startApp() {
171: if (firstTime) {
172: // Use the specified URL is overriden in the descriptor
173: String u = getAppProperty("HttpTest-Url");
174:
175: if (u != null) {
176: url = u;
177: }
178:
179: mainScreen();
180: firstTime = false;
181: } else {
182: display.setCurrent(t);
183: }
184: }
185:
186: /**
187: * Display the main screen.
188: */
189: void mainScreen() {
190: String s = "URL = " + url
191: + ". Press Get or Post to fetch it, or Choose to "
192: + "use another URL";
193: t = new TextBox("Http Test", s, s.length(), 0);
194: setCommands(t, false);
195: display.setCurrent(t);
196: }
197:
198: /**
199: * Pick a screen.
200: */
201: void chooseScreen() {
202: list = new List("Choose URL", Choice.EXCLUSIVE);
203:
204: for (int i = 0; i < urls.size(); i++) {
205: list.append((String) urls.elementAt(i), null);
206: }
207:
208: setCommands(list, true);
209: display.setCurrent(list);
210: }
211:
212: /**
213: * Add another screen.
214: */
215: void addScreen() {
216: addTextBox = new TextBox("New URL", "http://", 200, 0);
217: addTextBox.addCommand(addSaveCommand);
218: addTextBox.addCommand(cancelCommand);
219: addTextBox.setCommandListener(this );
220: display.setCurrent(addTextBox);
221: }
222:
223: /**
224: * Read the content of the page. Don't care about the response
225: * headers.
226: * @param request type of HTTP request (GET or POST)
227: */
228: private void readContents(String request) {
229: StringBuffer b = new StringBuffer();
230: ++attempt;
231: b.append("attempt " + attempt + " content of " + request + " "
232: + url + "\n");
233:
234: HttpConnection c = null;
235: OutputStream os = null;
236: InputStream is = null;
237: TextBox t = null;
238:
239: try {
240: long len = -1;
241: int ch = 0;
242: long count = 0;
243: int rc;
244:
245: DEBUG(request + " Page: " + url);
246: c = (HttpConnection) Connector.open(url);
247: DEBUG("c= " + c);
248:
249: c.setRequestMethod(request);
250:
251: c.setRequestProperty("foldedField",
252: "first line\r\n second line\r\n third line");
253:
254: if (request == HttpConnection.POST) {
255: String m = "Test POST text.";
256: DEBUG("Posting: " + m);
257: os = c.openOutputStream();
258: os.write(m.getBytes());
259: os.close();
260: }
261:
262: rc = c.getResponseCode();
263:
264: if (rc != HttpConnection.HTTP_OK) {
265: b
266: .append("Response Code: " + c.getResponseCode()
267: + "\n");
268: b.append("Response Message: " + c.getResponseMessage()
269: + "\n\n");
270: }
271:
272: is = c.openInputStream();
273:
274: DEBUG("is = " + is);
275:
276: if (c instanceof HttpConnection) {
277: len = ((HttpConnection) c).getLength();
278: }
279:
280: DEBUG("len = " + len);
281:
282: if (len != -1) {
283: // Read exactly Content-Length bytes
284: DEBUG("Content-Length: " + len);
285:
286: for (int i = 0; i < len; i++) {
287: if ((ch = is.read()) != -1) {
288: if (ch <= ' ') {
289: ch = ' ';
290: }
291:
292: b.append((char) ch);
293: count++;
294:
295: if (count > 200) {
296: break;
297: }
298: }
299: }
300: } else {
301: byte[] data = new byte[100];
302: int n = is.read(data, 0, data.length);
303:
304: for (int i = 0; i < n; i++) {
305: ch = data[i] & 0x000000ff;
306: b.append((char) ch);
307: }
308: }
309:
310: try {
311: if (is != null) {
312: is.close();
313: }
314:
315: if (c != null) {
316: c.close();
317: }
318: } catch (Exception ce) {
319: DEBUG("Error closing connection");
320: }
321:
322: try {
323: len = is.available();
324: DEBUG("Inputstream failed to throw IOException after close");
325: } catch (IOException io) {
326: DEBUG("expected IOException (available())");
327: io.printStackTrace();
328:
329: // Test to make sure available() is only valid while
330: // the connection is still open.,
331: }
332:
333: t = new TextBox("Http Test", b.toString(), b.length(), 0);
334: is = null;
335: c = null;
336: } catch (IOException ex) {
337: ex.printStackTrace();
338: DEBUG(ex.getClass().toString());
339: DEBUG(ex.toString());
340: DEBUG("Exception reading from http");
341:
342: if (c != null) {
343: try {
344: String s = null;
345:
346: if (c instanceof HttpConnection) {
347: s = ((HttpConnection) c).getResponseMessage();
348: }
349:
350: DEBUG(s);
351:
352: if (s == null) {
353: s = "No Response message";
354: }
355:
356: t = new TextBox("Http Error", s, s.length(), 0);
357: } catch (IOException e) {
358: e.printStackTrace();
359:
360: String s = e.toString();
361: DEBUG(s);
362:
363: if (s == null) {
364: s = ex.getClass().getName();
365: }
366:
367: t = new TextBox("Http Error", s, s.length(), 0);
368: }
369:
370: try {
371: c.close();
372: } catch (IOException ioe) {
373: // do not over throw current exception
374: }
375: } else {
376: t = new TextBox("Http Error", "Could not open URL",
377: 128, 0);
378: }
379: } catch (IllegalArgumentException ille) {
380: // Check if an invalid proxy web server was detected.
381: t = new TextBox("Illegal Argument", ille.getMessage(), 128,
382: 0);
383: } catch (Exception e) {
384: t = new TextBox("Error", e.toString(), 128, 0);
385: }
386:
387: if (is != null) {
388: try {
389: is.close();
390: } catch (Exception ce) {
391: ;
392: }
393: }
394:
395: if (c != null) {
396: try {
397: c.close();
398: } catch (Exception ce) {
399: ;
400: }
401: }
402:
403: setCommands(t, false);
404: display.setCurrent(t);
405: }
406:
407: /**
408: * Read the header of an HTTP connection. Don't care about
409: * the actual data.
410: *
411: * All response header fields are displayed in a TextBox screen.
412: * @param request type of HTTP request (GET or POST)
413: */
414: private void readHeaders(String request) {
415: HttpConnection c;
416: TextBox t;
417: StringBuffer b;
418:
419: try {
420: try {
421: c = (HttpConnection) Connector.open(url);
422: } catch (IllegalArgumentException e) {
423: String m = e.getMessage();
424: t = new TextBox("Illegal argument", e.getMessage(),
425: 128, 0);
426: setCommands(t, false);
427: display.setCurrent(t);
428:
429: return;
430: } catch (ConnectionNotFoundException e) {
431: t = new TextBox("Error", "Protocol not supported", 128,
432: 0);
433: setCommands(t, false);
434: display.setCurrent(t);
435:
436: return;
437: } catch (Exception e) {
438: t = new TextBox("Error", e.toString(), 128, 0);
439: setCommands(t, false);
440: display.setCurrent(t);
441:
442: return;
443: }
444:
445: try {
446: c.setRequestMethod(request);
447:
448: b = new StringBuffer();
449: b.append("URL: ");
450: b.append(c.getURL());
451: b.append("\nProtocol: ");
452: b.append(c.getProtocol());
453: b.append("\nHost: " + c.getHost());
454: b.append("\nFile: " + c.getFile());
455: b.append("\nRef: " + c.getRef());
456: b.append("\nQuery: ");
457: b.append(c.getQuery());
458: b.append("\nPort: ");
459: b.append(c.getPort());
460: b.append("\nMethod: ");
461: b.append(c.getRequestMethod());
462:
463: if (c instanceof HttpsConnection) {
464: // getSecurityInfo should connect
465: SecurityInfo sslInfo = ((HttpsConnection) c)
466: .getSecurityInfo();
467: Certificate cert = sslInfo.getServerCertificate();
468:
469: b.append("\nSecure protocol: ");
470: b.append(sslInfo.getProtocolName());
471: b.append("\nSecure protocol version: ");
472: b.append(sslInfo.getProtocolVersion());
473: b.append("\nCipher suite: ");
474: b.append(sslInfo.getCipherSuite());
475:
476: if (cert == null) {
477: b.append("\nNo server Certificate.");
478: } else {
479: b.append("\nServer certificate \n\t Type: ");
480: b.append(cert.getType());
481: b.append("\n\t Version: ");
482: b.append(cert.getVersion());
483: b.append("\n\t Serial number: ");
484: b.append(cert.getSerialNumber());
485: b.append("\n\t Issuer: ");
486: b.append(cert.getIssuer());
487: b.append("\n\t Subject: ");
488: b.append(cert.getSubject());
489: b.append("\n\t Signature algorithm: ");
490: b.append(cert.getSigAlgName());
491: b.append("\n\t Not valid before: ");
492: b.append(time2str(cert.getNotBefore()));
493: b.append("\n\t Not valid after: ");
494: b.append(time2str(cert.getNotAfter()));
495: }
496: }
497:
498: // if not connected getResponseCode should connect
499: b.append("\nResponseCode: ");
500: b.append(c.getResponseCode());
501: b.append("\nResponseMessage:");
502: b.append(c.getResponseMessage());
503: b.append("\nContentLength: ");
504: b.append(c.getLength());
505: b.append("\nContentType: ");
506: b.append(c.getType());
507: b.append("\nContentEncoding: ");
508: b.append(c.getEncoding());
509: b.append("\nContentExpiration: ");
510: b.append(c.getExpiration());
511: b.append("\nDate: ");
512: b.append(c.getDate());
513: b.append("\nLast-Modified: ");
514: b.append(c.getLastModified());
515: b.append("\n\n");
516:
517: int h = 0;
518:
519: while (true) {
520: try {
521: String key = c.getHeaderFieldKey(h);
522:
523: if (key == null) {
524: break;
525: }
526:
527: String value = c.getHeaderField(h);
528: b.append(key);
529: b.append(": ");
530: b.append(value);
531: b.append("\n");
532: h++;
533:
534: } catch (Exception e) {
535: // "Exception while fetching headers");
536: break;
537: }
538: }
539:
540: t = new TextBox("Http Test", b.toString(), b.length(),
541: 0);
542: setCommands(t, false);
543: display.setCurrent(t);
544: } finally {
545: c.close();
546: }
547: } catch (ConnectionNotFoundException e) {
548: t = new TextBox("Error", "Could not Connect.", 128, 0);
549: setCommands(t, false);
550: display.setCurrent(t);
551:
552: return;
553: } catch (CertificateException e) {
554: StringBuffer m = new StringBuffer(256);
555: String s;
556: Certificate cert = e.getCertificate();
557:
558: m.append(e.getMessage());
559:
560: if (cert != null) {
561: m.append("\nServer certificate \n\t Type: ");
562: m.append(cert.getType());
563: m.append("\n\t Version: ");
564: m.append(cert.getVersion());
565: m.append("\n\t Serial number: ");
566: m.append(cert.getSerialNumber());
567: m.append("\n\t Issuer: ");
568: m.append(cert.getIssuer());
569: m.append("\n\t Subject: ");
570: m.append(cert.getSubject());
571: m.append("\n\t Signature algorithm: ");
572: m.append(cert.getSigAlgName());
573: m.append("\n\t Not valid before: ");
574: m.append(time2str(cert.getNotBefore()));
575: m.append("\n\t Not valid after: ");
576: m.append(time2str(cert.getNotAfter()));
577: }
578:
579: s = m.toString();
580: t = new TextBox("Certificate Error", s, s.length(), 0);
581: setCommands(t, false);
582: display.setCurrent(t);
583:
584: return;
585: } catch (IOException ex) {
586: ex.printStackTrace();
587: }
588: }
589:
590: /**
591: * Set the funtion to perform based on commands selected.
592: * @param d Displayable object
593: * @param islist flag to indicate list processing
594: */
595: void setCommands(Displayable d, boolean islist) {
596: if (islist) {
597: d.addCommand(addCommand);
598: d.addCommand(okCommand);
599: } else {
600: d.addCommand(exitCommand);
601: d.addCommand(chooseCommand);
602: d.addCommand(getCommand);
603: d.addCommand(postCommand);
604: d.addCommand(headCommand);
605: }
606:
607: d.setCommandListener(this );
608: }
609:
610: /**
611: * Pause signals the thread to stop by clearing the thread field.
612: * If stopped before done with the iterations it will
613: * be restarted from scratch later.
614: */
615: public void pauseApp() {
616: }
617:
618: /**
619: * Destroy must cleanup everything. The thread is signaled
620: * to stop and no result is produced.
621: * @param unconditional Flag to indicate that forced shutdown
622: * is requested
623: */
624: public void destroyApp(boolean unconditional) {
625: }
626:
627: /**
628: * Respond to commands, including exit
629: * @param c command to perform
630: * @param s Screen displayable object
631: */
632: public void commandAction(Command c, Displayable s) {
633: synchronized (this ) {
634: if (commandThread != null) {
635: // process only one command at a time
636: return;
637: }
638:
639: currentCommand = c;
640: commandThread = new Thread(this );
641: commandThread.start();
642: }
643: }
644:
645: /**
646: * Perform the current command set by the method commandAction.
647: */
648: public void run() {
649: if (currentCommand == exitCommand) {
650: destroyApp(false);
651: notifyDestroyed();
652: } else if (currentCommand == getCommand) {
653: readContents(HttpConnection.GET);
654: } else if (currentCommand == postCommand) {
655: readContents(HttpConnection.POST);
656: } else if (currentCommand == headCommand) {
657: readHeaders(HttpConnection.HEAD);
658: } else if (currentCommand == chooseCommand) {
659: chooseScreen();
660: } else if (currentCommand == okCommand) {
661: int i = list.getSelectedIndex();
662:
663: if (i >= 0) {
664: url = list.getString(i);
665: }
666:
667: mainScreen();
668: } else if (currentCommand == addSaveCommand) {
669: urls.addElement(addTextBox.getString().trim());
670: chooseScreen();
671: } else if (currentCommand == addCommand) {
672: addScreen();
673: } else if (currentCommand == cancelCommand) {
674: chooseScreen();
675: }
676:
677: synchronized (this ) {
678: // signal that another command can be processed
679: commandThread = null;
680: }
681: }
682: }
|