Currently I am playing around with the new Smack 4.1 that offers full support for android. Sending and receiving messages is no problem, works fine. But now, I get stuck on sending and receiving Files. For example:
Send File
public void sendFile(String fileName,String to){
if(transferManager==null) {
transferManager = FileTransferManager.getInstanceFor(mConnection);
}
OutgoingFileTransfer transfer = transferManager.createOutgoingFileTransfer(to);
try {
transfer.sendFile(new File(fileName), "This is a Test!");
} catch (SmackException e) {
e.printStackTrace();
}
}
Receive files
public void setReceiveFileListener(){
if(transferManager==null) {
transferManager = FileTransferManager.getInstanceFor(mConnection);
}
transferManager.addFileTransferListener(new FileTransferListener() {
#Override
public void fileTransferRequest(FileTransferRequest request) {
IncomingFileTransfer transfer = request.accept();
try {
File file = new File(Environment.getExternalStorageDirectory() + File.separator +"TEST"+File.separator+ "new.txt");
transfer.recieveFile(file);
} catch (SmackException | IOException e) {
e.printStackTrace();
}
}
});
}
In this scenario, I just send a text file and want to save it as a new text file at sd card as "new.txt". The problem is not with the file itself, the file exists. Also, the receiver id is correct, it´s like "user#host.com/Smack" . That´s the same receiver which I can send normal messages successfully.
The error code I get from Smack:
error 503 - service unavailable
This error respone I get directly from smack after sending file and it seems the file is not transferred, because the receiver listener shows nothing (I made Logs on both, sending and receiving), but listener is surely registerred.
But I am totally sure, that File Transfer is supported by the server, it´s declared at their website www.coderollers.com . I´ve read many questions about here in SO and also at the Smack Developer Community. Nothing helped, so my question here is:
What could be the cause of this problem?
Could it be because I have to change the Port?
How to change the port of an existing connection in smack?
Any alternatives to file transport?
My normal port is 5222, that works fine to send messages. I hope someone get it work and can lead me to the right directions....thanks for helping!
SOLUTION
For all who are interested: Devendra Singhs answer is correct, with a little note by myself. It seems to be important, which resource is used. From mobile to mobile, You have to use "mobile". Wether "Smack" nor any other resource identifier would work here. So it is important to initialize the OutgoingFileTransfer correctly like this:
OutgoingFileTransfer oft = ftm1.createOutgoingFileTransfer
(XmppStringUtils.completeJidFrom(USER, SERV, "mobile"));//important resource "mobile"
I got it after very research.
FileTransferManager ftm1 = FileTransferManager.getInstanceFor(connection);
FileTransferManager ftm2 = FileTransferManager.getInstanceFor(connection2);
ftm2.addFileTransferListener(new FileTransferListener() {
#Override
public void fileTransferRequest(FileTransferRequest request) {
IncomingFileTransfer ift = request.accept();
try {
InputStream is = ift.recieveFile();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int nRead;
byte[] buf = new byte[1024];
while ((nRead = is.read(buf, 0, buf.length)) != -1) {
os.write(buf, 0, nRead);
}
os.flush();
dataReceived = os.toByteArray();
} catch (SmackException | IOException | XMPPErrorException e) {
e.printStackTrace();
}
if (Arrays.equals(dataToSend, dataReceived)) {
System.out.println("Received data matches send data. \\o/");
} else {
System.err.println("Recieved data DOES NOT match send data. :(");
}
}
});
OutgoingFileTransfer oft = ftm1.createOutgoingFileTransfer(XmppStringUtils.completeJidFrom(USER, SERV, "resourse"));
oft.sendStream(new ByteArrayInputStream(dataToSend), "hello.txt", dataToSend.length, "A greeting");
outerloop: while (!oft.isDone()) {
switch (oft.getStatus()) {
case error:
System.out.println("Filetransfer error: " + oft.getError());
break outerloop;
default:
System.out.println("Filetransfer status: " + oft.getStatus() + ". Progress: " + oft.getProgress());
break;
}
Thread.sleep(1000);
}
connection.disconnect();
connection2.disconnect();
Thread.sleep(1000);
}
the one connection is sending file and another connection is receiving this is working code.
I had same problem, I investigated the stanza and solved it this way.
Many people use "/Smack" or "/Resource" as resource part in jid, but that can be done another way.
Resource path is changing with every presence changed of user. Lets say we want to send image to this user:
"user1#mydomain"
You must add "/Resource" or "/Smack" part to this jid and it become this:
user1#mydomain/Resource
But resource path is changing with presence so you must follow every presence change to update resource path.
Best way is to get user presence is in roster listener and in presencheChanged() method you get last user resource part like this:
Roster roster=getRoster();
roster.addRosterListener(new RosterListener() {
#Override
public void entriesAdded(Collection<Jid> addresses) {
Log.d("entriesAdded", "ug");
context.sendBroadcast(new Intent("ENTRIES_ADDED"));
}
#Override
public void entriesUpdated(Collection<Jid> addresses) {
Log.d("entriesUpdated", "ug");
}
#Override
public void entriesDeleted(Collection<Jid> addresses) {
Log.d("entriesDeleted", "ug");
}
#Override
public void presenceChanged(Presence presence) {
Log.d("presenceChanged", "ug");
//Resource from presence
String resource = presence.getFrom().getResourceOrEmpty().toString();
//Update resource part for user in DB or preferences
//...
}
});
}
Resource string will be some generated string like "6u1613j3kv" and jid will become:
user1#mydomain/6u1613j3kv
That means that you must create your outgoing transfer like this:
EntityFullJid jid = JidCreate.entityFullFrom("user1#mydomain/6u1613j3kv");
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(jid)
In your case this resource part is "mobile". Just change it with resource part from presence in roster listener.
And that is how i have solved my problem with file transfer on smack and Openfire.
Also to mention you must add following properties in your Openfire server:
xmpp.proxy.enabled - true
xmpp.proxy.externalip - MY_IP_ADDRESS
xmpp.proxy.port -7777
Just to mention, I am using Openfire 4.0.2 and Smack 4.2.2.
Also this can be configured the easy way, just set the resource on
XMPPTCPConnectionConfiguration.Builder .
like
XMPPTCPConnectionConfiguration.Builder configurationBuilder =
XMPPTCPConnectionConfiguration.builder();
configurationBuilder.setResource("yourResourceName");
Related
I want to transfer file eg. image or video from Openfire with Smack Api.
But I am getting an error code 503 service unavailable every time.
My Coding is
public void onConnectionEstablished(){
fileTransferManager = FileTransferManager.getInstanceFor(connection);
OutgoingFileTransfer.setResponseTimeout(30000);
addFileTransferListener();
}
public void sendImage(File file, String to) throws SmackInvocationException {
if (fileTransferManager == null || !isConnected()) {
throw new SmackInvocationException("server not connected");
}
EntityFullJid fullJid;
OutgoingFileTransfer transfer = fileTransferManager.createOutgoingFileTransfer(getFullJid(to));
try {
transfer.sendFile(file, file.getName());
} catch (Exception e) {
Log.e(LOG_TAG, "send file error");
throw new SmackInvocationException(e);
}
while(!transfer.isDone()) {
if(transfer.getStatus().equals(FileTransfer.Status.refused) || transfer.getStatus().equals(FileTransfer.Status.error)
|| transfer.getStatus().equals(FileTransfer.Status.cancelled)){
throw new SmackInvocationException("send file error, " + transfer.getError());
}
}
Log.d(LOG_TAG, "send file status: " + transfer.getStatus());
if(transfer.getStatus().equals(FileTransfer.Status.refused) || transfer.getStatus().equals(FileTransfer.Status.error)
|| transfer.getStatus().equals(FileTransfer.Status.cancelled)){
throw new SmackInvocationException("send file error, " + transfer.getError());
}
}
private void addFileTransferListener() {
fileTransferManager.addFileTransferListener(new FileTransferListener() {
public void fileTransferRequest(final FileTransferRequest request) {
new Thread() {
#Override
public void run() {
IncomingFileTransfer transfer = request.accept();
String fileName = String.valueOf(System.currentTimeMillis());
File file = new File(FileUtils.getReceivedImagesDir(context), fileName + FileUtils.IMAGE_EXTENSION);
try {
transfer.recieveFile(file);
} catch (IOException e) {
e.printStackTrace();
}
catch (SmackException e) {
Log.e(LOG_TAG, "receive file error", e);
return;
}
while (!transfer.isDone()) {
if(transfer.getStatus().equals(FileTransfer.Status.refused) || transfer.getStatus().equals(FileTransfer.Status.error)
|| transfer.getStatus().equals(FileTransfer.Status.cancelled)){
Log.e(LOG_TAG, "receive file error, " + transfer.getError());
return;
}
}
}
}.start();
}
});
}
My Log is
<iq type="error" id="Z6hyN-147" from="7665935694#ec2-18-221-73-31.us-east-2.compute.amazonaws.com/smack" to="8094772915#ec2-18-221-73-31.us-east-2.compute.amazonaws.com/smack"><si xmlns="http://jabber.org/protocol/si" id="jsi_1095486335648182993" mime-type="image/jpeg" profile="http://jabber.org/protocol/si/profile/file-transfer"><file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="1509942029217.jpg" size="830807"><desc>1509942029217.jpg</desc></file><feature xmlns="http://jabber.org/protocol/feature-neg"><x xmlns="jabber:x:data" type="form"><field var="stream-method" type="list-single"><option><value>http://jabber.org/protocol/bytestreams</value></option><option><value>http://jabber.org/protocol/ibb</value></option></field></x></feature></si><error code="503" type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>
One thing i observed that when both the user sender and receiver are on the same network then not getting error.
And there is a file transfer setting in Openfire server that is given below.I donot know what it means and how to configure it.
I have seen a lot of question on stackoverflow but no answer i got.
Please help me.Thanks
I had same problem, I investigated the stanza and solved it this way.
Many people use "/Smack" or "/Resource" as resource part in jid, but that can be done another way.
Resource path is changing with every presence changed of user. Lets say we want to send image to this user:
"user1#mydomain"
You must add "/Resource" or "/Smack" part to this jid and it become this:
user1#mydomain/Resource
But resource path is changing with presence so you must follow every presence change to update resource path.
Best way is to get user presence is in roster listener and in presencheChanged() method you get last user resource part like this:
Roster roster=getRoster();
roster.addRosterListener(new RosterListener() {
#Override
public void entriesAdded(Collection<Jid> addresses) {
Log.d("entriesAdded", "ug");
context.sendBroadcast(new Intent("ENTRIES_ADDED"));
}
#Override
public void entriesUpdated(Collection<Jid> addresses) {
Log.d("entriesUpdated", "ug");
}
#Override
public void entriesDeleted(Collection<Jid> addresses) {
Log.d("entriesDeleted", "ug");
}
#Override
public void presenceChanged(Presence presence) {
Log.d("presenceChanged", "ug");
//Resource from presence
String resource = presence.getFrom().getResourceOrEmpty().toString();
//Update resource part for user in DB or preferences
//...
}
});
}
Resource string will be some generated string like "6u1613j3kv" and jid will become:
user1#mydomain/6u1613j3kv
That means that you must create your outgoing transfer like this:
EntityFullJid jid = JidCreate.entityFullFrom("user1#mydomain/6u1613j3kv");
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(jid)
In your case :
String to = "user1#mydomain/6u1613j3kv";
EntityFullJid fullJid = JidCreate.entityFullFrom(to);
OutgoingFileTransfer transfer = fileTransferManager.createOutgoingFileTransfer(fullJid);
6u1613j3kv string is generated on every presence change.
And that is how i have solved my problem with file transfer on smack and Openfire.
Also to mention you must add following properties in your Openfire server:
xmpp.proxy.enabled - true
xmpp.proxy.externalip - MY_IP_ADDRESS
xmpp.proxy.port -7777
Just to mention, I am using Openfire 4.0.2 and Smack 4.2.2.
I'm trying to transfer file using smack extension library 4.2.0 but couldn't able to successfully transfer file. When i try to transfer file this is the error I'm getting
org.jivesoftware.smack.XMPPException$XMPPErrorException: XMPP error reply received from 252615100006#server/Smack: XMPPError: service-unavailable - cancel
Even though the peer 00006 is online but I don't why I'm receiving error from the peer
This is my code to transfer file
public void sendImageMessage(String sendTo, String imagePath) throws XmppStringprepException {
FileTransferManager manager = FileTransferManager.getInstanceFor(mConnection);
EntityBareJid jid = JidCreate.entityBareFrom(sendTo);
EntityFullJid entityFullJid = JidCreate.entityFullFrom(jid+"/Smack");
Domainpart domainpart = entityFullJid.getDomain();
// Log.d(TAG ," JID Domain "+entityFullJid.do)
OutgoingFileTransfer outgoingFileTransfer = manager.createOutgoingFileTransfer(entityFullJid);
File file = new File(imagePath);
try {
outgoingFileTransfer.sendFile(file, file.getName());
} catch (SmackException e) {
e.printStackTrace();
}
while (!outgoingFileTransfer.isDone()) {
if (outgoingFileTransfer.getStatus().equals(FileTransfer.Status.error)) {
System.out.println("ERROR!!! " + outgoingFileTransfer.getError());
} else if (outgoingFileTransfer.getStatus().equals(FileTransfer.Status.cancelled)
|| outgoingFileTransfer.getStatus().equals(FileTransfer.Status.refused)) {
System.out.println("Cancelled!!! " + outgoingFileTransfer.getError());
}
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (outgoingFileTransfer.getStatus().equals(FileTransfer.Status.refused) || outgoingFileTransfer.getStatus().equals(FileTransfer.Status.error)
|| outgoingFileTransfer.getStatus().equals(FileTransfer.Status.cancelled)) {
System.out.println("refused cancelled error" + outgoingFileTransfer.getError().toString());
} else {
System.out.println("Successfully_SEND");
}
Any help and guidance is much appreciated
Edit : I user spark client to transfer files using my server it susseccfully tranfered the file. So the problem is not with server I thin't it's in code on client side.
I had same problem, I investigated the stanza and solved it this way.
Many people use "/Smack" or "/Resource" as resource part in jid, but that can be done another way.
Resource path is changing with every presence changed of user. Lets say we want to send image to this user:
"user1#mydomain"
You must add "/Resource" or "/Smack" part to this jid and it become this:
user1#mydomain/Resource
user1#mydomain/Smack
But resource path is changing with presence so you must follow every presence change to update resource path.
Best way is to get user presence is in roster listener and in presencheChanged() method you get last user resource part like this:
Roster roster=getRoster();
roster.addRosterListener(new RosterListener() {
#Override
public void entriesAdded(Collection<Jid> addresses) {
Log.d("entriesAdded", "ug");
context.sendBroadcast(new Intent("ENTRIES_ADDED"));
}
#Override
public void entriesUpdated(Collection<Jid> addresses) {
Log.d("entriesUpdated", "ug");
}
#Override
public void entriesDeleted(Collection<Jid> addresses) {
Log.d("entriesDeleted", "ug");
}
#Override
public void presenceChanged(Presence presence) {
Log.d("presenceChanged", "ug");
//Resource from presence
String resource = presence.getFrom().getResourceOrEmpty().toString();
//Update resource part for user in DB or preferences
//...
}
});
}
Resource string will be some generated string like "6u1613j3kv" and jid will become:
user1#mydomain/6u1613j3kv
That means that you must create your outgoing transfer like this:
EntityFullJid jid = JidCreate.entityFullFrom("user1#mydomain/6u1613j3kv");
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(jid)
In your case like this:
EntityBareJid jid = JidCreate.entityBareFrom(sendTo);
EntityFullJid entityFullJid = JidCreate.entityFullFrom(jid + resource);
Where resource is resourcepart from Presence in listener.
And that is how i have solved my problem with file transfer on smack and Openfire.
Also to mention you must add following properties in your Openfire server:
xmpp.proxy.enabled - true
xmpp.proxy.externalip - MY_IP_ADDRESS
xmpp.proxy.port -7777
Just to mention, I am using Openfire 4.0.2 and Smack 4.2.2.
Also this can be configured the easy way, just set the resource on
XMPPTCPConnectionConfiguration.Builder .
like
XMPPTCPConnectionConfiguration.Builder configurationBuilder =
XMPPTCPConnectionConfiguration.builder();
configurationBuilder.setResource("yourResourceName");
I can create group chat room successfully XMPP(smack). I have added
invitation listener, but never called. Does anyone know how to do it?
Using:
XMPP
Smack 4.2
Openfire server
Send Invitation code:
muc.invite(userId +"#" +XMPP.getInstance().HOST + "/Smack", "Meet me in this excellent room");
Invitation listener code:
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
manager.addInvitationListener(new InvitationListener() {
#Override
public void invitationReceived(XMPPConnection xmppConnection, MultiUserChat muc, String inviter, String reason, String password, Message message) {
try {
muc.join(nickname);
} catch (SmackException.NoResponseException e) {
e.printStackTrace();
} catch (XMPPException.XMPPErrorException e) {
e.printStackTrace();
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
}
});
Probably you have an issue about RESOURCE.
When you send an invitation to a certain JID, you might omit resource part or the message will be sent only to specified resource.
JID it's composed in following way:
user#serverdomain/resource
With this invitation, you are inviting only user using "Smack" as Resource.
Resource it's configurated in AbstractXMPPConnection object or in login phase
XMPPTCPConnectionConfiguration.builder()
.setServiceName(serverName)
.setHost(server)
.setPort(port)
.setResource( RESOURCE_IDENTIFIER)
.build();
connection = new XMPPTCPConnection(config);
connection.login(username, password, RESOURCE_IDENTIFIER);
So, probably, you declared as your resource identificator (just an arbitrary String) not "Smack" but "Spark" o something else or left default one.
Just omit Resource part (or fix with correct one, but I suggest to omit)
muc.invite(userId +"#" +XMPP.getInstance().HOST, "Meet me in this excellent room");
Of course, userId must exist and HOST it's the valid one
I am currently trying to upload a file from an android client using retrofit2 to a server using Spring Boot and its REST api.
CLIENT
I specifiy the upload method as described here: https://github.com/square/retrofit/issues/1063
public interface RetroRespondService {
#Multipart
#POST("/v1/answers")
public Call<ResponseDTO> sendPictures(#Part("file\"; filename=\"image.png")RequestBody image);
}
In another class the method to provide the actual image is declared:
(Now its just a test scenario. When image uploading is actually accomplished it will get more sophisticated.)
public void performAnswerRequest() {
try {
if (mRetrofit == null) {
mRetrofit = new Retrofit.Builder()
.baseUrl(DataHolder.getHostName())
.build();
}
//load test image
AssetManager manager = getAssets();
File file = new File(getFilesDir(), "image.png");
Utility.writeBytesToFile(new BufferedInputStream(manager.open("heart.png")), file);
RetroRespondService requestService = mRetrofit.create(RetroRespondService.class);
RequestBody image= RequestBody.create(MediaType.parse("multipart/form-data"), file);
Call<ResponseDTO> response = requestService.sendPictures(image);
response.enqueue(new AsyncAnswerResponse());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
SERVER
What I actually do not know is, how to properly get the image on the spring side.
#RequestMapping(value = API_VERSION + "/answers", method = RequestMethod.POST)
#ResponseBody
ResponseEntity<ResponseDTO> addAnswers(#RequestParam("file\"; filename=\"image.png") MultipartFile answers) throws DBEntryDoesNotExistException, EvaluationException, ParticipantException {
// In fact I have set a brake point here. Never entered the method yet, though
System.out.println("Yay!")
return null;
}
ERROR
Request: localhost:8080/v1/answers raised org.springframework.web.bind.MissingServletRequestParameterException:
Required MultipartFile parameter 'file"; filename="image.png' is not present
Since wireshark reports that in fact a request of size 1894 Bytes was send and this is the size of the image i want to upload I strongly believe the the data is actually transmitted but cannot be decoded from the server.
I have also seen this answers: How to config "CommonsMultipartResolver" in spring4 without xml to upload file
and subsequently implemented this class on the server side:
#Configuration
public class MultipartConfiguration {
#Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver=new CommonsMultipartResolver();
resolver.setDefaultEncoding("utf-8");
resolver.setMaxUploadSize(1048576);
return resolver;
}
}
If you have any pointers in how to solve this I would appreciate your answer tremendously :)
If there are any questions left unanswered feel free to ask away.
Btw.: Sending and receiving JSON encoded data works just fine in both directions.
I have offline messages option enabled in the openfire server.But I'm unable to get offline messages
User A is online ,User B is online ,in this case I'm able to get messages.
Now User B Turned off his WiFi(Note : User A waited till the user B Session completely killed in the server )
now User A send a message to User B
in this case I'm able to see the message in the openfire offline table.
Now User B Comes online again server is sending the message to user B as the server come to know that User B is online
(Message disappeared from offline messages table ).
But User B is not going to receive that message.
connection.login(userName, userPwd, UiUtility.getMyPhoneNO());
PacketFilter filter = new PacketTypeFilter(org.jivesoftware.smack.packet.Message.class);
packetListener =new PacketListener() {
public void processPacket(Packet packet) {
Message message = (Message) packet;
if (message.getBody() != null) {
String fromName = StringUtils.parseBareAddress(message
.getFrom());
Log.i("XMPPClient", "Got text [" + message.getBody()
+ "] from [" + fromName + "]");
}
}
};
connection.addPacketListener(packetListener, filter);
Again after successful login im able to chat normally.But I wonder why those offline messages are missing ? .My PacketListener unable to catch those offline messages .Please Help me
Asmack is depreceated. Use Smack. An Open Source XMPP Client Library written in Java for JVMs and Android. Add the following lines to your gradle file:
compile 'org.igniterealtime.smack:smack-android:4.1.3'
compile 'org.igniterealtime.smack:smack-tcp:4.1.3'
compile 'org.igniterealtime.smack:smack-extensions:4.1.3'
The problem is easy to be solved.
Before making connection with the XMPP server just register providers using ProviderManager class provided by ASmack library.
If this can't solve ur problem visit ur local server and search for offline messages, and select the option ALWAYS STORE setting the storage limit to be 1000 kb. It is 100 kb by default.
Hope this works.
After lot struggle, I have resolved the issue. In your openfire admin page, go to "client settings" and reduce the idle time from 360sec (by default) to 1 sec(may be). Only then when you disconnected from Internet, it can detect that you are offline and preserve rest of the messages as OFFLINE.
#Override
public void onNetworkConnectionChanged(boolean isConnected) {
if(isConnected){
new Thread() {
public void run() {
try {
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
builder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
builder.setUsernameAndPassword("phone", "admin");
builder.setSendPresence(true);
builder.setServiceName(<Service name>);
builder.setHost(<Host name>);
builder.setResource("Test");
builder.setDebuggerEnabled(true);
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("Available");
connection = new XMPPTCPConnection(builder.build());
connection.connect();
connection.login();
Presence presence123 = new Presence(Presence.Type.available);
presence123.setStatus("Available");
try {
connection.sendStanza(presence123);
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
StanzaFilter filter = new AndFilter(new StanzaTypeFilter(Message.class));
PacketListener myListener = new PacketListener()
{
public void processPacket(Stanza stanza)
{
retrieveMessage(stanza,userType);
}
};
connection.addPacketListener(myListener, filter);
try {
connection.sendStanza(presence);
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
} catch (SmackException | XMPPException | IOException e) {
e.printStackTrace();
}
//return connection.isConnected();
}
}.start();
The above is working fine and able to retrieve the offline messages. The method "retrieveMessage(stanza,userType);" is used to process the incoming message and update the Adapter. Make sure to send the Presence as "Available" when you reconnect. Please let me know if there are still any issues.