I am trying to publish a simple message to ibmmq using java.The message sending is successful.But when i check the queue on ibm console .The Message is showed as
But I am expecting as simple String.
Here is my code.when I am trying to convert I am getting below message
The message of type jms_bytes can not have its body assigned to java.lang.String
import com.ibm.mq.*;
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.jms.MQQueueConnectionFactory;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.wmq.WMQConstants;
import javax.jms.*;
import java.io.IOException;
import java.util.Hashtable;
public class PublisherTest
{
static private String CHANNEL = "anychannel";
static private int PORT = 1414;
static private String HOST = localhost;
static private String QMANAGER = "QM1";
static private String QUEUE = "queue.test";
static private String USER = USER;
static private Hashtable<String, Object> props =
new Hashtable<String, Object>();
static MQQueueManager qMgr = null;
static private void putMsgOnQueue(String message) {
// Disabling IBM cipher suite mapping due to
// using Oracle Java and not IBM Java
System.setProperty("com.ibm.mq.cfg.useIBMCipherMappings", "false");
// Enabling SSL debug to view the communication
System.setProperty("javax.net.debug", "ssl:handshake");
props.put(MQConstants.CHANNEL_PROPERTY, CHANNEL);
props.put(MQConstants.PORT_PROPERTY, PORT);
props.put(MQConstants.HOST_NAME_PROPERTY, HOST);
props.put(MQConstants.USER_ID_PROPERTY, USER);
props.put(MQConstants.PASSWORD_PROPERTY, PASSWORD);
props.put(MQConstants.SSL_CIPHER_SUITE_PROPERTY, "TLS_RSA_WITH_AES_256_CBC_SHA256");
try {
qMgr = new MQQueueManager(QMANAGER, props);
// MQOO_OUTPUT = Open the queue to put messages
// MQOO_INPUT_AS_Q_DEF = Using queue-defined defaults
int openOptions = MQConstants.MQOO_OUTPUT;
// creating destination
MQQueue queue = qMgr.accessQueue(QUEUE, openOptions);
// specify the message options...
MQPutMessageOptions pmo = new MQPutMessageOptions(); // Default
// MQPMO_ASYNC_RESPONSE = MQPUT operation is completed without the
// application waiting for the queue manager to complete the call
// Using this option can improve messaging performance,
// particularly for applications using client bindings.
pmo.options = MQConstants.MQPMO_ASYNC_RESPONSE;
// create message
MQMessage mqMessage = new MQMessage();
System.out.println("Writing message to queue: " + QUEUE);
mqMessage.writeString(message.toString());
// Put message on queue
queue.put(mqMessage, pmo);
// Close queue
queue.close();
// Get status
MQAsyncStatus asyncStatus = qMgr.getAsyncStatus();
// Print status code (0 = successful)
System.out.println(asyncStatus.reasonCode);
} catch (MQException e) {
System.out.println("The connection to MQ could not be established." + e.getMessage());
} catch (IOException e) {
System.out.println("Error while writing message." +
e.getMessage());
} finally {
try {
qMgr.disconnect();
} catch (MQException e) {
System.out.println("The connection could not be closed." +
e.getMessage());
}
}
}
public static void main(String[] args) {
putMsgOnQueue("WELCOME");
}
}
ANY Help Would be Appreciated.
The default message format is MQFMT_NONE. This means the message body consists of bytes. Your code is not setting message format. So my thinking is that MQ Console is indicating such messages as binary.
Suggest you set the message format as string and run. This sets the message format as string.
MQMessage mqMessage = new MQMessage();
mqMessage.format = MQConstants.MQFMT_STRING;
Related
Good Day,
I am struggling with below error when trying to connect to IBM MQ. Unable to access createContext(); and forces me to use (JMSContext) cf.createConnection(); which is resulting me error as below:
"Exception in thread "main" java.lang.ClassCastException: com.ibm.mq.jms.MQConnection cannot be cast to javax.jms.JMSContext
at pushmsgs.main(pushmsgs.java:55)"
import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.wmq.WMQConstants;
import javax.jms.*;
public class pushmsgs {
// System exit status value (assume unset value to be 1)
private static int status = 1;
// Create variables for the connection to MQ
private static final String HOST = "22.188.133.100"; // Host name or IP address
private static final int PORT = 3415; // Listener port for your queue manager
private static final String CHANNEL = "DEV.APP.SVRCONN"; // Channel name
private static final String QMGR = "SITQUEUEMGR"; // Queue manager name
// private static final String APP_USER = "app"; // User name that application uses to connect to MQ
//private static final String APP_PASSWORD = "_APP_PASSWORD_"; // Password that the application uses to connect to MQ
private static final String QUEUE_NAME = "TESTQUEUE.MQAPP.REQ.RCV"; // Queue that the application uses to put and get messages to and from
/**
* Main method
*
* #param args
*/
public static void main(String[] args) {
// Variables
JMSContext context = null;
Destination destination = null;
JMSProducer producer = null;
JMSConsumer consumer = null;
try {
// Create a connection factory
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
// Set the properties
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, HOST);
cf.setIntProperty(WMQConstants.WMQ_PORT, PORT);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, CHANNEL);
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, QMGR);
// cf.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, "JmsPutGet (JMS)");
//cf.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
// cf.setStringProperty(WMQConstants.USERID, APP_USER);
//cf.setStringProperty(WMQConstants.PASSWORD, APP_PASSWORD);
//cf.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, "*TLS12");
// Create JMS objects
context = (JMSContext) cf.createConnection();
destination = context.createQueue("queue:///" + QUEUE_NAME);
long uniqueNumber = System.currentTimeMillis() % 1000;
TextMessage message = context.createTextMessage("Your lucky number today is " + uniqueNumber);
producer = context.createProducer();
producer.send(destination, message);
System.out.println("Sent message:\n" + message);
context.close();
recordSuccess();
} catch (JMSException jmsex) {
recordFailure(jmsex);
}
System.exit(status);
} // end main()
/**
* Record this run as successful.
*/
private static void recordSuccess() {
System.out.println("SUCCESS");
status = 0;
return;
}
/**
* Record this run as failure.
*
* #param ex
*/
private static void recordFailure(Exception ex) {
if (ex != null) {
if (ex instanceof JMSException) {
processJMSException((JMSException) ex);
} else {
System.out.println(ex);
}
}
System.out.println("FAILURE");
status = -1;
return;
}
/**
* Process a JMSException and any associated inner exceptions.
*
* #param jmsex
*/
private static void processJMSException(JMSException jmsex) {
System.out.println(jmsex);
Throwable innerException = jmsex.getLinkedException();
if (innerException != null) {
System.out.println("Inner exception(s):");
}
while (innerException != null) {
System.out.println(innerException);
innerException = innerException.getCause();
}
return;
}
}
Added JMS mvn dependency and com.ibm.mq.allclient dependency
It's because you are assigning the wrong object to the wrong variable.
context = (JMSContext) cf.createConnection();
I don't know why you would want to do that. A connection is not a context.
It should be:
Connection conn = cf.createConnection("MyUserId", "mypassword");
See my example called MQTestJMS51 here.
I am new in MQTT world. I have written a code to subscribe a topic and get message from topic and store it in database. Now my problem is how to put this code on server so that it will keep receiving message infinitely. I am trying to create a scheduler but in that case i am Getting Persistence Already in Use error from MQTT. I cannot change the clientId every time it connect. It is a fixed one in my case. Is there any way to get the persistence object which is already connected for a particular clientId?
Please help. Thanks and advance.
Please Find the code subscribe topic and messageArrived method of mqqt to get message from topic
public class AppTest {
private MqttHandler handler;
public void doApp() {
// Read properties from the conf file
Properties props = MqttUtil.readProperties("MyData/app.conf");
String org = props.getProperty("org");
String id = props.getProperty("appid");
String authmethod = props.getProperty("key");
String authtoken = props.getProperty("token");
// isSSL property
String sslStr = props.getProperty("isSSL");
boolean isSSL = false;
if (sslStr.equals("T")) {
isSSL = true;
}
// Format: a:<orgid>:<app-id>
String clientId = "a:" + org + ":" + id;
String serverHost = org + MqttUtil.SERVER_SUFFIX;
handler = new AppMqttHandler();
handler.connect(serverHost, clientId, authmethod, authtoken, isSSL);
// Subscribe Device Events
// iot-2/type/<type-id>/id/<device-id>/evt/<event-id>/fmt/<format-id>
handler.subscribe("iot-2/type/" + MqttUtil.DEFAULT_DEVICE_TYPE
+ "/id/+/evt/" + MqttUtil.DEFAULT_EVENT_ID + "/fmt/json", 0);
}
/**
* This class implements as the application MqttHandler
*
*/
private class AppMqttHandler extends MqttHandler {
// Pattern to check whether the events comes from a device for an event
Pattern pattern = Pattern.compile("iot-2/type/"
+ MqttUtil.DEFAULT_DEVICE_TYPE + "/id/(.+)/evt/"
+ MqttUtil.DEFAULT_EVENT_ID + "/fmt/json");
DatabaseHelper dbHelper = new DatabaseHelper();
/**
* Once a subscribed message is received
*/
#Override
public void messageArrived(String topic, MqttMessage mqttMessage)
throws Exception {
super.messageArrived(topic, mqttMessage);
Matcher matcher = pattern.matcher(topic);
if (matcher.matches()) {
String payload = new String(mqttMessage.getPayload());
// Parse the payload in Json Format
JSONObject contObj = new JSONObject(payload);
System.out
.println("jsonObject arrived in AppTest : " + contObj);
// Call method to insert data in database
dbHelper.insertIntoDB(contObj);
}
}
}
Code to connect to client
public void connect(String serverHost, String clientId, String authmethod,
String authtoken, boolean isSSL) {
// check if client is already connected
if (!isMqttConnected()) {
String connectionUri = null;
//tcp://<org-id>.messaging.internetofthings.ibmcloud.com:1883
//ssl://<org-id>.messaging.internetofthings.ibmcloud.com:8883
if (isSSL) {
connectionUri = "ssl://" + serverHost + ":" + DEFAULT_SSL_PORT;
} else {
connectionUri = "tcp://" + serverHost + ":" + DEFAULT_TCP_PORT;
}
if (client != null) {
try {
client.disconnect();
} catch (MqttException e) {
e.printStackTrace();
}
client = null;
}
try {
client = new MqttClient(connectionUri, clientId);
} catch (MqttException e) {
e.printStackTrace();
}
client.setCallback(this);
// create MqttConnectOptions and set the clean session flag
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
options.setUserName(authmethod);
options.setPassword(authtoken.toCharArray());
//If SSL is used, do not forget to use TLSv1.2
if (isSSL) {
java.util.Properties sslClientProps = new java.util.Properties();
sslClientProps.setProperty("com.ibm.ssl.protocol", "TLSv1.2");
options.setSSLProperties(sslClientProps);
}
try {
// connect
client.connect(options);
System.out.println("Connected to " + connectionUri);
} catch (MqttException e) {
e.printStackTrace();
}
}
}
I am trying to send sms using JAVA. After googling, I found out that SMPP protocol is to be used for it and stumbled upon the below source code.
public class SendSMS
{
public static void main(String[] args) throws Exception
{
SendSMS obj = new SendSMS();
SendSMS.sendTextMessage("<mobile number>");
}
private TimeFormatter tF = new AbsoluteTimeFormatter();
/*
* This method is used to send SMS to for the given MSISDN
*/
public void sendTextMessage(String MSISDN)
{
// bind param instance is created with parameters for binding with SMSC
BindParameter bP = new BindParameter(
BindType.BIND_TX,
"<user_name>",
"<pass_word>",
"<SYSTEM_TYPE>",
TypeOfNumber.UNKNOWN,
NumberingPlanIndicator.UNKNOWN,
null);
SMPPSession smppSession = null;
try
{
// smpp session is created using the bindparam and the smsc ip address/port
smppSession = new SMPPSession("<SMSC_IP_ADDRESS>", 7777, bP);
}
catch (IOException e1)
{
e1.printStackTrace();
}
// Sample TextMessage
String message = "This is a Test Message";
GeneralDataCoding dataCoding = new GeneralDataCoding(false, true,
MessageClass.CLASS1, Alphabet.ALPHA_DEFAULT);
ESMClass esmClass = new ESMClass();
try
{
// submitShortMessage(..) method is parametrized with necessary
// elements of SMPP submit_sm PDU to send a short message
// the message length for short message is 140
smppSession.submitShortMessage(
"CMT",
TypeOfNumber.NATIONAL,
NumberingPlanIndicator.ISDN,
"<MSISDN>",
TypeOfNumber.NATIONAL,
NumberingPlanIndicator.ISDN,
MSISDN,
esmClass,
(byte) 0,
(byte) 0,
tF.format(new Date()),
null,
new RegisteredDelivery(SMSCDeliveryReceipt.DEFAULT),
(byte) 0,
dataCoding,
(byte) 0,
message.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
But the problem I encounter with the source code is that it requires specific set of parameters like user_name, pass_word, system_type, SMSC IP address etc which I have no clue of. I have only recently known about the SMPP protocol and so am unaware of how to get this code working to fulfil my usecase of sending sms to my mobile. So can someone please help me get this code to work or guide me to a place where i can learn about doing this?
I've been working on SMPP project recently.
The library I used for SMPP protocol is OpenSMPP.
Here is the example of my class for building and sending SMPP data
public class SmppTransport implements Transport {
#Override
public void send(String url, Map<String, String> map) throws IOException {
int smscPort = Integer.parseInt(map.get("port"));
String smscHost = map.get("send_url");
String smscUsername = map.get("username");
String smscPassword = map.get("password");
String recipientPhoneNumber = map.get("phone_num");
String messageText = map.get("text");
try {
SubmitSM request = new SubmitSM();
// request.setSourceAddr(createAddress(senderPhoneNumber)); // you can skip this
request.setDestAddr(createAddress(recipientPhoneNumber));
request.setShortMessage(messageText);
// request.setScheduleDeliveryTime(deliveryTime); // you can skip this
request.setReplaceIfPresentFlag((byte) 0);
request.setEsmClass((byte) 0);
request.setProtocolId((byte) 0);
request.setPriorityFlag((byte) 0);
request.setRegisteredDelivery((byte) 1); // we want delivery reports
request.setDataCoding((byte) 0);
request.setSmDefaultMsgId((byte) 0);
Session session = getSession(smscHost, smscPort, smscUsername, smscPassword);
SubmitSMResp response = session.submit(request);
} catch (Throwable e) {
// error
}
}
private Session getSession(String smscHost, int smscPort, String smscUsername, String smscPassword) throws Exception{
if(sessionMap.containsKey(smscUsername)) {
return sessionMap.get(smscUsername);
}
BindRequest request = new BindTransmitter();
request.setSystemId(smscUsername);
request.setPassword(smscPassword);
// request.setSystemType(systemType);
// request.setAddressRange(addressRange);
request.setInterfaceVersion((byte) 0x34); // SMPP protocol version
TCPIPConnection connection = new TCPIPConnection(smscHost, smscPort);
// connection.setReceiveTimeout(BIND_TIMEOUT);
Session session = new Session(connection);
sessionMap.put(smscUsername, session);
BindResponse response = session.bind(request);
return session;
}
private Address createAddress(String address) throws WrongLengthOfStringException {
Address addressInst = new Address();
addressInst.setTon((byte) 5); // national ton
addressInst.setNpi((byte) 0); // numeric plan indicator
addressInst.setAddress(address, Data.SM_ADDR_LEN);
return addressInst;
}
}
And my operator gave me this parameters for SMPP. There are many configuration options but these are essential
#host = 192.168.10.10 // operator smpp server ip
#port = 12345 // operator smpp server port
#smsc-username = "my_user"
#smsc-password = "my_pass"
#system-type = ""
#source-addr-ton = 5
#source-addr-npi = 0
So if you want to test your code without registering with GSM service provider, you can simulate SMPP server on your computer. SMPPSim is a great project for testing. Download it and run on your computer. It can be configured in multiple ways e.g. request delivery reports from SMPP server, set sms fail ratio and e.t.c. I've tested SMPPSim on linux.
Use following code for single class execution:
public class SmppTransport {
static Map sessionMap=new HashMap<String,String>();
String result=null;
public String send(String url, Map<String, String> map) throws Exception {
int smscPort = Integer.parseInt(map.get("port"));
String smscHost = map.get("send_url");
String smscUsername = map.get("username");
String smscPassword = map.get("password");
String recipientPhoneNumber = map.get("phone_num");
String messageText = map.get("text");
try {
SubmitSM request = new SubmitSM();
// request.setSourceAddr(createAddress(senderPhoneNumber)); // you can skip this
request.setDestAddr(createAddress(recipientPhoneNumber));
request.setShortMessage(messageText);
// request.setScheduleDeliveryTime(deliveryTime); // you can skip this
request.setReplaceIfPresentFlag((byte) 0);
request.setEsmClass((byte) 0);
request.setProtocolId((byte) 0);
request.setPriorityFlag((byte) 0);
request.setRegisteredDelivery((byte) 1); // we want delivery reports
request.setDataCoding((byte) 0);
request.setSmDefaultMsgId((byte) 0);
Session session = getSession(smscHost, smscPort, smscUsername, smscPassword);
SubmitSMResp response = session.submit(request);
result=new String(response.toString());
} catch (Exception e) {
result=StackTraceToString(e);
}
return result;
}
private Session getSession(String smscHost, int smscPort, String smscUsername, String smscPassword) throws Exception{
if(sessionMap.containsKey(smscUsername)) {
return (Session) sessionMap.get(smscUsername);
}
BindRequest request = new BindTransmitter();
request.setSystemId(smscUsername);
request.setPassword(smscPassword);
request.setSystemType("smpp");
// request.setAddressRange(addressRange);
request.setInterfaceVersion((byte) 0x34); // SMPP protocol version
TCPIPConnection connection = new TCPIPConnection(smscHost, smscPort);
// connection.setReceiveTimeout(BIND_TIMEOUT);
Session session = new Session(connection);
sessionMap.put(smscUsername, session.toString());
BindResponse response = session.bind(request);
return session;
}
private Address createAddress(String address) throws WrongLengthOfStringException {
Address addressInst = new Address();
addressInst.setTon((byte) 5); // national ton
addressInst.setNpi((byte) 0); // numeric plan indicator
addressInst.setAddress(address, Data.SM_ADDR_LEN);
return addressInst;
}
public String StackTraceToString(Exception err) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
err.printStackTrace(pw);
return sw.toString();
}
public String sendSMS(String Port, String Host,String SMPPUserName,String SMPPPassword,String Phone_Number,String Message) throws Exception {
String response=null;
sessionMap.put("port",Port);
sessionMap.put("send_url",Host);
sessionMap.put("username",SMPPUserName);
sessionMap.put("password",SMPPPassword);
sessionMap.put("phone_num",Phone_Number);
sessionMap.put("text",Message);
Set set=sessionMap.entrySet();//Converting to Set so that we can traverse
Iterator itr=set.iterator();
while(itr.hasNext()){
Map.Entry entry=(Map.Entry)itr.next();
}
SmppTransport test =new SmppTransport();
try {
response=test.send("10.50.**.**", sessionMap);
System.out.println(response);
} catch (Exception e) {
response=StackTraceToString(e);
}
return response;
}
public static void main(String[] args) throws Exception
{
SmppTransport sm=new SmppTransport();
String test=sm.sendSMS("80*6", "10.50.**.**", "f***obi", "x***fQ", "+9187965*****", "Testing1");
System.out.println("Data: "+test);
}}
Use this simulator here,
It acts as a service provide, after build and test your application on it you have to change just config parameters(username, password, ip, port, ...) that provided to you by the service provider .
you can find all configurations to connect to this simulator in conf file.
SMPP is a protocol between mobile network operators/carriers and content providers. The fields you specified (username, password, SMSC IP) are provisioned from the operators. Unfortunately, unless you work for a content provider company, or have a deal with an operator, you are unlikely to get these details.
Simulators can let you test out your SMPP code but they will not actually deliver content to your phone.
My best advice if you want to send SMS from your Java app would be to use an SMS API like Twilio's.
I am somewhat new to Google Cloud Messaging. We have been working with it for a couple of months but just recently we have been getting "Connection Draining" messages. When this happens all communication stops.
Google says: https://developer.android.com/google/gcm/ccs.html#response
When you receive a CONNECTION_DRAINING message, you should
immediately begin sending messages to another CCS connection, opening
a new connection if necessary. You should, however, keep the original
connection open and continue receiving messages that may come over the
connection (and ACKing them)—CCS will handle initiating a connection
close when it is ready.
My question is
If I open a new connection manually, how does it know what connection to use if I don't close the existing connection?
If 6 messages are sent concurrently how do I stop the method from opening 6 connections? Or am I confused on this?
Why does connection draining happen?
I am surprised this isn't already put into play in their example code. It seems like its pretty much everything you need. Is it already done for me in the code and I am missing it?
I don't have a main method in my code, I user servlets as triggers instead. My connection is initailized like this
#PostConstruct
public void init() throws Exception{
try {
smackCcsClient.connect(Long.parseLong(env.getProperty("gcm.api")), env.getProperty("gcm.key"));
}catch (IOException e ){
e.printStackTrace();
}catch(SmackException e){
e.printStackTrace();
}catch(XMPPException e){
e.printStackTrace();
}
}
however after this I never touch the connection again. Am I handling this wrong, is the connection something I should be touching more frequently or something I need to keep track of?
_______________________ADDED AFTER THE QUESTION_________________________
I added a connection inside of their example code to try to reinitialize a connection. It looks like this:
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
//Open new connection because old connection will be closing or is already closed.
try {
connect(Long.parseLong(env.getProperty("gcm.api")), env.getProperty("gcm.key"));
} catch (XMPPException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SmackException e) {
e.printStackTrace();
}
} else {
logger.log(Level.INFO, "Unrecognized control type: %s. This could happen if new features are " + "added to the CCS protocol.",
controlType);
}
I have written a code for handling such cases(basically diverting new downstream messages to a new connection)... not thoroughly tested...
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import javax.net.ssl.SSLSocketFactory;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import com.fasterxml.jackson.core.type.TypeReference;
/**
* Based on https://developer.android.com/google/gcm/ccs.html#smack
*
* #author Abhinav.Dwivedi
*
*/
public class SmackCcsClient implements CcsClient {
private static final Logger logger = LoggerFactory.getLogger(SmackCcsClient.class);
private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5235;
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
private static volatile SmackCcsClient instance;
static {
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE, new PacketExtensionProvider() {
#Override
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
private final Deque<Channel> channels;
public static SmackCcsClient instance() {
if (instance == null) {
synchronized (SmackCcsClient.class) {
if (instance == null) {
instance = new SmackCcsClient();
}
}
}
return instance;
}
private SmackCcsClient() {
channels = new ConcurrentLinkedDeque<Channel>();
channels.addFirst(connect());
}
private class Channel {
private XMPPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it will not accept any new downstream
* messages.
*/
private volatile boolean connectionDraining = false;
/**
* Sends a packet with contents provided.
*/
private void send(String jsonRequest) throws NotConnectedException {
Packet request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendPacket(request);
}
private void handleControlMessage(Map<String, Object> jsonObject) {
logger.debug("handleControlMessage(): {}", jsonObject);
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
} else {
logger.info("Unrecognized control type: {}. This could happen if new features are "
+ "added to the CCS protocol.", controlType);
}
}
}
/**
* Sends a downstream message to GCM.
*
*/
#Override
public void sendDownstreamMessage(String message) throws Exception {
Channel channel = channels.peekFirst();
if (channel.connectionDraining) {
synchronized (channels) {
channel = channels.peekFirst();
if (channel.connectionDraining) {
channels.addFirst(connect());
channel = channels.peekFirst();
}
}
}
channel.send(message);
logger.debug("Message Sent via CSS: ({})", message);
}
/**
* Handles an upstream data message from a device application.
*
*/
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
// PackageName of the application that sent this message.
String category = (String) jsonObject.get("category");
String from = (String) jsonObject.get("from");
#SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
logger.info("Message received from device: category ({}), from ({}), payload: ({})", category, from,
JsonUtil.toJson(payload));
}
/**
* Handles an ACK.
*
* <p>
* Logs a INFO message, but subclasses could override it to properly handle ACKs.
*/
public void handleAckReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.debug("handleAckReceipt() from: {}, messageId: {}", from, messageId);
}
/**
* Handles a NACK.
*
* <p>
* Logs a INFO message, but subclasses could override it to properly handle NACKs.
*/
protected void handleNackReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.debug("handleNackReceipt() from: {}, messageId: ", from, messageId);
}
/**
* Creates a JSON encoded ACK message for an upstream message received from an application.
*
* #param to
* RegistrationId of the device who sent the upstream message.
* #param messageId
* messageId of the upstream message to be acknowledged to CCS.
* #return JSON encoded ack.
*/
protected static String createJsonAck(String to, String messageId) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("message_type", "ack");
message.put("to", to);
message.put("message_id", messageId);
return JsonUtil.toJson(message);
}
/**
* Connects to GCM Cloud Connection Server using the supplied credentials.
*
* #return
*/
#Override
public Channel connect() {
try {
Channel channel = new Channel();
ConnectionConfiguration config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
channel.connection = new XMPPTCPConnection(config);
channel.connection.connect();
channel.connection.addConnectionListener(new LoggingConnectionListener());
// Handle incoming packets
channel.connection.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
logger.debug("Received: ({})", packet.toXML());
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket = (GcmPacketExtension) incomingMessage.getExtension(GCM_NAMESPACE);
String json = gcmPacket.getJson();
try {
Map<String, Object> jsonObject = JacksonUtil.DEFAULT.mapper().readValue(json,
new TypeReference<Map<String, Object>>() {});
// present for ack, nack and control, null otherwise
Object messageType = jsonObject.get("message_type");
if (messageType == null) {
// Normal upstream data message
handleUpstreamMessage(jsonObject);
// Send ACK to CCS
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
String ack = createJsonAck(from, messageId);
channel.send(ack);
} else if ("ack".equals(messageType.toString())) {
// Process Ack
handleAckReceipt(jsonObject);
} else if ("nack".equals(messageType.toString())) {
// Process Nack
handleNackReceipt(jsonObject);
} else if ("control".equals(messageType.toString())) {
// Process control message
channel.handleControlMessage(jsonObject);
} else {
logger.error("Unrecognized message type ({})", messageType.toString());
}
} catch (Exception e) {
logger.error("Failed to process packet ({})", packet.toXML(), e);
}
}
}, new PacketTypeFilter(Message.class));
// Log all outgoing packets
channel.connection.addPacketInterceptor(new PacketInterceptor() {
#Override
public void interceptPacket(Packet packet) {
logger.debug("Sent: {}", packet.toXML());
}
}, new PacketTypeFilter(Message.class));
channel.connection.login(ExternalConfig.gcmSenderId() + "#gcm.googleapis.com", ExternalConfig.gcmApiKey());
return channel;
} catch (Exception e) {
logger.error(Logging.FATAL, "Error in creating channel for GCM communication", e);
throw new RuntimeException(e);
}
}
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static final class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
this.json = json;
}
public String getJson() {
return json;
}
#Override
public String toXML() {
return String.format("<%s xmlns=\"%s\">%s</%s>", GCM_ELEMENT_NAME, GCM_NAMESPACE,
StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
public Packet toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
private static final class LoggingConnectionListener implements ConnectionListener {
#Override
public void connected(XMPPConnection xmppConnection) {
logger.info("Connected.");
}
#Override
public void authenticated(XMPPConnection xmppConnection) {
logger.info("Authenticated.");
}
#Override
public void reconnectionSuccessful() {
logger.info("Reconnecting..");
}
#Override
public void reconnectionFailed(Exception e) {
logger.error("Reconnection failed.. ", e);
}
#Override
public void reconnectingIn(int seconds) {
logger.info("Reconnecting in {} secs", seconds);
}
#Override
public void connectionClosedOnError(Exception e) {
logger.info("Connection closed on error.");
}
#Override
public void connectionClosed() {
logger.info("Connection closed.");
}
}
}
I'm also new in GCM and facing the same problem...I solved it by creating new SmackCcsClient() on CONNECTION_DRAINING message. Older connection should still exists and receiving messages, but not sending because:
protected volatile boolean connectionDraining = true;
Google says that connection will be closed by CCS:
CCS will handle initiating a connection close when it is ready.
Until connection is closed by CCS you will be able to receive messages from both connections, but able to send messages with just new one. When old connection is closed it should be destroyed, I'm not sure if garbage collector is called or not...trying to solve this issue
P.S.: I'm not 100% sure with this answer, but maybe it will open more space for discussion.
I just pushed the code for the FCM Connection Draining to my FCM XMPP Server example.
Project:
XMPP Connection Server for FCM using the latest version of the Smack library (4.2.2) + Connection Draining Implementation.
GitHub link: https://github.com/carlosCharz/fcmxmppserverv2
Youtube link: https://youtu.be/KVKEj6PeLTc
If you had some problems check my troubleshooting section. Hope you can find it useful. Greetings!
This is one of the most common application scenario that can be found all over the net. and I'm not asking any questions about the java codes that I did because I was successful in running it on my laptop where both the client and server part of the .java file resides. Rather I have had problem getting it to work in between two computers. I tried establishing physical connection using cross-over cable to connect two computers, and did a test to see if file transfers successfully and it did, however, keeping one Server part of the .java file in one computer and client part in the other, I tried to run the server first and then the client but it got a "access denied" error.
For reference here's my two .java files:
/* ChatClient.java */
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ChatClient {
private static int port = 5000; /* port to connect to */
private static String host = "localhost"; /* host to connect to (server's IP)*/
private static BufferedReader stdIn;
private static String nick;
/**
* Read in a nickname from stdin and attempt to authenticate with the
* server by sending a NICK command to #out. If the response from #in
* is not equal to "OK" go bacl and read a nickname again
*/
private static String getNick(BufferedReader in,
PrintWriter out) throws IOException {
System.out.print("Enter your nick: ");
String msg = stdIn.readLine();
out.println("NICK " + msg);
String serverResponse = in.readLine();
if ("SERVER: OK".equals(serverResponse)) return msg;
System.out.println(serverResponse);
return getNick(in, out);
}
public static void main (String[] args) throws IOException {
Socket server = null;
try {
server = new Socket(host, port);
} catch (UnknownHostException e) {
System.err.println(e);
System.exit(1);
}
stdIn = new BufferedReader(new InputStreamReader(System.in));
/* obtain an output stream to the server... */
PrintWriter out = new PrintWriter(server.getOutputStream(), true);
/* ... and an input stream */
BufferedReader in = new BufferedReader(new InputStreamReader(
server.getInputStream()));
nick = getNick(in, out);
/* create a thread to asyncronously read messages from the server */
ServerConn sc = new ServerConn(server);
Thread t = new Thread(sc);
t.start();
String msg;
/* loop reading messages from stdin and sending them to the server */
while ((msg = stdIn.readLine()) != null) {
out.println(msg);
}
}
}
class ServerConn implements Runnable {
private BufferedReader in = null;
public ServerConn(Socket server) throws IOException {
/* obtain an input stream from the server */
in = new BufferedReader(new InputStreamReader(
server.getInputStream()));
}
public void run() {
String msg;
try {
/* loop reading messages from the server and show them
* on stdout */
while ((msg = in.readLine()) != null) {
System.out.println(msg);
}
} catch (IOException e) {
System.err.println(e);
}
}
}
and here's the ChatServer.java:
/* ChatServer.java */
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Hashtable;
public class ChatServer {
private static int port = 5000; /* port to listen on */
public static void main (String[] args) throws IOException
{
ServerSocket server = null;
try {
server = new ServerSocket(port); /* start listening on the port */
} catch (IOException e) {
System.err.println("Could not listen on port: " + port);
System.err.println(e);
System.exit(1);
}
Socket client = null;
while(true) {
try {
client = server.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.err.println(e);
System.exit(1);
}
/* start a new thread to handle this client */
Thread t = new Thread(new ClientConn(client));
t.start();
}
}
}
class ChatServerProtocol {
private String nick;
private ClientConn conn;
/* a hash table from user nicks to the corresponding connections */
private static Hashtable<String, ClientConn> nicks =
new Hashtable<String, ClientConn>();
private static final String msg_OK = "OK";
private static final String msg_NICK_IN_USE = "NICK IN USE";
private static final String msg_SPECIFY_NICK = "SPECIFY NICK";
private static final String msg_INVALID = "INVALID COMMAND";
private static final String msg_SEND_FAILED = "FAILED TO SEND";
/**
* Adds a nick to the hash table
* returns false if the nick is already in the table, true otherwise
*/
private static boolean add_nick(String nick, ClientConn c) {
if (nicks.containsKey(nick)) {
return false;
} else {
nicks.put(nick, c);
return true;
}
}
public ChatServerProtocol(ClientConn c) {
nick = null;
conn = c;
}
private void log(String msg) {
System.err.println(msg);
}
public boolean isAuthenticated() {
return ! (nick == null);
}
/**
* Implements the authentication protocol.
* This consists of checking that the message starts with the NICK command
* and that the nick following it is not already in use.
* returns:
* msg_OK if authenticated
* msg_NICK_IN_USE if the specified nick is already in use
* msg_SPECIFY_NICK if the message does not start with the NICK command
*/
private String authenticate(String msg) {
if(msg.startsWith("NICK")) {
String tryNick = msg.substring(5);
if(add_nick(tryNick, this.conn)) {
log("Nick " + tryNick + " joined.");
this.nick = tryNick;
return msg_OK;
} else {
return msg_NICK_IN_USE;
}
} else {
return msg_SPECIFY_NICK;
}
}
/**
* Send a message to another user.
* #recepient contains the recepient's nick
* #msg contains the message to send
* return true if the nick is registered in the hash, false otherwise
*/
private boolean sendMsg(String recipient, String msg) {
if (nicks.containsKey(recipient)) {
ClientConn c = nicks.get(recipient);
c.sendMsg(nick + ": " + msg);
return true;
} else {
return false;
}
}
/**
* Process a message coming from the client
*/
public String process(String msg) {
if (!isAuthenticated())
return authenticate(msg);
String[] msg_parts = msg.split(" ", 3);
String msg_type = msg_parts[0];
if(msg_type.equals("MSG")) {
if(msg_parts.length < 3) return msg_INVALID;
if(sendMsg(msg_parts[1], msg_parts[2])) return msg_OK;
else return msg_SEND_FAILED;
} else {
return msg_INVALID;
}
}
}
class ClientConn implements Runnable {
private Socket client;
private BufferedReader in = null;
private PrintWriter out = null;
ClientConn(Socket client) {
this.client = client;
try {
/* obtain an input stream to this client ... */
in = new BufferedReader(new InputStreamReader(
client.getInputStream()));
/* ... and an output stream to the same client */
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.err.println(e);
return;
}
}
public void run() {
String msg, response;
ChatServerProtocol protocol = new ChatServerProtocol(this);
try {
/* loop reading lines from the client which are processed
* according to our protocol and the resulting response is
* sent back to the client */
while ((msg = in.readLine()) != null) {
response = protocol.process(msg);
out.println("SERVER: " + response);
}
} catch (IOException e) {
System.err.println(e);
}
}
public void sendMsg(String msg) {
out.println(msg);
}
}
Now, what should I do in order to run this two files from two computers given that I have the physical connection(TCP/IP) setup already??
Thanks in advance... :)
Sounds like it's quite possibly a firewall problem. Have you tried opening a hole in your firewall for port 1001?
Have you also looked at your java.policy and make sure that it is configured to allow local codebase to open sockets?
as mentioned in comment, you should not use port < 1025 for you applications, since they are always used in deamon processes. However you should test your program like this
1) if you get connection refused then you should check the exception properly, whether client program takes time before generating exception ( that mean request is going to server and then it's giving connection refused), in that case you should try java.policy put following in a file named java.policy
grant {
permission java.net.SocketPermission ":1024-65535",
"connect,accept";
permission java.net.SocketPermission ":80", "connect";
permission java.io.FilePermission "", "read,write,delete";
permission java.security.SecurityPermission "";
};
while compiling use this flag -Djava.security.policy=java.policy
more-over you should also try -Djava.rmi.server.hostname=IP, where IP is clien-ip for client.java and server-ip for server.java
2) if you are immediately getting exception at client side then your request is not going outside your pc, so client has some problem.
check the exception properly and post them over here.
3) though i've not got access denied error, but it seems to have port problem that might be solved using policy or port>1024.
post what are you getting now.