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.tck.wma.sms;
028:
029: import com.sun.tck.wma.PropLoader;
030: import com.sun.tck.wma.BinaryMessage;
031: import com.sun.tck.wma.Message;
032: import com.sun.tck.wma.MessageConnection;
033: import com.sun.tck.wma.MessageTransportConstants;
034: import com.sun.tck.wma.TextMessage;
035: import java.net.DatagramPacket;
036: import java.net.DatagramSocket;
037: import java.net.InetAddress;
038: import java.util.Properties;
039: import com.sun.midp.io.j2me.sms.TextEncoder;
040:
041: import java.io.IOException;
042:
043: /**
044: * SMS message connection handler.
045: */
046: public class SMSMessageConnection extends PropLoader implements
047: MessageConnection {
048:
049: /** Machine name - the parsed target address from the URL. */
050: protected String host = null;
051:
052: /** Port number from the URL connection string. */
053: protected String port = null;
054:
055: /** Datagram host for sending/receiving. */
056: protected String clientHost;
057:
058: /** Datagram transport for sending. */
059: protected int portOut;
060:
061: /** Datagram transport for receiving. */
062: protected int portIn;
063:
064: /** Phone number of the message sender. */
065: protected String phoneNumber;
066:
067: /** Fragment size for large messages. */
068: int fragmentsize;
069:
070: /** Datagram server connection. */
071: DatagramSocket dgc;
072:
073: /** Datagram buffer. */
074: byte[] buf = new byte[MessageTransportConstants.DATAGRAM_PACKET_SIZE];
075:
076: /** Datagram envelope for sending or receiving messages. */
077: DatagramPacket mess = new DatagramPacket(buf,
078: MessageTransportConstants.DATAGRAM_PACKET_SIZE);
079:
080: /**
081: * Open flag indicates when the connection is closed. When a connection is
082: * closed, subsequent operations throw an exception.
083: */
084: protected boolean open;
085:
086: /** Constructor for SMS message connection handling. */
087: public SMSMessageConnection() {
088:
089: /*
090: * Configurable parameters for low level transport.
091: * e.g. sms://+5551234:54321 maps to datagram://129.148.70.80:123
092: */
093:
094: clientHost = "localhost";
095: portOut = 11100;
096: portIn = 11101;
097: phoneNumber = "+5551234";
098:
099: /*
100: * Check for overrides in the "connections.prop"
101: * configuration file.
102: */
103:
104: clientHost = getProp("localhost", "JSR_205_DATAGRAM_HOST",
105: "connections.prop", "DatagramHost");
106:
107: portOut = getIntProp(11100, "JSR_205_SMS_OUT_PORT",
108: "connections.prop", "SMSDatagramPortOut");
109:
110: portIn = getIntProp(11101, "JSR_205_SMS_PORT",
111: "connections.prop", "SMSDatagramPortIn");
112:
113: phoneNumber = getProp("+5551234", "JSR_205_PHONE_NUMBER",
114: "connections.prop", "PhoneNumber");
115:
116: }
117:
118: /**
119: * Opens a connection.
120: * <p>
121: * This method is called from <code>Connector.open()</code> method to obtain
122: * the destination address given in the <code>name</code> parameter.
123: * <p>
124: * The format for the <code>name</code> string for this method is:
125: * <code>sms://<em>phone_number</em>:<em>port</em></code>
126: * where the <em>phone_number:</em> is optional. If the
127: * <em>phone_number</em> parameter is present, the connection is being
128: * opened in "client" mode. This means that messages can be sent. If the
129: * parameter is absent, the connection is being opened in "server" mode.
130: * This means that messages can be sent and received.
131: * <p>
132: * The connection that is opened is to a low-level transport mechanism which
133: * can be any of the following:
134: * <ul>
135: * <li>A datagram Short Message Peer to Peer (SMPP) to a service
136: * center.
137: * <li>A <code>comm</code> connection to a phone device with AT-commands.
138: * </ul>
139: *
140: * @param name the target of the connection
141: * @return this connection
142: * @throws IOException if the connection is closed or unavailable.
143: */
144: public MessageConnection openPrim(String name) throws IOException {
145: /*
146: * If <code>host == null</code>, then we are a server endpoint at
147: * the supplied <code>port</code>.
148: *
149: * If <code>host != null</code> we are a client endpoint at a port
150: * decided by the system and the default address for
151: * SMS messages to be sent is <code>sms://host:port</code> .
152: */
153:
154: if (name.charAt(0) != '/' || name.charAt(1) != '/') {
155: throw new IllegalArgumentException(
156: "Missing protocol separator.");
157: }
158:
159: int colon = name.indexOf(':');
160: if (colon > 0) {
161: if (colon != 2) {
162: host = name.substring(2, colon);
163: }
164: port = name.substring(colon + 1);
165: } else {
166: if (name.length() > 2) {
167: host = name.substring(2);
168: }
169: }
170:
171: /* Open the inbound server datagram connection. */
172: dgc = new DatagramSocket(portIn);
173:
174: open = true;
175: return this ;
176: }
177:
178: /**
179: * Constructs a new message object of a text or binary type.
180: * <p>
181: * When the <code>TEXT_MESSAGE</code> constant is passed in, the created
182: * object implements the <code>TextMessage</code> interface. When the
183: * <code>BINARY_MESSAGE</code> constant is passed in, the created object
184: * implements the <code>BinaryMessage</code> interface.
185: * <p>
186: * If this method is called in a sending mode, a new <code>Message</code>
187: * object is requested from the connection. For example:
188: * <p>
189: * <code>Message msg = conn.newMessage(TEXT_MESSAGE);</code>
190: * <p>
191: * The newly created <code>Message</code> does not have the destination
192: * address set. It must be set by the application before the message is
193: * sent.
194: * <p>
195: * If it is called in receiving mode, the <code>Message</code> object does
196: * have its address set. The application can act on the object to extract
197: * the address and message data.
198: * <p>
199: * <!-- The <code>type</code> parameter indicates the number of bytes
200: * that should be
201: * allocated for the message. No restrictions are placed on the application
202: * for the value of <code>size</code>.
203: * A value of <code>null</code> is permitted and creates a
204: * <code>Message</code> object
205: * with a 0-length message. -->
206: *
207: * @param type either TEXT_MESSAGE or BINARY_MESSAGE.
208: * @return a new message
209: */
210: public Message newMessage(String type) {
211: return newMessage(type, null);
212: }
213:
214: /**
215: * Constructs a new message object of a text or binary type and specifies
216: * a destination address.
217: * When the
218: * <code>TEXT_MESSAGE</code> constant is passed in, the created
219: * object implements the <code>TextMessage</code> interface.
220: * When the <code>BINARY_MESSAGE</code> constant is passed in, the
221: * created object implements the <code>BinaryMessage</code>
222: * interface.
223: * <p>
224: * The destination address <code>addr</code> has the following format:
225: * <code>sms://</code><em>phone_number</em>:<em>port</em>.
226: *
227: * @param type either TEXT_MESSAGE or BINARY_MESSAGE.
228: * @param addr the destination address of the message.
229: * @return a new <code>Message</code> object.
230: */
231: public Message newMessage(String type, String addr) {
232: /* Return the appropriate type of sub message. */
233: if (type == MessageConnection.TEXT_MESSAGE) {
234: return new TextObject(addr);
235: } else if (type == MessageConnection.BINARY_MESSAGE) {
236: return new BinaryObject(addr);
237: }
238: return null; /* message type not supported */
239: }
240:
241: /**
242: * Sends a message over the connection. This method extracts
243: * the data payload from
244: * the <code>Message</code> object so that it
245: * can be sent as a datagram.
246: *
247: * @param dmsg a <code>Message</code> object.
248: * @exception ConnectionNotFoundException if the address is
249: * invalid or if no address is found in the message.
250: * @exception IOException if an I/O error occurs.
251: */
252: public void send(Message dmsg) throws IOException {
253:
254: /** Saved timestamp for use with multiple segment records. */
255: long sendtime = System.currentTimeMillis();
256:
257: byte[] buffer = null;
258: String type = null;
259: if (dmsg instanceof TextMessage) {
260: type = MessageConnection.TEXT_MESSAGE;
261: buffer = ((TextObject) dmsg).getPayloadData();
262: } else if (dmsg instanceof BinaryMessage) {
263: type = MessageConnection.BINARY_MESSAGE;
264: buffer = ((BinaryObject) dmsg).getPayloadData();
265: }
266:
267: /*
268: * For text messages choose between UCS-2 or GSM 7-bit
269: * encoding.
270: */
271: int encodingType = MessageTransportConstants.GSM_BINARY;
272: if (type.equals(MessageConnection.TEXT_MESSAGE)) {
273: byte[] gsm7bytes = TextEncoder.encode(buffer);
274: if (gsm7bytes != null) {
275: encodingType = MessageTransportConstants.GSM_TEXT;
276: buffer = gsm7bytes;
277: } else {
278: encodingType = MessageTransportConstants.GSM_UCS2;
279: }
280: }
281:
282: mess = new DatagramPacket(buf,
283: MessageTransportConstants.DATAGRAM_PACKET_SIZE);
284: mess.setAddress(InetAddress.getByName(clientHost));
285: mess.setPort(portOut);
286:
287: SMSPacket smsPacket = new SMSPacket();
288: smsPacket.setEncodingType(encodingType);
289: if (port != null) {
290: smsPacket.setPort(Integer.parseInt(port));
291: } else {
292: smsPacket.setPort(0);
293: }
294: smsPacket.setTimeStamp(sendtime);
295:
296: /*
297: * Set target address.
298: */
299: smsPacket.setAddress(dmsg.getAddress());
300:
301: /*
302: * Set sender's phone number
303: */
304: smsPacket.setPhoneNumber(phoneNumber);
305:
306: smsPacket.setMessageLength(buffer.length);
307: smsPacket.setMessage(buffer);
308:
309: debug("SMS PACKET: clientHost = "
310: + InetAddress.getByName(clientHost));
311: debug("SMS PACKET: portOut = " + portOut);
312:
313: debug("SMS PACKET: encoding type = " + encodingType);
314: debug("SMS PACKET: port = " + port);
315: debug("SMS PACKET: timestamp = " + sendtime);
316: debug("SMS PACKET: address = " + dmsg.getAddress());
317: debug("SMS PACKET: sender's phone number = " + phoneNumber);
318: debug("SMS PACKET: message length = " + buffer.length);
319: debug("SMS PACKET: message:" + new String(buffer));
320: byte[] buf = smsPacket.getData();
321:
322: mess.setData(buf, 0, buf.length);
323: dgc.send(mess);
324: }
325:
326: /**
327: * Receives the bytes that have been sent over the connection, constructs a
328: * <code>Message</code> object, and returns it.
329: * <p>
330: * If there are no <code>Message</code>s waiting on the connection, this
331: * method will block until a message is received, or the
332: * <code>MessageConnection</code> is closed.
333: *
334: * @return a <code>Message</code> object
335: * @exception IOException if an I/O error occurs.
336: */
337: public synchronized Message receive() throws IOException {
338:
339: dgc.receive(mess);
340:
341: /* get message buffer and extract info */
342: byte[] buf = mess.getData();
343: SMSPacket packet = new SMSPacket(buf);
344: int msgType = packet.getEncodingType();
345: int smsPort = packet.getPort();
346: long time = packet.getTimeStamp();
347: String address = packet.getAddress();
348: String phoneNumber = packet.getPhoneNumber();
349: int msgLen = packet.getMessageLength();
350: byte[] messg = packet.getMessage(msgLen);
351:
352: debug("SMS PACKET: encodingType = " + msgType);
353: debug("SMS PACKET: port = " + smsPort);
354: debug("SMS PACKET: time = " + time);
355: debug("SMS PACKET: address = " + address);
356: debug("SMS PACKET: Sender's phone number = " + phoneNumber);
357: debug("SMS PACKET: message length = " + msgLen);
358: debug("SMS PACKET: message:" + new String(messg));
359:
360: Message msg = null;
361: if (msgType == MessageTransportConstants.GSM_TEXT
362: || msgType == MessageTransportConstants.GSM_UCS2) {
363:
364: TextMessage textmsg = (TextMessage) newMessage(
365: MessageConnection.TEXT_MESSAGE, address);
366:
367: /* Always store text messages as UCS 2 bytes. */
368: if (msgType == MessageTransportConstants.GSM_TEXT) {
369: messg = TextEncoder.decode(messg);
370: }
371: ((TextObject) textmsg).setPayloadData(messg);
372: msg = textmsg;
373: } else {
374: BinaryMessage binmsg = (BinaryMessage) newMessage(
375: MessageConnection.BINARY_MESSAGE, address);
376: ((BinaryObject) binmsg).setPayloadData(messg);
377: msg = binmsg;
378: }
379: ((MessageObject) msg).setTimeStamp(time);
380:
381: return msg;
382: }
383:
384: /**
385: * Closes the connection. Reset the connection is open flag
386: * so methods can be checked to throws an appropriate exception
387: * for operations on a closed connection.
388: *
389: * @exception IOException if an I/O error occurs.
390: */
391: public void close() throws IOException {
392:
393: if (open) {
394: dgc.close();
395: dgc = null;
396: open = false;
397: }
398: }
399:
400: /**
401: * Show a debug message.
402: *
403: * @param text The text to be displayed after a mandatory prefix.
404: */
405: private void debug(String text) {
406: System.out.println("SMSMessageConnection: " + text);
407: }
408:
409: }
|