001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.io.j2me.mms;
028:
029: import com.sun.midp.io.j2me.ProtocolBase;
030:
031: import com.sun.midp.io.j2me.sms.MessageObject;
032:
033: import com.sun.midp.security.Permissions;
034: import java.io.DataInputStream;
035: import java.io.DataOutputStream;
036: import java.io.InputStream;
037: import java.io.OutputStream;
038: import java.util.Vector;
039: import javax.microedition.io.Connector;
040: import javax.microedition.io.Connection;
041: import javax.wireless.messaging.Message;
042: import javax.wireless.messaging.MessageConnection;
043:
044: // Exceptions
045: import java.io.IOException;
046: import java.io.InterruptedIOException;
047:
048: /**
049: * MMS message connection implementation.
050: *
051: * <code>Protocol</code> itself is not instantiated. Instead, the application
052: * calls <code>Connector.open</code> with an MMS URL string and obtains a
053: * {@link javax.wireless.messaging.MessageConnection MessageConnection}
054: * object. It is an instance of <code>MessageConnection</code> that is
055: * instantiated. The Generic Connection Framework mechanism in CLDC will return
056: * a <code>Protocol</code> object, which is the implementation of
057: * <code>MessageConnection</code>. The <code>Protocol</code> object represents a
058: * connection to a low-level transport mechanism.
059: * <p>
060: * Optional packages, such as <code>Protocol</code>, cannot reside in small
061: * devices. The Generic Connection Framework allows an application to reach the
062: * optional packages and classes indirectly. For example, an application can be
063: * written with a string that is used to open a connection. Inside the
064: * implementation of <code>Connector</code>, the string is mapped to a
065: * particular implementation: <code>Protocol</code>, in this case. This allows
066: * the implementation to be optional even though the interface,
067: * <code>MessageConnection</code>, is required.
068: * <p>
069: * Closing the connection frees an instance of <code>MessageConnection</code>.
070: * <p>
071: * The <code>Protocol</code> class contains methods to open and close the
072: * connection to the low-level transport mechanism. The messages passed on the
073: * transport mechanism are defined by the {@link MessageObject MessageObject}
074: * class. Connections can be made in either client mode or server mode.
075: * <ul>
076: * <li>Client mode connections are for sending messages only. They are created
077: * by passing a string identifying a destination address to the
078: * <code>Connector.open()</code> method.</li>
079: * <li>Server mode connections are for receiving and sending messages. They are
080: * created by passing a string that identifies a port, or equivalent, on the
081: * local host to the <code>Connector.open()</code> method.</li>
082: * </ul>
083: * The class also contains methods to send, receive, and construct
084: * <code>Message</code> objects.
085: * <p>
086: * <p>
087: * This class declares that it implements <code>StreamConnection</code> so it
088: * can intercept calls to <code>Connector.open*Stream()</code> to throw an
089: * <code>IllegalArgumentException</code>.
090: * </p>
091: */
092: public class Protocol extends ProtocolBase {
093:
094: /** Creates a message connection protocol handler. */
095: public Protocol() {
096: super ();
097: ADDRESS_PREFIX = "mms://";
098: }
099:
100: /**
101: * Gets the connection parameter in string mode.
102: * @return string that contains a parameter
103: */
104: protected String getAppID() {
105: return appID;
106: }
107:
108: /**
109: * Sets the connection parameter in string mode.
110: * @param newValue new value of connection parameter
111: */
112: protected void setAppID(String newValue) {
113: appID = newValue;
114: }
115:
116: /**
117: * The internal representation of the MMS message.
118: */
119: private class MMSPacket {
120: /** The sender's address. */
121: public byte[] fromAddress;
122: /** The application ID associated with the message. */
123: public byte[] appID;
124: /** The application ID to which replies will be sent. */
125: public byte[] replyToAppID;
126: /** Entire message contents. */
127: public byte[] message;
128: };
129:
130: /*
131: * Native function prototypes
132: */
133:
134: /**
135: * Native function to open a MMS connection.
136: *
137: * @param host The name of the host for this connection. Can be
138: * <code>null</code>.
139: * @param appID The application ID associated with this connection.
140: * Can be <code>null</code> for unblock sending and receiving messages.
141: * @param msid The MIDlet suite ID.
142: *
143: * @return returns handle to the open MMS connection.
144: */
145: private native int open0(String host, String appID, int msid)
146: throws IOException;
147:
148: /**
149: * Unblock the receive thread.
150: *
151: * @param msid The MIDlet suite ID.
152: *
153: * @return returns handle to the connection.
154: */
155: protected int unblock00(int msid) throws IOException {
156: return open0(null, null, msid);
157: }
158:
159: /**
160: * Native function to close mms connection
161: *
162: * @param appID The application ID associated with this connection.
163: * @param handle The MMS handle created when the connection was opened.
164: * @param deRegister Deregistration appID when parameter is 1.
165: *
166: * @return <code>0</code> if successful; <code>-1</code> for failure.
167: */
168: private native int close0(String appID, int handle, int deRegister);
169:
170: /**
171: * Close connection.
172: *
173: * @param connHandle handle returned by open0
174: * @param deRegister Deregistration appID when parameter is 1.
175: * @return 0 on success, -1 on failure
176: */
177: protected int close00(int connHandle, int deRegister) {
178: return close0(appID, connHandle, deRegister);
179: }
180:
181: /**
182: * Native function to get the device phone number
183: *
184: * @return the phone number of device.
185: */
186: private native String getPhoneNumber0();
187:
188: /**
189: * Sends an MMS message.
190: *
191: * @param handle The handle to the open MMS connection.
192: * @param toAddress The recipient's MMS address.
193: * @param fromAddress The sender's MMS address.
194: * @param appID The application ID to be matched against incoming messages.
195: * @param replyToAppID The ID of the application that processes replies.
196: * @param msgHeader The message header context.
197: * @param msgBody The message body context.
198: *
199: * @return the status of <code>0</code>, when bytes were sent;
200: * <code><0</code> when there is an error (This is accompanied by
201: * an exception.).
202: */
203: private native int send0(int handle, String toAddress,
204: String fromAddress, String appID, String replyToAppID,
205: byte[] msgHeader, byte[] msgBody) throws IOException;
206:
207: /**
208: * Receives a MMS message.
209: *
210: * @param handle The handle to the the MMS connection.
211: * @param appID The application ID to be matched against incoming messages.
212: * @param msid The MIDlet suite ID.
213: * @param packet The received message.
214: *
215: * @return The number of bytes received.
216: *
217: * @exception IOException if an I/O error occurs
218: */
219: private native int receive0(int handle, String appID, int msid,
220: MMSPacket packet) throws IOException;
221:
222: /**
223: * Waits until message available
224: *
225: * @param appID The application ID associated with this connection.
226: * @param handle The handle to the MMS connection.
227: *
228: * @return <code>0</code> on success, <code>-1</code> on failure
229: *
230: * @exception IOException if an I/O error occurs
231: */
232: private native int waitUntilMessageAvailable0(String appID,
233: int handle) throws IOException;
234:
235: /**
236: * Waits until message available
237: *
238: * @param handle handle to connection
239: * @return 0 on success, -1 on failure
240: * @exception IOException if an I/O error occurs
241: */
242: protected int waitUntilMessageAvailable00(int handle)
243: throws IOException {
244: return waitUntilMessageAvailable0(appID, handle);
245: }
246:
247: /**
248: * Computes the number of transport-layer segments that would be required to
249: * send the given message.
250: *
251: * @param msgBuffer The message to be sent.
252: * @param msgLen The length of the message.
253: * @param msgType The message type: binary or text.
254: * @param hasPort Indicates if the message includes a source or destination
255: * port number.
256: *
257: * @return The number of transport-layer segments required to send the
258: * message.
259: */
260: private native int numberOfSegments0(byte msgBuffer[], int msgLen,
261: int msgType, boolean hasPort);
262:
263: /*
264: * Helper methods
265: */
266:
267: /**
268: * Checks the internal setting of the receive permission. Called from
269: * the <code>receive</code> and <code>setMessageListener</code> methods.
270: *
271: * @exception InterruptedIOException if permission dialog was pre-empted.
272: */
273: protected void checkReceivePermission()
274: throws InterruptedIOException {
275:
276: // Check for permission to receive.
277: if (readPermission == false) {
278: try {
279: midletSuite.checkForPermission(Permissions.MMS_RECEIVE,
280: "mms:receive");
281: readPermission = true;
282: } catch (InterruptedException ie) {
283: throw new InterruptedIOException(
284: "Interrupted while trying "
285: + "to ask the user permission.");
286: }
287: }
288: }
289:
290: /*
291: * MessageConnection Interface
292: */
293:
294: /**
295: * Construct a new message object of a text, binary or multipart message
296: * type and specify a destination address. When a
297: * <code>MULTIPART_MESSAGE</code> constant is passed in, the created object
298: * implements the <code>MultipartMessage</code> interface.
299: * <p>
300: * If this method is called in a sending mode, a new <code>Message</code>
301: * object is requested from the connection. For example:
302: * <p>
303: * <code>Message msg = conn.newMessage(MULTIPART_MESSAGE);</code>
304: * <p>
305: * The <code>Message</code> does not have the destination address set. It
306: * must be set by the application before the message is sent.
307: * <p>
308: * If this method is called in receiving mode, the <code>Message</code>
309: * object does have its address set. The application can act on the object
310: * to extract the address and message data.
311: * <p>
312: * <!-- The <code>type</code> parameter indicates the number of bytes that
313: * should be allocated for the message. No restrictions are placed on the
314: * application for the value of <code>size</code>. A value of
315: * <code>null</code> is permitted and creates a <code>Message</code> object
316: * with a 0-length message. -->
317: *
318: * @param type <code>MULTIPART_MESSAGE</code> is the only type permitted.
319: *
320: * @return A new MMS <code>Message</code> object.
321: */
322: public Message newMessage(String type) {
323:
324: String address = null;
325:
326: // Provide the default address from the original open.
327: if (host != null) {
328: address = ADDRESS_PREFIX + host;
329: if (appID != null) {
330: address = address + ":" + appID;
331: }
332: }
333:
334: return newMessage(type, address);
335: }
336:
337: /**
338: * Construct a new <code>MULTIPART_MESSAGE</code> message object and specify
339: * a destination address. The <code>MULTIPART_MESSAGE</code> constant must
340: * be passed in. The created object implements the
341: * <code>MultipartMessage</code> interface.
342: * <p>
343: * The destination address <code>addr</code> has the following format:
344: * <p>
345: * <code>mms://<em>phone_number</em>:<em>application_id</em></code>
346: *
347: * @param type <code>MULTIPART_MESSAGE</code> is the only type permitted.
348: * @param addr The destination address of the message.
349: *
350: * @return A new MMS <code>Message</code> object.
351: */
352: public Message newMessage(String type, String addr) {
353:
354: if (!type.equals(MessageConnection.MULTIPART_MESSAGE)) {
355: throw new IllegalArgumentException(
356: "Message type not supported.");
357: }
358:
359: return new MultipartObject(addr);
360: }
361:
362: /**
363: * Send an MMS message over the connection. The data are extracted from the
364: * <code>Message</code> object payload for use by the underlying transport
365: * layer.
366: *
367: * @param msg A <code>Message</code> object.
368: *
369: * @exception java.io.IOException if the message could not be sent
370: * or because of network failure.
371: * @exception java.lang.IllegalArgumentException if the message is
372: * incomplete or contains invalid information. This exception
373: * is also thrown if the payload of the message exceeds
374: * the maximum length for the given messaging protocol.
375: * @exception java.io.InterruptedIOException if a timeout occurs while
376: * either trying to send the message or if this
377: * <code>Connection</code> object is closed during this
378: * <code>send</code> operation.
379: * @exception java.lang.NullPointerException if the parameter is null.
380: * @exception java.lang.SecurityException if the application does not
381: * have permission to send the message.
382: */
383: public void send(Message msg) throws IOException {
384:
385: if (msg == null) {
386: throw new NullPointerException("Null message");
387: }
388:
389: // If this isn't a multipart message, bail out.
390: if (!(msg instanceof MultipartObject)) {
391: throw new IllegalArgumentException(
392: "Unsupported message type");
393: }
394:
395: // Make sure the connection is still open.
396: ensureOpen();
397:
398: // Create the multi-part object that will be used below.
399: MultipartObject mpo = (MultipartObject) msg;
400:
401: /*
402: * Check for valid MMS URL connection format. Note that the addresses in
403: * the lists are not used. This is simply a check to make sure that the
404: * addresses can be placed into the multipart object's header when the
405: * header and message are bundled within MultipartObject.
406: *
407: * Process each MMS address in the to:, cc: and bcc: address lists. An
408: * MMS address assumes this form: address:appID
409: *
410: * Each MMS address is parsed to extract the address and application ID
411: * data. Those parts are then checked for validity.
412: *
413: * The loop starts by processing all addresses in the to: field (if
414: * any), followed by the addresses in the cc: list, then the bcc: list.
415: *
416: */
417: Vector allAddresses = new Vector();
418: String[] addresses = mpo.getAddresses("to");
419: int currIndex = 0;
420: boolean checkedTo = false;
421: boolean checkedCC = false;
422:
423: // The application ID extracted from an address in the address list.
424: String parsedAppID = null;
425: while (true) {
426:
427: /*
428: * If no addresses were in the to: field, or if all addresses have
429: * been extracted and checked from the current address list
430: * (Initially, the to: list), then continue to process the cc: list
431: * (if any), next, followed by the bcc: list.
432: */
433: if (addresses == null || currIndex >= addresses.length) {
434:
435: if (!checkedTo) {
436:
437: // The to: list has been processed. Process cc: list, next.
438: checkedTo = true;
439: addresses = mpo.getAddresses("cc");
440: currIndex = 0;
441: continue;
442:
443: } else if (!checkedCC) {
444:
445: // The cc: list has been processed. Process bcc: list, next.
446: checkedCC = true;
447: addresses = mpo.getAddresses("bcc");
448: currIndex = 0;
449: continue;
450: } else {
451:
452: /*
453: * The to:, cc: and bcc: lists have now been checked, so
454: * bail out of the while() loop.
455: */
456: break;
457: }
458: }
459:
460: /*
461: * Pick up the next address and add it to the list. Then, parse it
462: * to extract the address and application ID parts.
463: */
464: String addr = addresses[currIndex++];
465: allAddresses.addElement(addr);
466:
467: MMSAddress parsedAddress = MMSAddress
468: .getParsedMMSAddress(addr);
469:
470: if (parsedAddress == null
471: || parsedAddress.type == MMSAddress.INVALID_ADDRESS
472: || parsedAddress.type == MMSAddress.APP_ID) {
473: throw new IllegalArgumentException(
474: "Invalid MMS address: " + addr);
475: }
476:
477: if (parsedAppID == null) {
478: parsedAppID = parsedAddress.appId;
479: } else if (parsedAddress.appId != null
480: && !parsedAppID.equals(parsedAddress.appId)) {
481: throw new IllegalArgumentException(
482: "Only one Application-ID "
483: + "can be specified per message");
484: }
485:
486: } // while
487:
488: if (allAddresses.size() == 0) {
489: throw new IllegalArgumentException(
490: "No to, cc, or bcc addresses.");
491: }
492:
493: /*
494: * Since JTWI requires the destination phone number (not port)
495: * and the number of messages to be displayed in the permission
496: * dialog to the user, the permission check must happen after the
497: * address check and message disassembly.
498: */
499: try {
500: /*
501: * IMPL_NOTE: (Original comment for MMS): Add the display of the addresses
502: * in a nice way, and the size of all attachments and total size, as
503: * per page 66 (Appendix E) of the specification.
504: *
505: * Nicer display can happen by creating an MMSAddressList class that
506: * extends Vector and overrides toString().
507: *
508: */
509: midletSuite.checkForPermission(Permissions.MMS_SEND,
510: allAddresses.toString(), Integer
511: .toString(numberOfSegments(msg)));
512: } catch (InterruptedException ie) {
513: throw new InterruptedIOException(
514: "Interrupted while trying to ask the user permission");
515: }
516:
517: // Construct the target address protocol string.
518: String messageAppID = mpo.getApplicationID();
519: String toAddress = "mms://:";
520: if (messageAppID != null) {
521: toAddress = toAddress + messageAppID;
522: }
523:
524: /*
525: * If no application ID was supplied, use the ID that was used to open
526: * the connection as the default ID.
527: */
528: String replyToAppID = null;
529: if (messageAppID != null && host == null) {
530: replyToAppID = appID;
531: mpo.setReplyToApplicationID(replyToAppID);
532: }
533:
534: // Retain the original "from:" address.
535: String oldFromAddress = ((MessageObject) mpo).getAddress();
536:
537: // Create the "reply-to" information.
538: String phoneNumber = getPhoneNumber0();
539: String fromAddress = "mms://" + phoneNumber;
540: if (replyToAppID != null) {
541: fromAddress = fromAddress + ":" + replyToAppID;
542: }
543: mpo.setFromAddress(fromAddress);
544:
545: // Send the message and reply information.
546: byte[] header = mpo.getHeaderAsByteArray();
547: byte[] body = mpo.getBodyAsByteArray();
548: try {
549: send0(connHandle, toAddress, fromAddress, messageAppID,
550: replyToAppID, header, body);
551: } catch (IOException ex) {
552: io2InterruptedIOExc(ex, "sending");
553: }
554:
555: // Reset the from: address to its original form.
556: mpo.setFromAddress(oldFromAddress);
557: }
558:
559: /**
560: * Receives the bytes that have been sent over the connection, constructs a
561: * <code>MultipartMessage</code> object, and returns the message.
562: * <p>
563: * If there are no <code>MultipartMessage</code>s waiting on the connection,
564: * this method will block until a message is received, or the
565: * <code>MessageConnection</code> is closed.
566: *
567: * @return A <code>MultipartMessage</code> object.
568: *
569: * @exception java.io.IOException if an error occurs while receiving a
570: * message.
571: * @exception java.io.InterruptedIOException if this
572: * <code>MessageConnection</code> object is closed during this
573: * receive method call.
574: * @exception java.lang.SecurityException if the application does not have
575: * permission to receive messages using the given application ID.
576: */
577: public synchronized Message receive() throws IOException {
578:
579: if (host != null) {
580: throw new IOException(
581: "Can not receive from client only connection: "
582: + host);
583: }
584:
585: // Check for permission to receive.
586: checkReceivePermission();
587:
588: // Make sure the connection is still open.
589: ensureOpen();
590:
591: // The connection must be read-only with no host address.
592: if (((m_mode & Connector.READ) == 0) || (host != null)) {
593: throw new IOException("Invalid connection mode.");
594: }
595:
596: // No message received yet.
597: Message msg = null;
598: int length = 0;
599:
600: try {
601:
602: MMSPacket mmsPacket = new MMSPacket();
603:
604: /*
605: * Packet has been received and deleted from inbox.
606: * Time to wake up receive thread.
607: */
608: // Pick up the message from the message pool.
609: length = receive0(connHandle, appID, midletSuite.getID(),
610: mmsPacket);
611:
612: if (length == 0) {
613: throw new InterruptedIOException("No message received.");
614: }
615:
616: if (length > 0) {
617:
618: /*
619: * Convert the message data into a multipart message. The
620: * message packet contains the appID that goes with the message
621: * as well as the entire message content in a big byte[] lump.
622: */
623: msg = MultipartObject
624: .createFromByteArray(mmsPacket.message);
625:
626: // IMPL_NOTE: KEEP ?
627: // IMPL_NOTE: msg.setTimeStamp(tm.getTimeStamp());
628:
629: String fromAddress = null;
630: if (mmsPacket.fromAddress != null) {
631: fromAddress = new String(mmsPacket.fromAddress);
632: }
633:
634: String replyToAppID;
635: if (mmsPacket.replyToAppID == null) {
636: replyToAppID = null;
637: } else {
638: replyToAppID = new String(mmsPacket.replyToAppID);
639: }
640:
641: ((MultipartObject) msg).setFromAddress(fromAddress);
642: String phoneNumber = getPhoneNumber0();
643: ((MultipartObject) msg).fixupReceivedMessageAddresses(
644: fromAddress, phoneNumber);
645: }
646:
647: } catch (InterruptedIOException ex) {
648: length = 0; // Avoid the "finally" exception, below.
649: throw new InterruptedIOException("MMS connection closed.");
650: } catch (IOException ex) {
651: io2InterruptedIOExc(ex, "receiving");
652: } finally {
653: if (length < 0) {
654: throw new InterruptedIOException("Connection closed.");
655: }
656: }
657:
658: return msg;
659: }
660:
661: /**
662: * Returns the number of segments required to send the given
663: * <code>Message</code>.
664: * <p>
665: * Note: The message is not actually sent. This method will compute the
666: * number of segments needed when this message is split into the protocol
667: * segments using the appropriate features of the underlying protocol. This
668: * method does not take the possible limitations of the implementation into
669: * account, which may limit the number of segments that can be sent, using
670: * this feature. These limitations are protocol-specific and are documented
671: * with the adapter definition for that protocol.
672: * </p>
673: * @param msg The <code>MultipartObject</code>message to be used for
674: * the computation.
675: *
676: * @return The number of protocol segments needed for sending the message.
677: * Returns <code>0</code> if the <code>Message</code> object cannot be
678: * sent using the underlying protocol.
679: */
680: public int numberOfSegments(Message msg) {
681:
682: if (!(msg instanceof MultipartObject)) {
683: return 0;
684: }
685:
686: /** Number of segments that need to be sent. */
687: int segments = 0;
688:
689: byte[] msgBuffer = null;
690: try {
691: msgBuffer = ((MultipartObject) msg).getAsByteArray();
692: } catch (IOException ioe) {
693: // ignore this.
694: }
695:
696: // Pick up the message length.
697: if (msgBuffer != null) {
698:
699: // There is always a "port" (Application ID).
700: boolean hasPort = true;
701:
702: /* Compute the total number of transport-layer segments. */
703: segments = numberOfSegments0(msgBuffer, msgBuffer.length,
704: 0, hasPort);
705: }
706:
707: return segments;
708: }
709:
710: /**
711: * Closes the connection. Resets the connection <code>open</code> flag to
712: * <code>false</code>. Subsequent operations on a closed connection should
713: * throw an appropriate exception.
714: *
715: * @exception IOException if an I/O error occurs
716: */
717: public void close() throws IOException {
718:
719: /*
720: * Set appID to null, in order to quit out of the while loop
721: * in the receiver thread.
722: */
723: String save_appID = null;
724: if (appID != null) {
725: save_appID = new String(appID);
726: }
727:
728: appID = null;
729:
730: synchronized (closeLock) {
731: if (open) {
732: /*
733: * Reset open flag early to prevent receive0 executed by
734: * concurrent thread to operate on partially closed
735: * connection
736: */
737: open = false; /* Close the connection and unregister the application ID. */
738: close0(save_appID, connHandle, 1);
739:
740: setMessageListener(null);
741:
742: /*
743: * Reset handle and other params to default
744: * values. Multiple calls to close() are allowed
745: * by the spec and the resetting would prevent any
746: * strange behaviour.
747: */
748: connHandle = 0;
749: host = null;
750: m_mode = 0;
751: }
752: }
753: }
754:
755: /*
756: * ConnectionBaseInterface Interface
757: */
758:
759: /**
760: * Opens a connection. This method is called from the
761: * <code>Connector.open()</code> method to obtain the destination address
762: * given in the <code>name</code> parameter.
763: * <p>
764: * The format for the <code>name</code> string for this method is:
765: * <p>
766: * <code>mms://[<em>phone_number</em>]:[<em>application_id</em>]</code>
767: * <p>
768: * where the <em>phone_number</em> is optional. If the <em>phone_number</em>
769: * parameter is present, the connection is being opened in client mode. This
770: * means that messages can be sent. If the parameter is absent, the
771: * connection is being opened in server mode. This means that messages can
772: * be sent and received.
773: * <p>
774: * The connection that is opened is to a low-level transport mechanism which
775: * can be any of the following:
776: * <ul>
777: * <li>A datagram Short Message Peer-to-Peer (SMPP) to a service center.
778: * <li>A <code>comm</code> connection to a phone device with AT-commands.
779: * <li>A native MMS stack.
780: * </ul>
781: * Currently, the <code>mode</code> and <code>timeouts</code> parameters are
782: * ignored.
783: *
784: * @param name The target of the connection.
785: * @param mode Indicates whether the caller intends to write to the
786: * connection. Currently, this parameter is ignored.
787: * @param timeouts Indicates whether the caller wants timeout exceptions.
788: * Currently, this parameter is ignored.
789: *
790: * @return This connection.
791: *
792: * @exception IOException if the connection is closed or unavailable.
793: */
794: public Connection openPrim(String name, int mode, boolean timeouts)
795: throws IOException {
796:
797: return openPrimInternal(name, mode, timeouts);
798: }
799:
800: /*
801: * StreamConnection Interface
802: */
803:
804: /**
805: * Open and return an input stream for a connection. This method always
806: * throws <code>IllegalArgumentException</code>.
807: *
808: * @return An input stream.
809: * @exception IOException if an I/O error occurs.
810: * @exception IllegalArgumentException is thrown for all requests.
811: */
812: public InputStream openInputStream() throws IOException {
813: throw new IllegalArgumentException("Not supported.");
814: }
815:
816: /**
817: * Open and return a data input stream for a connection. This method always
818: * throws <code>IllegalArgumentException</code>.
819: *
820: * @return An input stream.
821: * @exception IOException if an I/O error occurs.
822: * @exception IllegalArgumentException is thrown for all requests.
823: */
824: public DataInputStream openDataInputStream() throws IOException {
825: throw new IllegalArgumentException("Not supported.");
826: }
827:
828: /**
829: * Open and return an output stream for a connection. This method always
830: * throws <code>IllegalArgumentException</code>.
831: *
832: * @return An output stream.
833: * @exception IOException if an I/O error occurs.
834: * @exception IllegalArgumentException is thrown for all requests.
835: */
836: public OutputStream openOutputStream() throws IOException {
837: throw new IllegalArgumentException("Not supported.");
838: }
839:
840: /**
841: * Open and return a data output stream for a connection. This method always
842: * throws <code>IllegalArgumentException</code>.
843: *
844: * @return An output stream.
845: * @exception IOException if an I/O error occurs.
846: * @exception IllegalArgumentException is thrown for all requests.
847: */
848: public DataOutputStream openDataOutputStream() throws IOException {
849: throw new IllegalArgumentException("Not supported.");
850: }
851:
852: /*
853: * Protocol members
854: */
855:
856: /**
857: * Opens a connection. This method is called from the
858: * <code>Connector.open()</code> method to obtain the destination address
859: * given in the <code>name</code> parameter.
860: * <p>
861: * The format for the <code>name</code> string for this method is:
862: * <p>
863: * <code>mms://[<em>phone_number</em>]:[<em>application_id</em>]</code>
864: * <p>
865: * where the <em>phone_number</em> is optional. If the <em>phone_number</em>
866: * parameter is present, the connection is being opened in client mode. This
867: * means that messages can be sent. If the parameter is absent, the
868: * connection is being opened in server mode. This means that messages can
869: * be sent and received.
870: * <p>
871: * The connection that is opened is to a low-level transport mechanism which
872: * can be any of the following:
873: * <ul>
874: * <li>A datagram Short Message Peer-to-Peer (SMPP) to a service center.
875: * <li>A <code>comm</code> connection to a phone device with AT-commands.
876: * <li>A native MMS stack.
877: * </ul>
878: * Currently, the <code>mode</code> and <code>timeouts</code> parameters are
879: * ignored.
880: *
881: * @param name The target of the connection.
882: * @param mode Indicates whether the caller intends to write to the
883: * connection. Currently, this parameter is ignored.
884: * @param timeouts Indicates whether the caller wants timeout exceptions.
885: * Currently, this parameter is ignored.
886: *
887: * @return This connection.
888: *
889: * @exception IOException if the connection is closed or unavailable.
890: * @exception IllegalArgumentException if the parameters are invalid
891: */
892: public synchronized Connection openPrimInternal(String name,
893: int mode, boolean timeouts) throws IOException {
894:
895: /*
896: * The general form of a MMS address is <code>mms://host:port</code>.
897: * The form at this point should now be <code>//host:port</code>
898: */
899: if ((name == null) || (name.length() <= 2)
900: || (name.charAt(0) != '/') || (name.charAt(1) != '/')) {
901:
902: throw new IllegalArgumentException(
903: "Missing protocol separator.");
904: }
905:
906: String fullAddress = "mms:" + name;
907:
908: MMSAddress parsedAddress = MMSAddress
909: .getParsedMMSAddress(fullAddress);
910: if (parsedAddress == null) {
911: throw new IllegalArgumentException(
912: "Invalid MMS connection URL");
913: }
914:
915: host = null;
916: if (parsedAddress.address != null) {
917: host = new String(parsedAddress.address);
918: }
919:
920: appID = null;
921: if (parsedAddress.appId != null) {
922: appID = new String(parsedAddress.appId);
923: }
924:
925: // Make sure the I/O constraint is READ, WRITE or READ_WRITE, only.
926: if ((mode != Connector.READ) && (mode != Connector.WRITE)
927: && (mode != Connector.READ_WRITE)) {
928: throw new IllegalArgumentException("Invalid mode");
929: }
930:
931: /*
932: * If <code>host == null</code>, then this is a server endpoint at
933: * the supplied <code>appId</code>.
934: *
935: * If <code>host != null</code>, then this is a client endpoint at an
936: * application-id decided by the system, and the default address for
937: * MMS messages to be sent is <code>mms://host:appId</code>.
938: */
939: if (mode == Connector.READ && host != null && host.length() > 0) {
940: throw new IllegalArgumentException("Cannot read on "
941: + "a client connection.");
942: }
943:
944: /*
945: * Perform a one-time check to see if the application has the permission
946: * to use this connection type.
947: */
948: if (openPermission == false) {
949: try {
950: midletSuite.checkForPermission(Permissions.MMS_SERVER,
951: "mms:open");
952: openPermission = true;
953: } catch (InterruptedException ie) {
954: throw new InterruptedIOException(
955: "Interrupted while trying "
956: + "to ask the user permission");
957: }
958: }
959:
960: try {
961: connHandle = open0(host, appID, midletSuite.getID());
962: } catch (IOException ioe) {
963: m_mode = 0;
964: throw new IOException("Unable to open MMS connection: "
965: + ioe.getMessage());
966: } catch (OutOfMemoryError oome) {
967: m_mode = 0;
968: throw new IOException("Unable to open MMS connection: "
969: + oome.getMessage());
970: }
971: open = true;
972:
973: m_mode = mode;
974:
975: // Return this connection.
976: return this;
977: }
978:
979: }
|