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.
Related
I run a custom WebSocketServlet for Jetty, which sends short text push notifications (for an async mobile and desktop word game) to many platforms (Facebook, Vk.com, Mail.ru, Ok.ru also Firebase and Amazon messaging) using a Jetty HttpClient instance:
public class MyServlet extends WebSocketServlet {
private final SslContextFactory mSslFactory = new SslContextFactory();
private final HttpClient mHttpClient = new HttpClient(mSslFactory);
#Override
public void init() throws ServletException {
super.init();
try {
mHttpClient.start();
} catch (Exception ex) {
throw new ServletException(ex);
}
mFcm = new Fcm(mHttpClient); // Firebase
mAdm = new Adm(mHttpClient); // Amazon
mApns = new Apns(mHttpClient); // Apple
mFacebook = new Facebook(mHttpClient);
mMailru = new Mailru(mHttpClient);
mOk = new Ok(mHttpClient);
mVk = new Vk(mHttpClient);
}
This has worked very good for the past year, but since I have recently upgraded my WAR-file to use Jetty 9.4.14.v20181114 the trouble has begun -
public class Facebook {
private final static String APP_ID = "XXXXX";
private final static String APP_SECRET = "XXXXX";
private final static String MESSAGE_URL = "https://graph.facebook.com/%s/notifications?" +
// the app access token is: "app id | app secret"
"access_token=%s%%7C%s" +
"&template=%s";
private final HttpClient mHttpClient;
public Facebook(HttpClient httpClient) {
mHttpClient = httpClient;
}
private final BufferingResponseListener mMessageListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
LOG.warn("facebook failure: {}", result.getFailure());
return;
}
try {
// THE jsonStr SUDDENLY CONTAINS PREVIOUS CONTENT!
String jsonStr = getContentAsString(StandardCharsets.UTF_8);
LOG.info("facebook success: {}", jsonStr);
} catch (Exception ex) {
LOG.warn("facebook exception: ", ex);
}
}
};
public void postMessage(int uid, String sid, String body) {
String url = String.format(MESSAGE_URL, sid, APP_ID, APP_SECRET, UrlEncoded.encodeString(body));
mHttpClient.POST(url).send(mMessageListener);
}
}
Suddenly the getContentAsString method called for successful HttpClient invocations started to deliver the strings, which were fetched previously - prepended to the the actual result string.
What could it be please, is it some changed BufferingResponseListener behaviour or maybe some non-obvious Java quirk?
BufferingResponseListener was never intended to be reusable across requests.
Just allocate a new BufferingResponseListener for every request/response.
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 want to create ec2 instances when ever the new user arrive. I created a servlet class to do this. When User arrive i check DB that is user new or not if new then create the instance and send back his/her IP. When i send http request to this servlet one by one for users i get the IP correctly. But when i send HTTP Call in parallel (for user1 send request in tab1, for user2 send request in tab2 simultaneously before getting response from user1 HTTP call). When i do this i got error. Sometimes user1 said
"The instance ID 'i-0b79495934c3b5459' does not exist (Service:
AmazonEC2; Status Code: 400; Error Code: InvalidInstanceID.NotFound;
Request ID: e18a9eaa-cb1b-4130-a3ee-bf1b19fa184c) "
And user2 send IP in response. Kindly help me What is the issue and how to resolve this.
This is the Servlet Class which i created.
public class GateKeeperController extends HttpServlet {
private static final long serialVersionUID = 1L;
BasicAWSCredentials awsCreds = new BasicAWSCredentials(credentials);
AmazonEC2Client ec2Client = new AmazonEC2Client(awsCreds);
RunInstancesRequest runInstancesRequest;
RunInstancesResult runInstancesResult;
Reservation reservation;
Instance intstance;
DescribeInstancesRequest describeInstanceRequest;
DescribeInstancesResult describeInstanceResult;
GatekeeperModal gateKeepermodal;
String sourceAMI = null;
String destinationAMI = null;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession s = request.getSession();
String userID = (String) request.getParameter("userID");
Double lattitude = Double.parseDouble((String) request.getParameter("lat"));
Double lonitude = Double.parseDouble((String) request.getParameter("long"));
if (userID != null) {
Pair coordinates = new Pair(lattitude, lonitude);
RegionSelection targetRegion = new RegionSelection();
String regionResult = targetRegion.getRegion(coordinates);
String instanceIP = null;
gateKeepermodal = new GatekeeperModal();
try {
if (gateKeepermodal.checkUserIsNew(userID)) {
instanceIP = startInstance(userID, regionResult);
if (instanceIP != null) {
response.getWriter().write(instanceIP);
}
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
}
}
private String startInstance(String userID, String region) {
String ami_id = new AMI().getAMI_ID(region);
ec2Client.setEndpoint(region);
runInstancesRequest = new RunInstancesRequest();
runInstancesRequest.withImageId(ami_id).withInstanceType("t2.micro").withMinCount(1).withMaxCount(1)
.withKeyName("GateKeeper_User").withSecurityGroups("GateKeeper User");
runInstancesResult = ec2Client.runInstances(runInstancesRequest);
reservation = runInstancesResult.getReservation();
intstance = reservation.getInstances().get(0);
String s1 = intstance.getState().getName();
String s2 = InstanceStateName.Running.name();
while (!s1.toLowerCase().equals(s2.toLowerCase())) {
describeInstanceRequest = new DescribeInstancesRequest();
describeInstanceRequest.withInstanceIds(intstance.getInstanceId());
ec2Client.setEndpoint(region);
describeInstanceResult = ec2Client.describeInstances(describeInstanceRequest);
reservation = describeInstanceResult.getReservations().get(0);
intstance = reservation.getInstances().get(0);
s1 = intstance.getState().getName();
s2 = InstanceStateName.Running.name();
}
GateKeeperUser user = new GateKeeperUser(userID, intstance.getInstanceId(), intstance.getPublicIpAddress(),
region);
Boolean result;
try {
result = gateKeepermodal.createUser(user);
if (result) {
return intstance.getPublicIpAddress();
} else {
return null;
}
} catch (SQLException e) {
}
return null;
}
}
According to the documentation:
"If you successfully run the RunInstances command, and then
immediately run another command using the instance ID that was
provided in the response of RunInstances, it may return an
InvalidInstanceID.NotFound error. This does not mean the instance does
not exist. Some specific commands that may be affected are:
DescribeInstances: To confirm the actual state of the instance, run
this command using an exponential backoff algorithm.
TerminateInstances: To confirm the state of the instance, first run
the DescribeInstances command using an exponential backoff algorithm."
Major edit: 2015-05-27: After some degree of success updated on where I'm currently stuck rather than leaving a rambling post....could really do with some pointers on this one - a little bogged down....
I'm running some code on a Linux app server (WebSphere) that needs to authenticate to an IIS web service which is configured for "Integrated Authentication", but I'm having some problems forming the Authorization: Negotiate token.
I should also say that I need to put this token into the HTTP header for a JAX-WS SOAP request that I will subsequently build. I know my SOAP request itself works because we were using WS-Security Username token profile previously and it worked fine - trying to swap to kerberos is proving difficult...
My problem is with initSecContext I think. It appears that on the first call the context is configured in "some" way and there is some returned token data, but .isEstablished is false. The problem I'm having is putting the initSecContext call into a loop - it seems IIS just closes the connection when I do this. Can anyone give me some pointers - I seem to be taking the approach used by other posters and the Oracle samples (although the IBM/WebSphere sample only makes a single initSecContext call and doesn't check .isEstablished which seems odd to me based on the Oracle documentation).
Anyway, the error I get is below (note the Ready: property seems to clearly say initSecContext needs to loop - to me at least);
[5/27/15 6:51:11:605 UTC] 0000004f SystemOut O INFO: com.mycorp.kerberosKerberosTokenGenerator/getKerberosToken/run: After initSecContext:
--- GSSContext ---
Owner: domainuser#MYDOMAIN.COM
Peer: HTTP/iishost.mycorp.com
State: initialized
Lifetime: indefinite
Ready: no
Flags:
Confidentiality off
Delegation on
Integrity off
MutualAuthn on
ReplayDetection off
SequenceDetection off
DelegatedCred: unknown
--- End of GSSContext ---
[5/27/15 6:51:11:605 UTC] 0000004f SystemOut O INFO: com.mycorp.kerberosKerberosTokenGenerator/getKerberosToken/run: Context is not established, trying again
[5/27/15 6:51:11:606 UTC] 0000004f SystemOut O ERROR: com.mycorp.kerberosKerberosTokenGenerator/getKerberosToken/run: IOException during context establishment: Connection reset
My code is below;
LoginContext lc = getLoginContext(contextName);
final Subject subject = lc.getSubject();
String b64Token = (String) Subject.doAs(subject, new PrivilegedExceptionAction() {
#Override
public Object run() throws PrivilegedActionException, GSSException {
// Create socket to server
Socket socket;
DataInputStream inStream = null;
DataOutputStream outStream = null;
try {
socket = new Socket("iishost.mycorp.com", 443);
inStream = new DataInputStream(socket.getInputStream());
outStream = new DataOutputStream(socket.getOutputStream());
} catch (IOException ex) {
System.out.println("Exception setting up server sockets: " + ex.getMessage());
}
GSSName gssName = manager.createName(userName, GSSName.NT_USER_NAME, KRB5_MECH_OID);
GSSCredential gssCred = manager.createCredential(gssName.canonicalize(KRB5_MECH_OID),
GSSCredential.DEFAULT_LIFETIME,
KRB5_MECH_OID,
GSSCredential.INITIATE_ONLY);
gssCred.add(gssName, GSSCredential.INDEFINITE_LIFETIME,
GSSCredential.INDEFINITE_LIFETIME,
SPNEGO_MECH_OID,
GSSCredential.INITIATE_ONLY);
GSSName gssServerName = manager.createName(servicePrincipal, KERBEROS_V5_PRINCIPAL_NAME);
GSSContext clientContext = manager.createContext(gssServerName.canonicalize(SPNEGO_MECH_OID),
SPNEGO_MECH_OID,
gssCred,
GSSContext.DEFAULT_LIFETIME);
clientContext.requestCredDeleg(true);
clientContext.requestMutualAuth(true);
byte[] token = new byte[0];
while (!clientContext.isEstablished()) {
try {
token = clientContext.initSecContext(token, 0, token.length);
// IF I LOOK AT token HERE THERE IS CERTAINLY TOKEN DATA THERE - .isEstablished IS STILL FALSE
outStream.writeInt(token.length);
outStream.write(token);
outStream.flush();
// Check if we're done
if (!clientContext.isEstablished()) {
token = new byte[inStream.readInt()];
inStream.readFully(token);
}
} catch (IOException ex) {
// THIS EXCEPTION IS THROWN ON SECOND ITERATION - LOOKS LIKE IIS CLOSES THE CONNECTION
System.out.println("IOException during context establishment: " + ex.getMessage());
}
}
String b64Token = Base64.encode(token);
clientContext.dispose(); // I'm assuming this won't invalidate the token in some way as I need to use it later
return b64Token;
}
});
This doc tells me I don't need to loop on initSecContext, but .isEstablished returns false for me: http://www-01.ibm.com/support/knowledgecenter/SS7K4U_8.5.5/com.ibm.websphere.zseries.doc/ae/tsec_SPNEGO_token.html?cp=SS7K4U_8.5.5%2F1-3-0-20-4-0&lang=en
The Oracle docs tell me I should: https://docs.oracle.com/javase/7/docs/api/org/ietf/jgss/GSSContext.html
My only hesitation is that from the Oracle docs it seems like I'm starting the application conversation, but what I'm trying to do it obtain the token only & it's later on in my code when I will use JAX-WS to post my actual web service call (including the spnego/kerberos token in the http header) - is this the cause of my issue?
Just an update. I have this working now - my previous code was largely ok - it was just my understanding of how the Kerberos token would be added to the JAX-WS request. Turns out it's just a matter of attaching a Handler to the bindingProvider. The handler then obtains the Kerberos token and adds it to the header of the request - nice and easy.
Below is my working Handler which is added to the Handler chain obtained from a call to bindingProvider.getBinding().getHandlerChain()
public class HTTPKerberosHandler implements SOAPHandler<SOAPMessageContext> {
private final String contextName;
private final String servicePrincipal;
private static Oid KRB5_MECH_OID = null;
private static Oid SPNEGO_MECH_OID = null;
private static Oid KERBEROS_V5_PRINCIPAL_NAME = null;
final String className = this.getClass().getName();
static {
try {
KERBEROS_V5_PRINCIPAL_NAME = new Oid("1.2.840.113554.1.2.2.1");
KRB5_MECH_OID = new Oid("1.2.840.113554.1.2.2");
SPNEGO_MECH_OID = new Oid("1.3.6.1.5.5.2");
} catch (final GSSException ex) {
System.out.println("Exception creating mechOid's: " + ex.getMessage());
ex.printStackTrace();
}
}
public HTTPKerberosHandler(final String contextName, final String servicePrincipal) {
this.contextName = contextName;
this.servicePrincipal = servicePrincipal;
}
#Override
public Set<QName> getHeaders() {
return null;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return false;
}
#Override
public void close(MessageContext context) {
// No action
}
#Override
public boolean handleMessage(SOAPMessageContext context) {
if (((Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY))) {
return handleRequest(context);
} else {
return handleResponse(context);
}
}
private boolean handleRequest(SOAPMessageContext context) {
byte[] token = getKerberosToken(contextName, servicePrincipal);
HashMap<String, String> sendTransportHeaders = new HashMap<String, String>();
sendTransportHeaders.put("Authorization", "Negotiate " + Base64.encode(token));
context.put(com.ibm.websphere.webservices.Constants.REQUEST_TRANSPORT_PROPERTIES, sendTransportHeaders);
return true;
}
private boolean handleResponse(SOAPMessageContext context) {
logger.logInformation(className, "handleResponse", "Inbound response detected");
return true;
}
public byte[] getKerberosToken(final String contextName, final String servicePrincipal) {
try {
LoginContext lc = getLoginContext(contextName);
final Subject subject = lc.getSubject();
byte[] token = (byte[]) Subject.doAs(subject, new PrivilegedExceptionAction() {
#Override
public Object run() throws PrivilegedActionException, GSSException {
final String methodName = "getKerberosToken/run";
final GSSManager manager = GSSManager.getInstance();
Set<Principal> principals = subject.getPrincipals();
Iterator it = principals.iterator();
String principalName = ((Principal) it.next()).getName();
logger.logInformation(className, methodName, "Using principal: [" + principalName + "]");
GSSName gssName = manager.createName(principalName, GSSName.NT_USER_NAME, KRB5_MECH_OID);
GSSCredential gssCred = manager.createCredential(gssName.canonicalize(KRB5_MECH_OID),
GSSCredential.DEFAULT_LIFETIME,
KRB5_MECH_OID,
GSSCredential.INITIATE_ONLY);
gssCred.add(gssName, GSSCredential.INDEFINITE_LIFETIME,
GSSCredential.INDEFINITE_LIFETIME,
SPNEGO_MECH_OID,
GSSCredential.INITIATE_ONLY);
logger.logInformation(className, methodName, "Client TGT obtained: " + gssCred.toString());
GSSName gssServerName = manager.createName(servicePrincipal, GSSName.NT_USER_NAME);
GSSContext clientContext = manager.createContext(gssServerName.canonicalize(SPNEGO_MECH_OID),
SPNEGO_MECH_OID,
gssCred,
GSSContext.DEFAULT_LIFETIME);
logger.logInformation(className, methodName, "Service ticket obtained: " + clientContext.toString());
byte[] token = new byte[0];
token = clientContext.initSecContext(token, 0, token.length);
clientContext.dispose();
return token;
}
});
return token;
} catch (PrivilegedActionException ex) {
logger.logError(HTTPKerberosHandler.class.getName(), methodName, "PrivilegedActionException: " + ex.getMessage());
} catch (Exception ex) {
logger.logError(HTTPKerberosHandler.class.getName(), methodName, "Exception: " + ex.getMessage());
}
return null;
}
private LoginContext getLoginContext(String contextName) {
LoginContext lc = null;
try {
lc = new LoginContext(contextName);
lc.login();
} catch (LoginException le) {
logger.logError(HTTPKerberosHandler.class.getName(), methodName, "Login exception: [" + le.getMessage() + "]");
le.printStackTrace();
}
return lc;
}
}
http://www.androidhive.info/2014/10/android-building-group-chat-app-using-sockets-part-2/
Hi,
This is a tutorial about building a group chat app using socket programming. This app allows us to chat between multiple devices like android mobiles and web.
I want to send more than one "String" at a time to the server. I'm having trouble figuring that out.
The link to the tutorial where I downloaded the code is pasted above. I've already made the dynamic web page and I have it hosted on eapps.com At the very bottom of the this email is the edited code for the app. If you click the link above, you can see how I changed it.
The way it works is..
A web socket is created using WebSocketClient class and it has all the callback methods like onConnect, onMessage and onDisconnect.
In onMessage method parseMessage() is called to parse the JSON received from the socket server.
In parseMessage() method, the purpose of JSON is identified by reading the flag value.
When a new message is received, the message is added to list view data source and adapter.notifyDataSetChanged() is called to update the chat list.
sendMessageToServer() method is used to send the message from android device to socket server.
playBeep() method is called to play device’s default notification sound whenever a new message is received.
When you click the btnSend. it uses this method from the UtilsXd class. I've changed it a little in an attempt to pass an extra value.
public String getSendMessageJSONXD(String message, String whichPicIndex) {
String json = null;
try {
JSONObject jObj = new JSONObject();
jObj.put("flag", FLAG_MESSAGE);
jObj.put("sessionId", getSessionId());
jObj.put("message", message);
jObj.put("id", id);
json = jObj.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
First of all, what I still don't understand is, where did the values for
String sessionId = jObj.getString("sessionId");
and
String onlineCount = jObj.getString("onlineCount");
from this method
private void parseMessage(final String msg, String idINDEX) {
come from.
They were't added in the JSON object created in the UtilsXD class so how are they created?
That's not the problem I'm having. This is.
superString is the value I want to pass to dictate which picture to show.
superString = (sharedPrefs.getString("prefSyncAvatar", "1"));
You can change your picture in from the settings.
When a message is received, a switch/case statement changes the picture of/ for the message received according to the value passed by superString.
I should be able to sit there and just receive messages, and whatever number the user passes, the profilePicture should be set according to that number.
Here's where the problem begins.
This constructer builds a message based of the message that's just been parsed.
// Message m = new Message(fromName, message, isSelf);
Message m = new Message(fromName, message, isSelf, id, name,
image, status, profilePic, timeStamp, url);
In this method.
private void parseMessage(final String msg, String idINDEX) {
I can pass an value to the string "id" excluding the JSON I need it to.
String id = idINDEX;
this works,
String id = "0";
this works,
String id = utils.getPictureId();
this works,
String id = jObj.getString("id");
This doesn't work.
This is the error I'm getting.
org.json.JSONException: No value for id (this is the issue)
I've added the key/value pair
jObj.put("id", id);
in
public String getSendMessageJSONXD(String message, String whichPicIndex) {
but it's not coming though to the message.
Here's where I think the problem is.
The method onMessage, isn't can't take an extra parameter because it's from a library project. And I can't find that method to make a new constructor.
#Override
public void onMessage(String message) {
Log.d(TAG, String.format("Got string message! %s", message));
parseMessage(message, superString);
}
#Override
public void onMessage(byte[] data) {
Log.d(TAG, String.format("Got binary message! %s",
bytesToHex(data)));
String hello = "99";
parseMessage(bytesToHex(data), superString);
}
/////// Here's the final code below ////////
// JSON flags to identify the kind of JSON response
private static final String TAG_SELF = "self", TAG_NEW = "new",
TAG_MESSAGE = "message", TAG_ID = "id", TAG_EXIT = "exit";
#SuppressWarnings("deprecation")
#SuppressLint({ "NewApi", "CutPasteId" })
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_chat);
showUserSettings();
getActionBar().setTitle("City Chat - Beta 1.3");
superString = (sharedPrefs.getString("prefSyncAvatar", "1"));
listView = (ListView) findViewById(R.id.list_view_messages);
feedItems = new ArrayList<FeedItem>();
// We first check for cached request
vollewStuff();
//
//
// THis is where this fun begins
btnSend = (Button) findViewById(R.id.btnSend);
inputMsg = (EditText) findViewById(R.id.inputMsg);
listViewMessages = (ListView) findViewById(R.id.list_view_messages);
utils = new UtilsXD(getApplicationContext());
// Getting the person name from previous screen
Intent i = getIntent();
name = i.getStringExtra("name");
Integer.parseInt((sharedPrefs.getString("prefSyncAvatar", "1")));
btnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Sending message to web socket server
sendMessageToServer(utils.getSendMessageJSONXD(inputMsg
.getText().toString(), superString), superString);
utils.storePictureId((sharedPrefs.getString("prefSyncAvatar",
"1")));
// Clearing the input filed once message was sent
inputMsg.setText("");
}
});
listMessages = new ArrayList<Message>();
adapter = new MessagesListAdapter(this, listMessages, feedItems);
listViewMessages.setAdapter(adapter);
/**
* Creating web socket client. This will have callback methods
* */
client = new WebSocketClient(URI.create(WsConfig.URL_WEBSOCKET
+ URLEncoder.encode(name)), new WebSocketClient.Listener() {
#Override
public void onConnect() {
}
/**
* On receiving the message from web socket server
* */
#Override
public void onMessage(String message) {
Log.d(TAG, String.format("Got string message! %s", message));
parseMessage(message, superString);
// parseMessage(message,
// (sharedPrefs.getString("prefSyncAvatar", "1")));
}
#Override
public void onMessage(byte[] data) {
Log.d(TAG, String.format("Got binary message! %s",
bytesToHex(data)));
String hello = "99";
parseMessage(bytesToHex(data), superString);
// Message will be in JSON format
// parseMessage(bytesToHex(data),
// (sharedPrefs.getString("prefSyncAvatar", "1")));
}
/**
* Called when the connection is terminated
* */
#Override
public void onDisconnect(int code, String reason) {
String message = String.format(Locale.US,
"Disconnected! Code: %d Reason: %s", code, reason);
showToast(message);
//
// clear the session id from shared preferences
utils.storeSessionId(null);
}
#Override
public void onError(Exception error) {
Log.e(TAG, "Error! : " + error);
// showToast("Error! : " + error);
showToast("Are you sure you want to leave?");
}
}, null);
client.connect();
}
/**
* Method to send message to web socket server
* */
private void sendMessageToServer(String message, String id) {
if (client != null && client.isConnected()) {
client.send(message);
client.send(id);
}
}
/**
* Parsing the JSON message received from server The intent of message will
* be identified by JSON node 'flag'. flag = self, message belongs to the
* person. flag = new, a new person joined the conversation. flag = message,
* a new message received from server. flag = exit, somebody left the
* conversation.
* */
private void parseMessage(final String msg, String idINDEX) {
try {
jObj = new JSONObject(msg);
// JSON node 'flag'
String flag = jObj.getString("flag");
String id = idINDEX;
// if flag is 'self', this JSON contains session id
if (flag.equalsIgnoreCase(TAG_SELF)) {
String sessionId = jObj.getString("sessionId");
// Save the session id in shared preferences
utils.storeSessionId(sessionId);
Log.e(TAG, "Your session id: " + utils.getSessionId());
} else if (flag.equalsIgnoreCase(TAG_NEW)) {
// If the flag is 'new', new person joined the room
String name = jObj.getString("name");
String message = jObj.getString("message");
// number of people online
String onlineCount = jObj.getString("onlineCount");
showToast(name + message + ". Currently " + onlineCount
+ " people online!");
} else if (flag.equalsIgnoreCase(TAG_MESSAGE)) {
// if the flag is 'message', new message received
String fromName = name;
String message = jObj.getString("message");
String sessionId = jObj.getString("sessionId");
// switch (Integer.parseInt((sharedPrefs.getString(
// "prefSyncAvatar", "1"))))
boolean isSelf = true;
switch (Integer.parseInt(utils.getPictureId())) {
case 1:
profilePic = "http://clxxxii.vm-host.net/clxxxii/citychatlion.png";
break;
case 2:
profilePic = "http://clxxxii.vm-host.net/clxxxii/citychatmatt.png";
break;
case 3:
profilePic = "http://clxxxii.vm-host.net/clxxxii/citychatroboman.png";
break;
case 4:
profilePic = "http://clxxxii.vm-host.net/clxxxii/citychatalien.png";
break;
case 5:
profilePic = "http://clxxxii.vm-host.net/clxxxii/citychatkitty.png";
break;
case 10:
profilePic = "http://clxxxii.vm-host.net/clxxxii/citychatkitty.png";
break;
}
// Checking if the message was sent by you
if (!sessionId.equals(utils.getSessionId())) {
fromName = jObj.getString("name");
// profilePic = jObj.getString("profilePic");
//
//
//
//
jObj.getString("message");
isSelf = false;
profilePic = "http://clxxxii.vm-host.net/clxxxii/citychatalien.png";
}
// profilePic =
// "http://clxxxii.vm-host.net/clxxxii/citychatlion.png";
Integer.parseInt(utils.getPictureId());
String name = "clxxxii";
String image = "http://i.huffpost.com/gen/1716876/thumbs/o-ATLANTA-TRAFFIC-facebook.jpg";
String status = "status";
String timeStamp = "1403375851930";
String url = "url";
// Message m = new Message(fromName, message, isSelf);
Message m = new Message(fromName, message, isSelf, id, name,
image, status, profilePic, timeStamp, url);
// Appending the message to chat list
appendMessage(m);
} else if (flag.equalsIgnoreCase(TAG_EXIT)) {
// If the flag is 'exit', somebody left the conversation
String name = jObj.getString("name");
String message = jObj.getString("message");
showToast(name + message);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
///////// I've updated the socket server from the first project. /////// I've added the JSON value "id" successfully /// But I how do I change the value without having to type in "5" please see below..
//
This is the JSONutilty method I changed.
//
public String getSendAllMessageJson(String sessionId, String fromName,
String message, String photoId) {
String json = null;
try {
JSONObject jObj = new JSONObject();
jObj.put("flag", FLAG_MESSAGE);
jObj.put("sessionId", sessionId);
jObj.put("name", fromName);
jObj.put("message", message);
jObj.put("id", photoId);
json = jObj.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
}
This is the method that is being used by SocketServer. I can successfully send a message from this activity, to the utility method to send over the network.
// Normal chat conversation message
json = jsonUtils //
.getSendAllMessageJson(sessionId, name, message, "5");
How can I retrieve a value sent over the network to place in spot where I have "5" instead of hard coding it?
Thanks!!!
jobj doesn't have the value id. Example of the JSON object looks like this:
{
"message": " joined conversation!",
"flag": "new",
"sessionId": "4",
"name": "Ravi Tamada",
"onlineCount": 6
}
(as shown in the part1 of the same tutorial).
That solves the first issue of onlineCount and sessionId.
Thanks #miselking, you where correct, The server was missing the JSON string "id". To actually solve the problem of post.
"I want to send more than one "String" at a time to the server. I'm having trouble figuring that out."
This is how you do it.
Step 1)
Adding the string photoId to the method
public String getSendAllMessageJson(String sessionId, String fromName,
String message, String photoId) {
String json = null;
try {
JSONObject jObj = new JSONObject();
jObj.put("flag", FLAG_MESSAGE);
jObj.put("sessionId", sessionId);
jObj.put("name", fromName);
jObj.put("message", message);
jObj.put("id", photoId);
json = jObj.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
}
Step 2)
Add the string to the correct sting to the method.
String json = null;
json = jsonUtils
.getSendAllMessageJson(sessionId, name, message,
photoId);
Step 3) Parse the JSON
try {
JSONObject jObj = new JSONObject(message);
msg = jObj.getString("message");
photoId = jObj.getString("id");
} catch (JSONException e) {
e.printStackTrace();
}