I am trying to send a message. Connection with firebase xmpp server is done. I am using latest smack 4.2.
public myClient(String senderId, String serverKey) throws IOException, NotConnectedException, InterruptedException {
Roster.setRosterLoadedAtLoginDefault(false);
Jid jid ;
jid = JidCreate.from(HOST);
final XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
.setCompressionEnabled(false)
.setSendPresence(false)
.setConnectTimeout(10000)
.setHost(HOST)
.setDebuggerEnabled(false)
.setPort(PORT)
.setXmppDomain((DomainBareJid) jid)
.setSocketFactory(SSLSocketFactory.getDefault())
.setUsernameAndPassword(senderId + "#gcm.googleapis.com", serverKey)
.build();
this.conn = new MyXMPPTCPConnection(conf);
try {
conn.connect();
conn.login();
} catch (XMPPException | InterruptedException | SmackException e) {
throw new IOException(e);
}
Roster roster = Roster.getInstanceFor(conn);
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
System.out.println(entry);
}
Entity jEntity ;
Jid jid1 = JidCreate.from("text");
EntityBareJid jid21 = JidCreate.entityBareFrom("ak#gcm.googleapis.com");
Chat chat = ChatManager.getInstanceFor(conn)
.createChat(jid21);
Message newMessage = new Message();
String t = "hello this is a test message";
newMessage.setBody(t.toString());
/* ((Object) chat).send(newMessage);*/
chat.sendMessage(newMessage);
StanzaFilter filter = new AndFilter(new StanzaTypeFilter(Message.class));
//PacketCollector myCollector = conn2.createPacketCollector(filter);
// Normally, you'd do something with the collector, like wait for new packets.
StanzaListener myListener = new StanzaListener() {
#Override
public void processPacket(Stanza packet) throws SmackException.NotConnectedException {
System.out.println("packet = [" + packet + "]");
}
};
conn.addAsyncStanzaListener(myListener, filter);
while (true) {
}
//conn2.disconnect();
}
When I am running this method got the following error
packet = [hello
this is a test
message25667738-6dd1-4c74-a64b-2fdbac74339bInvalidJson :
MissingPayload]
Jul 26, 2017 12:52:46 PM
org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader parsePackets
INFO: MyXMPPTCPConnection[438277974557#gcm.googleapis.com/41CA77F0]
(0) received closing element. Server wants to terminate the
connection, calling disconnect()
Please review and suggest.
Thanks in advance.
The FCM XMPP Server uses JSON syntax. So you need to use the systax as described for FCM payload in this documentation. Since XMPP uses XML, you need to embed the JSON payload in an xml node with the name gcm as follows:
<gcm xmlns:google:mobile:data>
JSON payload
</gcm>
For e.g, your JSON payload may look like this:
{
"to":"APA91bEXJvW4jUdxxxxx-xxxxxxxx",
"data":{"message":"Hello"},
"priority":"high"
}
For complete details on using the XMPP Connection Server using GCM/FCM, take a look at this documentation.
Using Smack you can create a class with the GcmPacketExtension to create the gcm element with the json payload.
Related
I am using SendGrid API v3 for Java. It works and does the job. However, if the sender is, say, hello#world.org, the recipient sees only that very hello#world.org. What I try to accomplish is that the recipient sees also a simple name (for example, Hello World <hello#world.org>) like this:
(Above, note that the actual address is noreply#k..., yet it is preceded with Kela Fpa.)
How can I do that programmatically?
Without your code, it's hard to suggest exactly what to do, but according to their API documentation, the endpoint does in fact support an optional 'name' attribute for the sender
Taking a further glance at their Java API's source code, it looks like the example, which is this:
import com.sendgrid.*;
import java.io.IOException;
public class Example {
public static void main(String[] args) throws IOException {
Email from = new Email("test#example.com");
String subject = "Sending with SendGrid is Fun";
Email to = new Email("test#example.com");
Content content = new Content("text/plain", "and easy to do anywhere, even with Java");
Mail mail = new Mail(from, subject, to, content);
SendGrid sg = new SendGrid(System.getenv("SENDGRID_API_KEY"));
Request request = new Request();
try {
request.setMethod(Method.POST);
request.setEndpoint("mail/send");
request.setBody(mail.build());
Response response = sg.api(request);
System.out.println(response.getStatusCode());
System.out.println(response.getBody());
System.out.println(response.getHeaders());
} catch (IOException ex) {
throw ex;
}
}
}
Using the source code, you can supply a "name" to the Email constructor, as seen here
Could be re-worked into this:
import com.sendgrid.*;
import java.io.IOException;
public class Example {
public static void main(String[] args) throws IOException {
Email from = new Email("test#example.com", "John Doe");
String subject = "Sending with SendGrid is Fun";
Email to = new Email("test#example.com", "Jane Smith");
Content content = new Content("text/plain", "and easy to do anywhere, even with Java");
Mail mail = new Mail(from, subject, to, content);
SendGrid sg = new SendGrid(System.getenv("SENDGRID_API_KEY"));
Request request = new Request();
try {
request.setMethod(Method.POST);
request.setEndpoint("mail/send");
request.setBody(mail.build());
Response response = sg.api(request);
System.out.println(response.getStatusCode());
System.out.println(response.getBody());
System.out.println(response.getHeaders());
} catch (IOException ex) {
throw ex;
}
}
}
Note, the email constructors being changed.
If you're not using the Mail helper class for some reason, please let me know and I can re-work an example maybe.
I've implemented XMPP server using Smack library, my server gets messages from Google Cloud Messaging server (now it is Firebase), but the problem is when I send one by one message from android to gcm server, my XMPP server receives only first message and the second is intercepted, (I can see only notification that there was a message
<message id="gQaM0-6"><gcm xmlns="google:mobile:data">{"message_type":"ack","message_id":"0","to":"eVtypIWW7Q8:APA91bH5oU0AC3zyuCAWVYkMzoGQeIiGe71c2BL4lE5uFHRfB3iPXtD-qIJDmJZ3ySsPDi0VhkKl0Cz3XZG7rWa1Ca7pX9yQqzWSMXBiGK4SEO4Q-Owfr45E_VBJMrXqsSziuJhek"}</gcm></message>
but I don't have data in this
and first message I get in method void processPacket(Packet packet)
here is the full code of XMPP server:
public class XMPPServer implements PacketListener {
private static XMPPServer sInstance = null;
private XMPPConnection connection;
private ConnectionConfiguration config;
private String mApiKey = null;
private String mProjectId = null;
private boolean mDebuggable = false;
private String fcmServerUsername = null;
public static XMPPServer getInstance() {
if (sInstance == null) {
throw new IllegalStateException("You have to prepare the client first");
}
return sInstance;
}
public static XMPPServer prepareClient(String projectId, String apiKey, boolean debuggable) {
synchronized (XMPPServer.class) {
if (sInstance == null) {
sInstance = new XMPPServer(projectId, apiKey, debuggable);
}
}
return sInstance;
}
private XMPPServer(String projectId, String apiKey, boolean debuggable) {
this();
mApiKey = apiKey;
mProjectId = projectId;
mDebuggable = debuggable;
fcmServerUsername = mProjectId + "#" + Util.FCM_SERVER_CONNECTION;
}
private XMPPServer() {
// Add GcmPacketExtension
ProviderManager.getInstance().addExtensionProvider(Util.FCM_ELEMENT_NAME, Util.FCM_NAMESPACE,
new PacketExtensionProvider() {
#Override
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
String json = parser.nextText();
GcmPacketExtension packet = new GcmPacketExtension(json);
return packet;
}
});
}
/**
* Connects to FCM Cloud Connection Server using the supplied credentials
*/
public void connect() throws XMPPException {
config = new ConnectionConfiguration(Util.FCM_SERVER, Util.FCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setSocketFactory(SSLSocketFactory.getDefault());
// Launch a window with info about packets sent and received
config.setDebuggerEnabled(mDebuggable);
connection = new XMPPConnection(config);
connection.connect();
connection.addConnectionListener(new ConnectionListener() {
//a few overrided methods
});
// Handle incoming packets (the class implements the PacketListener)
connection.addPacketListener(this, new PacketTypeFilter(Message.class));
// Second message without data I get in this method (1)
connection.addPacketWriterInterceptor(new PacketInterceptor() {
#Override
public void interceptPacket(Packet packet) {
System.out.println("INTERCEPT PACKAGE: " + packet.toXML());
}
}, new PacketTypeFilter(Message.class));
connection.login(fcmServerUsername, mApiKey);
}
/**
* Normal message with my data I get in this method (2)
*/
#SuppressWarnings("unchecked")
#Override
public void processPacket(Packet packet) {
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket = (GcmPacketExtension) incomingMessage.getExtension(Util.FCM_NAMESPACE);
String json = gcmPacket.getJson();
System.out.println("Message : " + json);
}
There is almost the whole code, the most important part I marked with (1) and (2), (use search to find quickly)
Why can I receive only first message with my data ?
And why does the second message go to PacketInterceptor (mark (1) ) ?
If you're using Firebase Cloud Messaging(FCM), check if your app server is connected to the following endpoints:
// Production
fcm-xmpp.googleapis.com:5235
// Testing
fcm-xmpp.googleapis.com:5236
In addition to that, you may want to also check Downstream messages wherein it was mentioned that once the XMPP connection is established, CCS and your server use normal XMPP <message> stanzas to send JSON-encoded messages back and forth. The body of the <message> must be:
<gcm xmlns:google:mobile:data>
JSON payload
</gcm>
Also, note of the exceptions in JSON payload for regular FCM messages. Visit the given links for more information.
These related SO posts might also help:
FCM receive message Issue
Send FCM messages from server side to android device
How can I get last 20 emails from gmail?
ListMessagesResponse listMessagesResponse = mService.users().messages()
.list(user).setQ("from:----")
.execute();
Currently I am using this but it returns message IDs without any payload. I want to get full email with body. Can it be like get the latest email from specific sender?
As #trajchevska pointed out you can only get the basic details with your code. To get messages in full format you should call mService.users().messages().get(user, messageId).execute() for every message. Best way for this is to create a batch call. So if you want to get all the messages that match specified query you should do something like this.
final ListMessagesResponse response = mService.users().messages().list(user).setQ("from:----").execute();
final List<Message> messages = new ArrayList<Message>();
while (response.getMessages() != null) {
messages.addAll(response.getMessages());
if (response.getNextPageToken() != null) {
String pageToken = response.getNextPageToken();
response = service.users().messages().list(user).setQ("from:----").setPageToken(pageToken).execute();
} else {
break;
}
}
final List<Message> fullMessages = new ArrayList<>();
final JsonBatchCallback<Message> callback = new JsonBatchCallback<Message>() {
public void onSuccess(Message message, HttpHeaders responseHeaders) {
fullMessages.add(message);
}
public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
// do what you want if error occurs
}
};
BatchRequest batch = mService.batch();
for (Message message : messages) {
mService.users().messages().get(user, message.getId()).setFormat("full").queue(batch, callback);
}
batch.execute();
Hope this helps.
The list function only returns the list of messages with basic details which is usually the id only. If you want to get the payload or other message details you need to iterate through all messages pulled with list and call the wanted function specifically on the selected object. I only have some basic knowledge in Java, but the logic would be something like this:
messages = listMessagesResponse.getMessages();
for (Message message : messages) {
payload = message.getPayload();
...
}
Check their docs, they have some example that can be helpful.
List Messages
Get Concrete Message
I want to develop a SOAP client using CXF to connect to SharePoint. The authentication scheme is NTLM.
I am blocked on a scenario where the logged-in user of a machine (on which the SOAP client is being run) has access to SharePoint. The CXF soap client always uses the logged-in user. I want to specify some other user credentials (not the logged-in).
As CXF uses in-JDK HttpURLConnection; and what I have read about HttpURLConnection is, it bypasses the specified credentials when the logged-in user is NTLM authenticated.
Codes were tried on CXF version 2.7.11.
Solutions that I have tried out:
1) Setting Conduit authorization
String username = "user";
String password = "password";
JaxWsProxyfactoryBean factory1 = new JaxWsProxyfactoryBean();
factory1.setServiceClass(WebsSoap.class);
factory1.setAddress(url);
factory1.setUsername(username);
factory1.setPassword(password);
WebsSoap service = (WebsSoap) factory1.create();
Client client = ClientProxy.getClient(service);
HTTPconduit conduit = (HTTPconduit) client.getconduit();
conduit.getAuthorization().setAuthorizationType("NTLM");
conduit.getAuthorization().setUserName(username);
conduit.getAuthorization().setPassword(password);
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
conduit.setClient(httpClientPolicy);
service.getWeb(".");
Problem:
This does not work for the scenario specified above, as it always uses the logged-in credentials. And when I specify invalid credentials, it does not fail.
2) AsyncHTTPConduit
Another solution is to use AsyncHTTPConduit that uses HttpAsyncClient instead of HttpURLConnection. This is beacuse HTTP components do not bypass specified credentials and logged-in user can be ignored (I have successfully verified this with a test client using HttpClient).
Below is the code snippet::
Bus bus = BusFactory.getDefaultBus();
bus.setProperty( "use.async.http.conduit", "true" );
Client client = ClientProxy.getClient( service );
HTTPConduit http = (HTTPConduit)client.getConduit();
if ( http instanceof AsyncHTTPConduit ) {
AsyncHTTPConduit conduit = (AsyncHTTPConduit)http;
DefaultHttpAsyncClient defaultHttpAsyncClient;
try {
defaultHttpAsyncClient = conduit.getHttpAsyncClient();
}
catch ( IOException exception ) {
throw new RuntimeException( exception );
}
defaultHttpAsyncClient.getCredentialsProvider().setCredentials( AuthScope.ANY,
new NTCredentials( "username", "password", "", "domain" ) );
conduit.getClient().setAllowChunking( false );
conduit.getClient().setAutoRedirect( true );
}
Problem:
Above code throws error:
Authorization loop detected on conduit.
The above code snapshot shows the usage of DefaultHttpAsyncClient which is deprecated now and CloseableHttpAsyncClient is to be used instead. But CloseableHttpAsyncClient does not provide a way to specify credentials to an already existing CloseableHttpAsyncClient object. Not sure how to use CloseableHttpAsyncClient in this scenario.
3) Other solutions
The other solution that I tried out is to use sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback, to bypass logged-in user authentication, as mentioned here. Use this approach along with solution #1 mentioned above. This works as expected for valid/invalid credentials, and the code bypasses the logged-in credentials :). But when I specify invalid credentials, I do not get HTTP 401 error, instead I get
Could not send message, server reached max retries 20
I am trying to avoid this solution because it uses java’s internal package and there is no way to determine HTTP 401 error directly.
What can I do to arrive at a complete solution?
Try this interceptor. This will avoid automatic authentication.
public class DisableAutomaticNTLMAuthOutInterceptor extends AbstractPhaseInterceptor<Message>
{
private boolean isFieldsAvailable;
private Field tryTransparentNTLMProxyField;
private Field tryTransparentNTLMServerField;
public DisableAutomaticNTLMAuthOutInterceptor() {
super(Phase.PRE_STREAM);
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Void run() {
try {
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMServerField = HttpURLConnection.class.getDeclaredField("tryTransparentNTLMServer");
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMServerField.setAccessible(true);
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMProxyField = HttpURLConnection.class.getDeclaredField("tryTransparentNTLMProxy");
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMProxyField.setAccessible(true);
DisableAutomaticNTLMAuthOutInterceptor.this.isFieldsAvailable = true;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
}
#Override
public void handleMessage(final Message message) throws Fault {
if (this.isFieldsAvailable)
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Void run() {
try {
Object httpConnection = message.get("http.connection");
if (httpConnection != null) {
DisableAutomaticNTLMAuthOutInterceptor.this.processHttpConnection(message.get("http.connection"));
}
} catch (Throwable t) {
t.printStackTrace();
}
return null;
}
});
}
private void processHttpConnection(Object httpConnection) throws IllegalArgumentException, IllegalAccessException {
if (HttpURLConnection.class.isAssignableFrom(httpConnection.getClass())) {
tryTransparentNTLMServerField.set(httpConnection, Boolean.FALSE);
tryTransparentNTLMProxyField.set(httpConnection, Boolean.FALSE);
} else {
Field tempField = null;
for (Field field : httpConnection.getClass().getDeclaredFields()) {
if (HttpURLConnection.class.isAssignableFrom(field.getType())) {
field.setAccessible(true);
tempField = field;
break;
}
}
if (tempField != null) {
processHttpConnection(tempField.get(httpConnection));
}
}
}
}
I have just started netty and I am really disappointed with the documentation present on
their website.
I am trying to connect to an URL using Netty.. I took the time client example from their website and changed it as per my requirement..
Code :
public class NettyClient {
public static void main(String[] args) throws Exception {
String host = "myUrl.com/v1/parma?param1=value";
int port = 443;
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ClientHandler());
ch.pipeline().addLast("encoder", new HttpRequestEncoder());
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}
But the problem is that that it expects only the url without the query parameters.. How can I pass query parameters with the URL?
and please provide me some link of a good documentation for Netty 4..
EDIT
Client code after referring the example mentioned in the answer :
URI uri = new URI("myUrl.com/v1/parma?param1=value");
String scheme = uri.getScheme() == null? "http" : uri.getScheme();
String host = "myUrl.com";
int port = 443;
boolean ssl = "https".equalsIgnoreCase(scheme);
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new NettyClientInitializer(ssl));
// Make the connection attempt.
Channel ch = b.connect(host, port).sync().channel();
// Prepare the HTTP request.
HttpRequest request = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
request.headers().set(HttpHeaders.Names.HOST, host);
request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
//request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
/*// Set some example cookies.
request.headers().set(
HttpHeaders.Names.COOKIE,
ClientCookieEncoder.encode(
new DefaultCookie("my-cookie", "foo"),
new DefaultCookie("another-cookie", "bar")));
*/
// Send the HTTP request.
ch.writeAndFlush(request);
// Wait for the server to close the connection.
ch.closeFuture().sync();
} finally {
// Shut down executor threads to exit.
group.shutdownGracefully();
}
handler code :
public class ClientHandler extends SimpleChannelInboundHandler<HttpObject> {
#Override
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpResponse) {
HttpResponse response = (HttpResponse) msg;
System.out.println("STATUS: " + response.getStatus());
System.out.println("VERSION: " + response.getProtocolVersion());
System.out.println();
if (!response.headers().isEmpty()) {
for (String name: response.headers().names()) {
for (String value: response.headers().getAll(name)) {
System.out.println("HEADER: " + name + " = " + value);
}
}
System.out.println();
}
if (HttpHeaders.isTransferEncodingChunked(response)) {
System.out.println("CHUNKED CONTENT {");
} else {
System.out.println("CONTENT {");
}
}
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
System.out.print(content.content().toString(CharsetUtil.UTF_8));
System.out.flush();
if (content instanceof LastHttpContent) {
System.out.println("} END OF CONTENT");
}
}
}
#Override
public void exceptionCaught(
ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
initializer code :
public class NettyClientInitializer extends ChannelInitializer<SocketChannel> {
private final boolean ssl;
public NettyClientInitializer(boolean ssl) {
this.ssl = ssl;
}
#Override
public void initChannel(SocketChannel ch) throws Exception {
// Create a default pipeline implementation.
ChannelPipeline p = ch.pipeline();
p.addLast("log", new LoggingHandler(LogLevel.INFO));
// Enable HTTPS if necessary.
/*
if (ssl) {
SSLEngine engine =
SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
p.addLast("ssl", new SslHandler(engine));
}
*/
p.addLast("codec", new HttpClientCodec());
// Remove the following line if you don't want automatic content decompression.
// p.addLast("inflater", new HttpContentDecompressor());
// Uncomment the following line if you don't want to handle HttpChunks.
p.addLast("aggregator", new HttpObjectAggregator(1048576));
p.addLast("handler", new ClientHandler());
}
}
Your code only handles the low-level connection at the moment. Indeed at this level only the hostname and port can be used.
For the HTTP request You have to construct an HttpRequest object and send it over the channel. In this request object You define the query parameters and all such things.
There is a bunch of example code about HTTP client functionality on Netty website - have a a look!
In this example the problem lies with the constructor for the DefaultHttpRequest parameter of uri.getRawPath(). The invocation of this method does NOT return the query parameters. It works in this case as there were no query parameters in the Snoop example. By substituting uri.toASCIIString() returns the encoded uri complete with the query parameters. To prove this to yourself, rather than having a method invocation within a method invocation (a bad idea for just this reason, add the statement
String url = uri.getRawPath();
and look at the string url.
I had the exact same problem. I've done this natively in servlets for years but now was trying to do it in a Netty app.
Consequently the new code would be:
String path = uri.toASCIIString();
// Prepare the HTTP request.
HttpRequest request = new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, path);
When you build the request, you need to add the query to the path. Instead of
uri.getRawPath()
use
uri.getRawPath() + "?" + uri.getRawQuery()