/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.tck.wma.sms;
import com.sun.tck.wma.PropLoader;
import com.sun.tck.wma.BinaryMessage;
import com.sun.tck.wma.Message;
import com.sun.tck.wma.MessageConnection;
import com.sun.tck.wma.MessageTransportConstants;
import com.sun.tck.wma.TextMessage;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Properties;
import com.sun.midp.io.j2me.sms.TextEncoder;
import java.io.IOException;
/**
* SMS message connection handler.
*/
public class SMSMessageConnection extends PropLoader
implements MessageConnection {
/** Machine name - the parsed target address from the URL. */
protected String host = null;
/** Port number from the URL connection string. */
protected String port = null;
/** Datagram host for sending/receiving. */
protected String clientHost;
/** Datagram transport for sending. */
protected int portOut;
/** Datagram transport for receiving. */
protected int portIn;
/** Phone number of the message sender. */
protected String phoneNumber;
/** Fragment size for large messages. */
int fragmentsize;
/** Datagram server connection. */
DatagramSocket dgc;
/** Datagram buffer. */
byte [] buf = new byte[MessageTransportConstants.DATAGRAM_PACKET_SIZE];
/** Datagram envelope for sending or receiving messages. */
DatagramPacket mess =
new DatagramPacket(buf, MessageTransportConstants.DATAGRAM_PACKET_SIZE);
/**
* Open flag indicates when the connection is closed. When a connection is
* closed, subsequent operations throw an exception.
*/
protected boolean open;
/** Constructor for SMS message connection handling. */
public SMSMessageConnection() {
/*
* Configurable parameters for low level transport.
* e.g. sms://+5551234:54321 maps to datagram://129.148.70.80:123
*/
clientHost = "localhost";
portOut = 11100;
portIn = 11101;
phoneNumber = "+5551234";
/*
* Check for overrides in the "connections.prop"
* configuration file.
*/
clientHost = getProp("localhost", "JSR_205_DATAGRAM_HOST",
"connections.prop", "DatagramHost");
portOut = getIntProp(11100, "JSR_205_SMS_OUT_PORT",
"connections.prop", "SMSDatagramPortOut");
portIn = getIntProp(11101, "JSR_205_SMS_PORT",
"connections.prop", "SMSDatagramPortIn");
phoneNumber = getProp("+5551234", "JSR_205_PHONE_NUMBER",
"connections.prop", "PhoneNumber");
}
/**
* Opens a connection.
* <p>
* This method is called from <code>Connector.open()</code> method to obtain
* the destination address given in the <code>name</code> parameter.
* <p>
* The format for the <code>name</code> string for this method is:
* <code>sms://<em>phone_number</em>:<em>port</em></code>
* where the <em>phone_number:</em> is optional. If the
* <em>phone_number</em> parameter is present, the connection is being
* opened in "client" mode. This means that messages can be sent. If the
* parameter is absent, the connection is being opened in "server" mode.
* This means that messages can be sent and received.
* <p>
* The connection that is opened is to a low-level transport mechanism which
* can be any of the following:
* <ul>
* <li>A datagram Short Message Peer to Peer (SMPP) to a service
* center.
* <li>A <code>comm</code> connection to a phone device with AT-commands.
* </ul>
*
* @param name the target of the connection
* @return this connection
* @throws IOException if the connection is closed or unavailable.
*/
public MessageConnection openPrim(String name)
throws IOException {
/*
* If <code>host == null</code>, then we are a server endpoint at
* the supplied <code>port</code>.
*
* If <code>host != null</code> we are a client endpoint at a port
* decided by the system and the default address for
* SMS messages to be sent is <code>sms://host:port</code> .
*/
if (name.charAt(0) != '/' || name.charAt(1) != '/') {
throw new IllegalArgumentException(
"Missing protocol separator.");
}
int colon = name.indexOf(':');
if (colon > 0) {
if (colon != 2) {
host = name.substring(2, colon);
}
port = name.substring(colon + 1);
} else {
if (name.length() > 2) {
host = name.substring(2);
}
}
/* Open the inbound server datagram connection. */
dgc = new DatagramSocket(portIn);
open = true;
return this;
}
/**
* Constructs a new message object of a text or binary type.
* <p>
* When the <code>TEXT_MESSAGE</code> constant is passed in, the created
* object implements the <code>TextMessage</code> interface. When the
* <code>BINARY_MESSAGE</code> constant is passed in, the created object
* implements the <code>BinaryMessage</code> interface.
* <p>
* If this method is called in a sending mode, a new <code>Message</code>
* object is requested from the connection. For example:
* <p>
* <code>Message msg = conn.newMessage(TEXT_MESSAGE);</code>
* <p>
* The newly created <code>Message</code> does not have the destination
* address set. It must be set by the application before the message is
* sent.
* <p>
* If it is called in receiving mode, the <code>Message</code> object does
* have its address set. The application can act on the object to extract
* the address and message data.
* <p>
* <!-- The <code>type</code> parameter indicates the number of bytes
* that should be
* allocated for the message. No restrictions are placed on the application
* for the value of <code>size</code>.
* A value of <code>null</code> is permitted and creates a
* <code>Message</code> object
* with a 0-length message. -->
*
* @param type either TEXT_MESSAGE or BINARY_MESSAGE.
* @return a new message
*/
public Message newMessage(String type) {
return newMessage(type, null);
}
/**
* Constructs a new message object of a text or binary type and specifies
* a destination address.
* When the
* <code>TEXT_MESSAGE</code> constant is passed in, the created
* object implements the <code>TextMessage</code> interface.
* When the <code>BINARY_MESSAGE</code> constant is passed in, the
* created object implements the <code>BinaryMessage</code>
* interface.
* <p>
* The destination address <code>addr</code> has the following format:
* <code>sms://</code><em>phone_number</em>:<em>port</em>.
*
* @param type either TEXT_MESSAGE or BINARY_MESSAGE.
* @param addr the destination address of the message.
* @return a new <code>Message</code> object.
*/
public Message newMessage(String type, String addr) {
/* Return the appropriate type of sub message. */
if (type == MessageConnection.TEXT_MESSAGE) {
return new TextObject(addr);
} else if (type == MessageConnection.BINARY_MESSAGE) {
return new BinaryObject(addr);
}
return null; /* message type not supported */
}
/**
* Sends a message over the connection. This method extracts
* the data payload from
* the <code>Message</code> object so that it
* can be sent as a datagram.
*
* @param dmsg a <code>Message</code> object.
* @exception ConnectionNotFoundException if the address is
* invalid or if no address is found in the message.
* @exception IOException if an I/O error occurs.
*/
public void send(Message dmsg) throws IOException {
/** Saved timestamp for use with multiple segment records. */
long sendtime = System.currentTimeMillis();
byte[] buffer = null;
String type = null;
if (dmsg instanceof TextMessage) {
type = MessageConnection.TEXT_MESSAGE;
buffer = ((TextObject)dmsg).getPayloadData();
} else if (dmsg instanceof BinaryMessage) {
type = MessageConnection.BINARY_MESSAGE;
buffer = ((BinaryObject)dmsg).getPayloadData();
}
/*
* For text messages choose between UCS-2 or GSM 7-bit
* encoding.
*/
int encodingType = MessageTransportConstants.GSM_BINARY;
if (type.equals(MessageConnection.TEXT_MESSAGE)) {
byte[] gsm7bytes = TextEncoder.encode(buffer);
if (gsm7bytes != null) {
encodingType = MessageTransportConstants.GSM_TEXT;
buffer = gsm7bytes;
} else {
encodingType = MessageTransportConstants.GSM_UCS2;
}
}
mess = new DatagramPacket(buf,
MessageTransportConstants.DATAGRAM_PACKET_SIZE);
mess.setAddress(InetAddress.getByName(clientHost));
mess.setPort(portOut);
SMSPacket smsPacket = new SMSPacket();
smsPacket.setEncodingType(encodingType);
if (port != null) {
smsPacket.setPort(Integer.parseInt(port));
} else {
smsPacket.setPort(0);
}
smsPacket.setTimeStamp(sendtime);
/*
* Set target address.
*/
smsPacket.setAddress(dmsg.getAddress());
/*
* Set sender's phone number
*/
smsPacket.setPhoneNumber(phoneNumber);
smsPacket.setMessageLength(buffer.length);
smsPacket.setMessage(buffer);
debug("SMS PACKET: clientHost = " + InetAddress.getByName(clientHost));
debug("SMS PACKET: portOut = " + portOut);
debug("SMS PACKET: encoding type = " + encodingType);
debug("SMS PACKET: port = " + port);
debug("SMS PACKET: timestamp = " + sendtime);
debug("SMS PACKET: address = " + dmsg.getAddress());
debug("SMS PACKET: sender's phone number = " + phoneNumber);
debug("SMS PACKET: message length = " + buffer.length);
debug("SMS PACKET: message:" + new String(buffer));
byte[] buf = smsPacket.getData();
mess.setData(buf, 0, buf.length);
dgc.send(mess);
}
/**
* Receives the bytes that have been sent over the connection, constructs a
* <code>Message</code> object, and returns it.
* <p>
* If there are no <code>Message</code>s waiting on the connection, this
* method will block until a message is received, or the
* <code>MessageConnection</code> is closed.
*
* @return a <code>Message</code> object
* @exception IOException if an I/O error occurs.
*/
public synchronized Message receive()
throws IOException {
dgc.receive(mess);
/* get message buffer and extract info */
byte[] buf = mess.getData();
SMSPacket packet = new SMSPacket(buf);
int msgType = packet.getEncodingType();
int smsPort = packet.getPort();
long time = packet.getTimeStamp();
String address = packet.getAddress();
String phoneNumber = packet.getPhoneNumber();
int msgLen = packet.getMessageLength();
byte[] messg = packet.getMessage(msgLen);
debug("SMS PACKET: encodingType = " + msgType);
debug("SMS PACKET: port = " + smsPort);
debug("SMS PACKET: time = " + time);
debug("SMS PACKET: address = " + address);
debug("SMS PACKET: Sender's phone number = " + phoneNumber);
debug("SMS PACKET: message length = " + msgLen);
debug("SMS PACKET: message:" + new String(messg));
Message msg = null;
if (msgType == MessageTransportConstants.GSM_TEXT ||
msgType == MessageTransportConstants.GSM_UCS2) {
TextMessage textmsg = (TextMessage)
newMessage(MessageConnection.TEXT_MESSAGE,
address);
/* Always store text messages as UCS 2 bytes. */
if (msgType == MessageTransportConstants.GSM_TEXT) {
messg = TextEncoder.decode(messg);
}
((TextObject)textmsg).setPayloadData(messg);
msg = textmsg;
} else {
BinaryMessage binmsg = (BinaryMessage)
newMessage(MessageConnection.BINARY_MESSAGE,
address);
((BinaryObject)binmsg).setPayloadData(messg);
msg = binmsg;
}
((MessageObject) msg).setTimeStamp(time);
return msg;
}
/**
* Closes the connection. Reset the connection is open flag
* so methods can be checked to throws an appropriate exception
* for operations on a closed connection.
*
* @exception IOException if an I/O error occurs.
*/
public void close() throws IOException {
if (open) {
dgc.close();
dgc = null;
open = false;
}
}
/**
* Show a debug message.
*
* @param text The text to be displayed after a mandatory prefix.
*/
private void debug(String text) {
System.out.println("SMSMessageConnection: " + text);
}
}
|