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;
028:
029: // Interfaces
030: import com.sun.cldc.io.ConnectionBaseInterface;
031: import javax.microedition.io.StreamConnection;
032: import javax.wireless.messaging.MessageConnection;
033:
034: // Classes
035: import com.sun.midp.midlet.MIDletSuite;
036: import com.sun.midp.midlet.Scheduler;
037: import com.sun.midp.security.Permissions;
038: import com.sun.midp.security.SecurityToken;
039: import com.sun.midp.security.ImplicitlyTrustedClass;
040: import com.sun.midp.security.SecurityInitializer;
041:
042: import java.io.DataInputStream;
043: import java.io.DataOutputStream;
044: import java.io.InputStream;
045: import java.io.OutputStream;
046: import javax.microedition.io.Connector;
047: import javax.microedition.io.Connection;
048: import javax.wireless.messaging.Message;
049: import javax.wireless.messaging.MessageListener;
050:
051: // Exceptions
052: import java.io.IOException;
053: import java.io.InterruptedIOException;
054:
055: /**
056: * Base class for SMS/CBS/MMS message connection implementation.
057: *
058: * <code>Protocol</code> itself is not instantiated. Instead, the application
059: * calls <code>Connector.open</code> with an URL string and obtains a
060: * {@link javax.wireless.messaging.MessageConnection MessageConnection}
061: * object. It is an instance of <code>MessageConnection</code>
062: * that is instantiated. The Generic Connection Framework mechanism
063: * in CLDC will return a <code>Protocol</code> object, which is the
064: * implementation of <code>MessageConnection</code>. The
065: * <code>Protocol</code> object represents a connection to a low-level transport
066: * mechanism.
067: * <p>
068: * Optional packages, such as <code>Protocol</code>, cannot reside in
069: * small devices.
070: * The Generic Connection Framework allows an application to reach the
071: * optional packages and classes indirectly. For example, an application
072: * can be written with a string that is used to open a connection. Inside
073: * the implementation of <code>Connector</code>, the string is mapped to a
074: * particular implementation: <code>Protocol</code>, in this case. This allows
075: * the implementation to be optional even though
076: * the interface, <code>MessageConnection</code>, is required.
077: * <p>
078: * Closing the connection frees an instance of <code>MessageConnection</code>.
079: * <p>
080: * The <code>Protocol</code> class contains methods
081: * to open and close the connection to the low-level transport mechanism. The
082: * messages passed on the transport mechanism are defined by the
083: * {@link MessageObject MessageObject}
084: * class.
085: * Connections can be made in either client mode or server mode.
086: * <ul>
087: * <li>Client mode connections are for sending messages only. They are
088: * created by passing a string identifying a destination address to the
089: * <code>Connector.open()</code> method.</li>
090: * <li>Server mode connections are for receiving and sending messages. They
091: * are created by passing a string that identifies a port, or equivalent,
092: * on the local host to the <code>Connector.open()</code> method.</li>
093: * </ul>
094: * The class also contains methods to send, receive, and construct
095: * <code>Message</code> objects.
096: * <p>
097: * <p>
098: * This class declares that it implements <code>StreamConnection</code>
099: * so it can intercept calls to <code>Connector.open*Stream()</code>
100: * to throw an <code>IllegalArgumentException</code>.
101: * </p>
102: *
103: */
104:
105: public abstract class ProtocolBase implements MessageConnection,
106: ConnectionBaseInterface, StreamConnection {
107:
108: /** Prefic for addressed message connections. */
109: protected String ADDRESS_PREFIX;
110:
111: /** Handle to the MIDlet suite containing this MIDlet. */
112: protected MIDletSuite midletSuite;
113:
114: /**
115: * Indicates whether the connection is open or closed. If it is closed,
116: * subsequent operations should throw an exception.
117: */
118: protected boolean open = false;
119:
120: /** Local handle to connection */
121: protected int connHandle = 0;
122:
123: /** Connection parameter from the URL. */
124: protected String appID = null;
125:
126: /** Connector mode. */
127: protected int m_mode = 0;
128:
129: /** Machine name - the parsed target address from the URL. */
130: protected String host = null;
131:
132: /**
133: * Inner class to request security token from SecurityInitializer.
134: * SecurityInitializer should be able to check this inner class name.
135: */
136: private static class SecurityTrusted implements
137: ImplicitlyTrustedClass {
138: };
139:
140: /** This class has a different security domain than the MIDlet suite */
141: private static SecurityToken classSecurityToken = SecurityInitializer
142: .requestToken(new SecurityTrusted());
143:
144: /** Message listener for async notifications. */
145: volatile MessageListener m_listener = null;
146:
147: /** Listener thread. */
148: Thread m_listenerThread = null;
149:
150: /** Used to protect read-modify operation on open field during close() */
151: protected Object closeLock = new Object();
152:
153: /**
154: * Indicates whether a trusted application is allowed to open the
155: * message connection. Set to true if the permission check passes.
156: * Note: return true to override Security Permissions
157: */
158: protected boolean openPermission = false;
159:
160: /**
161: * Indicates whether a trusted application is allowed to read from the
162: * message connection. Set to true if the permission check passes.
163: * Note: return true to override Security Permissions
164: */
165: protected boolean readPermission = false;
166:
167: /**
168: * Indicates whether a trusted application is allowed to write to the
169: * message connection. Set to true if the permission check passes.
170: * Note: return true to override Security Permissions
171: */
172: protected boolean writePermission = false;
173:
174: /** Creates a message connection protocol handler. */
175: public ProtocolBase() {
176: midletSuite = Scheduler.getScheduler().getMIDletSuite();
177: }
178:
179: /**
180: * Construct a new message object from the given type.
181: *
182: * @param type <code>MULTIPART_MESSAGE</code> is the only type permitted.
183: *
184: * @return A new MMS <code>Message</code> object.
185: */
186: public abstract Message newMessage(String type);
187:
188: /**
189: * Constructs a new message object from the given type and address.
190: *
191: * @param type <code>TEXT_MESSAGE</code> or
192: * <code>BINARY_MESSAGE</code>.
193: * @param addr the destination address of the message.
194: * @return a new <code>Message</code> object.
195: */
196: public abstract Message newMessage(String type, String addr);
197:
198: /**
199: * Receives the bytes that have been sent over the connection, constructs a
200: * <code>Message</code> object, and returns it.
201: * <p>
202: * If there are no <code>Message</code>s waiting on the connection, this
203: * method will block until a message is received, or the
204: * <code>MessageConnection</code> is closed.
205: *
206: * @return a <code>Message</code> object.
207: * @exception java.io.IOException if an error occurs while receiving a
208: * message.
209: * @exception java.io.InterruptedIOException if during this method call this
210: * <code>MessageConnection</code> object is closed.
211: * @exception java.lang.SecurityException if the application doesn't have
212: * permission to receive messages on the given port.
213: */
214: public abstract Message receive() throws IOException;
215:
216: /**
217: * Sends a message over the connection. This method extracts the data
218: * payload from the <code>Message</code> object so that it can be sent as a
219: * datagram.
220: *
221: * @param dmsg a <code>Message</code> object
222: * @exception java.io.IOException if the message could not be sent or
223: * because of network failure
224: * @exception java.lang.IllegalArgumentException if the message contains
225: * invalid information or is incomplete, or the message's payload
226: * exceeds the maximal length for the given protocol.
227: * @exception java.io.InterruptedIOException either if this
228: * <code>Connection</code> object is closed during the execution of this
229: * <code>send</code> method or if a timeout occurs while trying to send
230: * the message.
231: * @exception java.lang.NullPointerException if the parameter is
232: * <code>null</code>.
233: * @exception java.lang.SecurityException if the application doesn't have
234: * permission for sending the message.
235: */
236: public abstract void send(Message dmsg) throws IOException;
237:
238: /**
239: * Ensures that the connection is open.
240: * @exception IOException if the connection is closed
241: */
242: public void ensureOpen() throws IOException {
243: if (!open) {
244: throw new IOException("Connection closed");
245: }
246: }
247:
248: /**
249: * Generates InterruptedIOException when connection is closed.
250: * @param ex input IOException
251: * @param name name of operation: sending or receiving
252: * @exception IOException if the connection is not closed
253: */
254: protected void io2InterruptedIOExc(IOException ex, String name)
255: throws IOException, InterruptedIOException {
256: try {
257: ensureOpen();
258: } catch (IOException ioe) {
259: throw new InterruptedIOException("Connection closed "
260: + "during " + name);
261: }
262: throw ex;
263: }
264:
265: /**
266: * Registers a <code>MessageListener</code> object.
267: * <p>
268: * The platform will notify this listener object when a message has been
269: * received to this <code>MessageConnection</code>.
270: * </p>
271: * <p>If the queue of this <code>MessageConnection</code> contains some
272: * incoming messages that the application haven't read before the call
273: * of this method, the newly registered listener will be notified
274: * immediately exactly once for each such message in the queue.
275: * </p>
276: * <p>There can be at most one listener object registered for
277: * a <code>MessageConnection</code> object at any given point in time.
278: * Setting a new listener will implicitly de-register the possibly
279: * previously set listener.
280: * </p>
281: * <p>Passing <code>null</code> as the parameter de-registers the currently
282: * registered listener, if any.
283: * </p>
284: * @param listener <code>MessageListener</code> object to be registered.
285: * If <code>null</code>,
286: * the possibly currently registered listener will be
287: * de-registered and will not receive notifications.
288: * @exception java.lang.SecurityException if the application does not
289: * have a permission to receive messages using the given port
290: * number
291: * @exception java.io.IOException if it is requested to register
292: * a listener on a client connection or if the connection
293: * has been closed
294: */
295: public void setMessageListener(MessageListener listener)
296: throws IOException {
297:
298: boolean needStopReceiver = false;
299:
300: if (listener != null) {
301: /*
302: * Make sure the connection is still open.
303: */
304: ensureOpen();
305:
306: /*
307: * Check if we have permission to recieve.
308: */
309: checkReceivePermission();
310:
311: /*
312: * Don't let the application waste time listening on a client
313: * connection, which can not be used for receive operations.
314: */
315: if (host != null && host.length() > 0) {
316: throw new IOException(
317: "Cannot listen on client connection");
318: }
319: }
320:
321: synchronized (this ) {
322: if ((m_listener != null) && (listener == null)) {
323: needStopReceiver = true;
324: }
325: m_listener = listener;
326: /* Start a new receive thread when need */
327: if ((listener != null) && (m_listenerThread == null)) {
328: startReceiverThread();
329: }
330: }
331:
332: /* Kill listener when need */
333: if (needStopReceiver) {
334: String save_appID = getAppID();
335: /* Close thread without deregistering */
336: close00(connHandle, 0);
337: setAppID(null);
338: try {
339: m_listenerThread.join();
340: } catch (InterruptedException ie) {
341: } /* Ignore interrupted exception */
342: m_listenerThread = null;
343:
344: setAppID(save_appID);
345: /* Unblock the low level */
346: unblock00(MIDletSuite.UNUSED_SUITE_ID);
347: }
348: }
349:
350: /**
351: * Gets the connection parameter in string mode.
352: * @return string that contains a parameter
353: */
354: protected abstract String getAppID();
355:
356: /**
357: * Sets the connection parameter in string mode.
358: * @param newValue new value of connection parameter
359: */
360: protected abstract void setAppID(String newValue);
361:
362: /**
363: * Unblock the receive thread.
364: *
365: * @param msid The MIDlet suite ID.
366: *
367: * @return returns handle to the connection.
368: */
369: protected abstract int unblock00(int msid) throws IOException;
370:
371: /**
372: * Close connection.
373: *
374: * @param connHandle handle returned by open0
375: * @param deRegister Deregistration appID when parameter is 1.
376: * @return 0 on success, -1 on failure
377: */
378: protected abstract int close00(int connHandle, int deRegister);
379:
380: /**
381: * Checks internal setting of receive permission.
382: * Called from receive and setMessageListener methods.
383: * @exception InterruptedIOException if permission dialog
384: * was preempted
385: */
386: protected abstract void checkReceivePermission()
387: throws InterruptedIOException;
388:
389: /** Waits until message available and notify listeners */
390: /**
391: * Start receiver thread
392: */
393: private void startReceiverThread() {
394: final MessageConnection messageConnection = this ;
395:
396: if (m_listenerThread == null) {
397:
398: m_listenerThread = new Thread() {
399:
400: /**
401: * Run the steps that wait for a new message.
402: */
403: public void run() {
404:
405: int messageLength = 0;
406: do {
407:
408: /* No message, initially. */
409: messageLength = -1;
410: try {
411: messageLength = waitUntilMessageAvailable00(connHandle);
412: if (getAppID() == null) {
413: break;
414: }
415: /*
416: * If a message is available and there are
417: * listeners, notify all listeners of the
418: * incoming message.
419: */
420: if (messageLength >= 0) {
421: synchronized (this ) {
422: if (m_listener != null) {
423:
424: // Invoke registered listener.
425: m_listener
426: .notifyIncomingMessage(messageConnection);
427: }
428: }
429: }
430: } catch (InterruptedIOException iioe) {
431: /*
432: * Terminate, the reader thread has been
433: * interrupted
434: */
435: break;
436: } catch (IOException exception) {
437: break;
438: } catch (IllegalArgumentException iae) {
439: /*
440: * This happens if port has been set to 0;
441: * which indicates that the connection has been
442: * closed. So Terminate.
443: */
444: break;
445: }
446:
447: /*
448: * m_iport = 0 on close
449: */
450: } while (getAppID() != null);
451: }
452: };
453:
454: m_listenerThread.start();
455: }
456: }
457:
458: /**
459: * Waits until message available
460: *
461: * @param handle handle to connection
462: * @return 0 on success, -1 on failure
463: * @exception IOException if an I/O error occurs
464: */
465: protected abstract int waitUntilMessageAvailable00(int handle)
466: throws IOException;
467: }
|