--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/javaextensions/wma/sms_cbs/javasrc/com/nokia/mj/impl/sms/SMSConnectionImpl.java Mon May 03 12:27:20 2010 +0300
@@ -0,0 +1,757 @@
+/*
+* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package com.nokia.mj.impl.sms;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+import javax.wireless.messaging.Message;
+import javax.wireless.messaging.TextMessage;
+import javax.wireless.messaging.BinaryMessage;
+import javax.wireless.messaging.MessageListener;
+import javax.wireless.messaging.MessageConnection;
+
+import com.nokia.mj.impl.utils.Logger;
+import com.nokia.mj.impl.smscbs.utils.WmaUrl;
+import com.nokia.mj.impl.rt.support.ShutdownListener;
+import com.nokia.mj.impl.rt.support.ApplicationUtils;
+/**
+ * The SMSConnectionImpl used for sending and receiving Binary/Text Messages. It
+ * offers methods for creating Binary/Text Messages and for sending them. It
+ * implements the receiving functionality.
+ */
+public class SMSConnectionImpl implements MessageConnection
+{
+ private static final String SERVER_MSG =
+ "Message Connection must be a Server connection";
+
+ private static final int INITIAL = 0;
+
+ private static final int OPEN = 1;
+
+ private static final int CLOSED = 2;
+
+ private static final int ERROR = 3;
+
+ private static final int MAXIMUM_MESAGE_SEGMENTS_SUPPORTED = 10;
+
+ //current Connection state
+ private int iState;
+
+ // any error while receiving message
+ private int iRecvError;
+
+ //status of send
+ private int iSendResult;
+
+ // waiting for message
+ private int iMessageWaiting;
+
+ // number of received messages
+ private int iMessagesOnQueue;
+
+ // number of notifications to be done
+ private int iMessageNotificationsToMake;
+
+ // handle to the native side
+ private int iNativeHandle;
+
+ // any message notification missed
+ private boolean iMissedMessage;
+
+ // Is Server Connection
+ private boolean iServerConnection;
+
+ // synchronization for reading message
+ private final Object iReadLock;
+
+ // synchronization for sending message
+ private final Object iWriteLock;
+
+ // synchronization for closing
+ private final Object iCloseLock;
+
+ // synchronization for receiving
+ private final Object iMessageLock;
+
+ // synchronization for sending
+ private final Object iSendLock;
+
+ // the listener used for messages receiving notifications
+ private MessageListener iMessageListener;
+
+ private WmaUrl iUri;
+
+ public SMSConnectionImpl(WmaUrl aMsgUri, boolean aServerMode)
+ throws IOException
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::SMSConnectionImpl");
+ iUri = aMsgUri;
+ iState = INITIAL;
+ iServerConnection = aServerMode;
+ //create the native side peer
+ iNativeHandle = _createConnection(iUri.getAbsoluteUri(),
+ iServerConnection);
+ iReadLock = new Object();
+ iWriteLock = new Object();
+ iCloseLock = new Object();
+ iMessageLock = new Object();
+ iSendLock = new Object();
+ //register for shutdown listening
+ setShutdownListener();
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "- SMSConnectionImpl::SMSConnectionImpl");
+ }
+
+ /*
+ * This function registers this object for shutDown.
+ */
+ private void setShutdownListener()
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::setShutdownListener");
+ // Get the insatnce of ApplicationUtils.
+ ApplicationUtils appUtils = ApplicationUtils.getInstance();
+
+ // Get the name of the application.
+ appUtils.addShutdownListener(new ShutdownListener()
+ {
+ //The method that gets called when Application is shutting down
+ public void shuttingDown()
+ {
+ try
+ {
+ close();
+ }
+ catch (IOException ex)
+ {
+ //Nothing to do, just ignore
+ Logger.ELOG(Logger.EWMA, ex.toString(), ex);
+ }
+ }
+ });
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "- SMSConnectionImpl::setShutdownListener");
+ }
+
+ /**
+ * open() is only called when a MIDlet calls Connector.open() a MIDlet will
+ * not be able to access the Connection until open() has completed and a
+ * reference has been returned. It should then only be called once, by only
+ * one thread. particularly, a Connection cannot be closed by a MIDlet until
+ * it has been fully opened.
+ * @exceptions Throws IOException if any error while Opening
+ */
+ public void open() throws IOException
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,"+ SMSConnectionImpl::open");
+ if (iState == INITIAL)
+ {
+ iState = OPEN;
+ //in case of server connection a notifier thread is created which
+ //waits in the native side and informs the application when there
+ //is an incoming message.
+ if (iServerConnection)
+ {
+ synchronized (iCloseLock)
+ {
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ _openConnection(iNativeHandle);
+ }
+ }).start();
+ }
+ }
+ }
+ Logger.LOG(Logger.EWMA, Logger.EInfo,"- SMSConnectionImpl::open");
+ }
+
+ /**
+ * close() is only called when a MIDlet calls close() on a connection that
+ * is opened by calling Connector.open() A MIDlet will not be able to access
+ * the Connection after calling close() on that connection.
+ * @exceptions Throws IOException if any error while closing.
+ */
+ public void close() throws IOException
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,"+ SMSConnectionImpl::close");
+ synchronized (iCloseLock)
+ {
+ if (iState != CLOSED)
+ {
+ iState = CLOSED;
+ _closeConnection(iNativeHandle);
+
+ //notify the notifier thread if it is waiting for a receive() or
+ //setMessageListener() to be called
+ synchronized (iMessageLock)
+ {
+ iMessageLock.notify();
+ }
+ //notify the receive operation about connection closed
+ synchronized (iReadLock)
+ {
+ iReadLock.notify();
+ }
+ _dispose(iNativeHandle);
+ }
+ }
+ Logger.LOG(Logger.EWMA, Logger.EInfo,"- SMSConnectionImpl::close");
+ }
+
+ /**
+ * Sets the message listener for this connection
+ *
+ * @param aListener
+ * listener interested for incoming message.
+ * @exceptions throws IOException if the connection is already closed
+ * or this method is called on a client connection.
+ */
+ public void setMessageListener(MessageListener aListener)
+ throws IOException
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::setMessageListener");
+ if (!iServerConnection)
+ {
+ throw new IOException(SERVER_MSG);
+ }
+
+ if (iState == CLOSED)
+ {
+ throw new IOException(
+ "Setting message listener failed: connection is already closed");
+ }
+ checkReceivePermission();
+ synchronized (iReadLock)
+ {
+ iMessageListener = aListener;
+ if (aListener != null)
+ {
+ synchronized (iMessageLock)
+ {
+ iMessageLock.notify();
+ }
+ }
+ }
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::setMessageListener");
+ }
+
+ /**
+ * checks for any error while receiving a message
+ * @exception throws IOException in case of any error
+ */
+ private final void checkForReceiveError() throws IOException
+ {
+ if (iRecvError !=0)
+ {
+ int err = iRecvError;
+ iRecvError = 0;
+ if (err < 0)
+ {
+ _checkError(err, false);
+ }
+ }
+ }
+
+ /**
+ * Checks for security permission for receive operation.
+ * @exception throws SecurityException if no valid permission exists.
+ */
+ protected void checkReceivePermission()
+ {
+ ApplicationUtils appUtils = ApplicationUtils.getInstance();
+ SMSPermissionImpl permission = new SMSPermissionImpl("sms://*",
+ "receive");
+ appUtils.checkPermission(permission);
+ }
+ /**
+ * Receive is a 3 step process: 1) Wait for some incoming message 2)
+ * Retrieve the the message 3) Construct a Binary/Text Message
+ *
+ * @exceptions throws IOException if the connection is already closed.
+ * @exceptions throws InterruptedIOException if the connection is closed
+ * while receiving
+ * @returns the constructed message
+ */
+ public Message receive() throws IOException, InterruptedIOException
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,"+ SMSConnectionImpl::receive");
+ if (!iServerConnection)
+ {
+ //throw IOException if receive is called on client connection
+ throw new IOException(SERVER_MSG);
+ }
+ Message message = null;
+ synchronized (iReadLock)
+ {
+ //check for connection
+ if (iState == CLOSED)
+ {
+ throw new IOException(
+ "Receiving message failed: connection is already closed");
+ }
+ if (iUri.isRestrictedPort() == true)
+ {
+ throw new SecurityException(
+ "No permission to receive on port: " + iUri.getPort());
+ }
+ checkReceivePermission();
+ //if no messages were there to receive wait till some message
+ //arrives
+ if (iMessagesOnQueue == 0)
+ {
+ iMessageWaiting++;
+ //notify the notifier thread
+ synchronized (iMessageLock)
+ {
+ iMessageLock.notify();
+ }
+ //wait for incoming message notification
+ try
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "WMA : Waiting for message");
+ iReadLock.wait();
+ }
+ catch (InterruptedException ex)
+ {
+ Logger.ELOG(Logger.EWMA, ex.toString(), ex);
+ }
+ }
+ //check for any error while receiving
+ checkForReceiveError();
+ synchronized (iCloseLock)
+ {
+ String remoteAddress;
+ //if the connection was closed throw InterruptedIOException
+ if (iState == CLOSED)
+ {
+ throw new InterruptedIOException(
+ "Connection closed while receiving message");
+ }
+ //get the message type ( Text/Binary)
+ final int messageType = _getMessageType(iNativeHandle);
+ if (messageType != SMSMessageImpl.MESSAGE_TEXT &&
+ messageType != SMSMessageImpl.MESSAGE_BINARY)
+ {
+ if (messageType < 0)
+ {
+ _checkError(messageType, false);
+ }
+ }
+ //get the remote host address
+ remoteAddress = _getHostAddress(iNativeHandle);
+ //get the time stamp of the message
+ final long timestamp = _getTimestamp(iNativeHandle);
+ //construct the message object based on the message type
+ if (messageType == SMSMessageImpl.MESSAGE_TEXT)
+ {
+ //get the text message data.
+ final String textData = (String)_getReceivedMessageData(
+ iNativeHandle, messageType);
+ //construct the text message object
+ TextMessage textMessage = new SMSTextMessageImpl(
+ remoteAddress, timestamp);
+ textMessage.setPayloadText(textData);
+ message = textMessage;
+ }
+ else
+ {
+ //get the Binary message data
+ final byte[] binaryData = (byte[]) _getReceivedMessageData(
+ iNativeHandle, messageType);
+ //construct the binary message object
+ BinaryMessage binaryMessage = new SMSBinaryMessageImpl(
+ remoteAddress, timestamp);
+ binaryMessage.setPayloadData(binaryData);
+ message = binaryMessage;
+ }
+ }
+ iMessagesOnQueue--;
+ }
+ Logger.LOG(Logger.EWMA, Logger.EInfo,"- SMSConnectionImpl::receive");
+ return message;
+ }
+
+ /**
+ * Method for creating a Binary/Text Message object. If it is a client
+ * connection The destination address of the newly created messages will be
+ * set to the destination part of the URI provided when this connection was
+ * created.
+ *
+ * @param aType
+ * The type of the message to be created. It can only be
+ * "Binary/Text"
+ */
+ public Message newMessage(String aType)
+ {
+ String address = null;
+ if (!iServerConnection)
+ {
+ address = iUri.getAbsoluteUri();
+ }
+
+ return newMessage(aType, address);
+ }
+
+ /**
+ * Method for creating a Binary/Text Message object
+ *
+ * @param aType
+ * The type of the message to be created. It can only be
+ * "Binary/Text"
+ * @param aAddress
+ * the destination address of the Message
+ */
+ public Message newMessage(String aType, String aAddress)
+ {
+ Message msg;
+ if (aType.equals(TEXT_MESSAGE))
+ {
+ msg = new SMSTextMessageImpl(aAddress, 0);
+ }
+ else if (aType.equals(BINARY_MESSAGE))
+ {
+ msg = new SMSBinaryMessageImpl(aAddress, 0);
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Message type is invalid: " + aType);
+ }
+
+ return msg;
+ }
+
+ /**
+ * Returns the number of segments required for sending a
+ * Binary/Text Message.
+ * @param aMsg The Message for which the segments to be found.
+ * @exceptions throws IllegalArguementException if no address is specified
+ * or if the message is not a valid message
+ */
+ public int numberOfSegments(Message aMsg)
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::numberOfSegments");
+ int messageSegments = 0;
+ if (aMsg == null)
+ {
+ throw new NullPointerException("Message parameter is null");
+ }
+ String address = aMsg.getAddress();
+ // if it is a serverConnection and address field of the message is null
+ // then it calculates numberOfSegments assuming Port number present.
+ // Because that will be the maximum numberOfSegments required to send
+ // the message.
+ final WmaUrl uri;
+ if (iServerConnection && address != null)
+ {
+ uri = new WmaUrl(address);
+ uri.validateUri();
+ }
+ else
+ {
+ uri = iUri;
+ }
+
+ final SMSMessageImpl messageImpl;
+ try
+ {
+ messageImpl = (SMSMessageImpl) aMsg;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("NumberOfSegments can only"
+ + "be calculated for Text/Binary Message");
+ }
+ synchronized (iCloseLock)
+ {
+ if (iState != CLOSED)
+ {
+ if (messageImpl.getType() == messageImpl.MESSAGE_TEXT)
+ {
+
+ messageSegments = _smsTextDataSegments(iNativeHandle,
+ messageImpl.getType(), uri.getHost(), uri.getPort(),
+ messageImpl.getPayloadTextData());
+ }
+ else
+ {
+
+ messageSegments = _smsBinaryDataSegments(iNativeHandle,
+ messageImpl.getType(), uri.getHost(), uri.getPort(),
+ messageImpl.getPayloadBinaryData());
+ }
+ }
+ }
+ if (messageSegments > MAXIMUM_MESAGE_SEGMENTS_SUPPORTED)
+ {
+ messageSegments = 0;
+ }
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::numberOfSegments");
+ return messageSegments;
+ }
+
+ /**
+ * Method for sending a certain message. Only Binary/Text Messages can be
+ * sent through this connection. The destination addresses of the Message to
+ * be sent have to contain the destination address .
+ *
+ * @param aMsg
+ * The message to be sent
+ * @exceptions throws IOException if there is any n/w problem or if
+ * connection is already closed
+ * @exceptions throws InterruptedIOException if a timeout occurs or if
+ * the connection is closed while sending.
+ * @exceptions throws IllegalArguementException if No address or host is
+ * specified
+ */
+ public void send(Message aMsg) throws IOException, InterruptedIOException
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo, "+ SMSConnectionImpl::send");
+ int messageSegments = 0;
+ if (aMsg == null)
+ {
+ throw new NullPointerException("Message parameter is null");
+ }
+ String sendAddress = aMsg.getAddress();
+ //check whether destination address is specified in the message
+ if (sendAddress == null)
+ {
+ throw new IllegalArgumentException(
+ "Message destination address not specified");
+ }
+ final WmaUrl uri = new WmaUrl(sendAddress);
+ // validate the address
+ uri.validateUri();
+ // check whether destination host address is specified in the message
+ if ("".equals(uri.getHost()))
+ {
+ throw new IllegalArgumentException(
+ "Message destination address not specified");
+ }
+ final SMSMessageImpl messageImpl;
+ try
+ {
+ messageImpl = (SMSMessageImpl) aMsg;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("The message to be sent with"
+ + "this connection can only be Text/Binary Message");
+ }
+ // validates the port
+ if (uri.isRestrictedPort() == true)
+ {
+ throw new SecurityException(
+ "No permission to send on port: " + uri.getPort());
+ }
+ //to synchronize send functionality
+ synchronized (iWriteLock)
+ {
+ synchronized (iSendLock)
+ {
+ // send operation should not be carried out while close
+ // is in progress
+ synchronized (iCloseLock)
+ {
+ //verify whether connection is closed.
+ if (iState == CLOSED)
+ {
+ throw new IOException(
+ "Sending message failed: connection is already closed");
+ }
+ //calculate the number of message segments required to send this
+ //message
+ if (messageImpl.getType() == messageImpl.MESSAGE_TEXT)
+ {
+
+ messageSegments = _smsTextDataSegments(iNativeHandle,
+ messageImpl.getType(), uri.getHost(), uri.getPort(),
+ messageImpl.getPayloadTextData());
+ }
+ else
+ {
+
+ messageSegments = _smsBinaryDataSegments(iNativeHandle,
+ messageImpl.getType(), uri.getHost(), uri.getPort(),
+ messageImpl.getPayloadBinaryData());
+ }
+ //check whether message is too big for sending
+ if (messageSegments > MAXIMUM_MESAGE_SEGMENTS_SUPPORTED)
+ {
+ throw new IllegalArgumentException(
+ "Message exceeds the maximum supported segments: " +
+ MAXIMUM_MESAGE_SEGMENTS_SUPPORTED);
+ }
+ ApplicationUtils appUtils = ApplicationUtils.getInstance();
+ // remove the protocol
+ SMSPermissionImpl permission = new SMSPermissionImpl("sms://*",
+ "send",messageSegments,uri.getHost()
+ + ((uri.getPort() != null
+ && uri.getPort().length() > 0)
+ ? (":" + uri.getPort()) : ""));
+
+ appUtils.checkPermission(permission);
+ //to allow close operation to succeed while sending, send
+ //operation is carried out in a new thread
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ doSend();
+ }
+ }).start();
+ //wait till the message was sent successfully
+ try
+ {
+ iSendLock.wait();
+ }
+ catch (InterruptedException ex)
+ {
+ Logger.ELOG(Logger.EWMA, "Sending a message failed", ex);
+ }
+ if (iSendResult < 0)
+ {
+ _checkError(iSendResult, true);
+ }
+ }
+ }
+ }
+ Logger.LOG(Logger.EWMA, Logger.EInfo, "- SMSConnectionImpl::send");
+ }
+
+
+ /**
+ * This method runs in seperate thread and unblocks the send with the
+ * send status
+ */
+ private void doSend()
+ {
+ iSendResult = _sendMessage(iNativeHandle);
+ synchronized (iSendLock)
+ {
+ iSendLock.notify();
+ }
+ }
+
+ /**
+ * Callback through which the Native notifies about the receiving of a
+ * Message or any error while receiving a message.
+ * @param1 aReceivedMesgs - number of messages to be received
+ * @param2 aError - any error while receiving a message
+ *
+ */
+ protected int messageReceiveCallback(int aReceivedMsgs, int aError)
+ {
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::messageReceiveCallback");
+ synchronized (iMessageLock)
+ {
+ //check either receive was called or a listener was set on this
+ //connection
+ if (0 == iMessageWaiting && (null == iMessageListener))
+ {
+ //if neither receive nor setMessageListener was called on this
+ //wait for either of them to be called
+ try
+ {
+ iMessageLock.wait();
+ }
+ catch (InterruptedException ex)
+ {
+ Logger.ELOG(Logger.EWMA, ex.toString(), ex);
+ }
+ }
+ }
+ synchronized (iReadLock)
+ {
+ //calculate number of new messages arrived and to be notified
+ if (aReceivedMsgs > 0)
+ {
+ iMessagesOnQueue += aReceivedMsgs;
+ iMessageNotificationsToMake += aReceivedMsgs;
+ }
+ //incase of any error store that error
+ else
+ {
+ iRecvError = aError;
+ }
+ if (0 != iMessageWaiting)
+ {
+ iMessageWaiting--;
+ //if a receive operation was called notify the receive about
+ //the incoming message
+ iReadLock.notify();
+ }
+ else if (null != iMessageListener)
+ {
+ //if a listener was set notify the listener about the incoming
+ //messages
+ while (iMessageNotificationsToMake > 0)
+ {
+ iMessageListener.notifyIncomingMessage(this);
+ iMessageNotificationsToMake--;
+ }
+ //incase of an error notify
+ if (iRecvError != 0)
+ {
+ iMessageListener.notifyIncomingMessage(this);
+ }
+ }
+ }
+ Logger.LOG(Logger.EWMA, Logger.EInfo,
+ "+ SMSConnectionImpl::messageReceiveCallback");
+ return ((iState == CLOSED)? -1 : 0);
+ }
+
+ private native int _createConnection(String aUri,
+ boolean aServerConnection);
+
+ private native void _openConnection(int aNativeHandle);
+
+ private native void _closeConnection(int aNativeHandle);
+
+ private native int _smsTextDataSegments(int aNativePeerHandle,
+ int aMsgType, String aHostAddress, String aHostPort, String aData);
+
+ private native int _smsBinaryDataSegments(int aNativePeerHandle,
+ int aMsgType, String aHostAddress, String aHostPort, byte[] aData);
+
+ private native int _sendMessage(int aNativePeerHandle);
+
+ private native int _getMessageType(int aNativePeerHandle);
+
+ private native String _getHostAddress(int aNativePeerHandle);
+
+ private native long _getTimestamp(int aNativePeerHandle);
+
+ private native Object _getReceivedMessageData(int aNativePeerHandle,
+ int aMsgType);
+ private native void _dispose(int aNativeHandle);
+
+ private native void _checkError(int aError,boolean aIsSendOperation);
+}
+