I'm trying to get a SIP servlet chat server working, together with the textclient found here.
When I use 2 clients to send messages to eachother (peer to peer), everything goes well. But when I use one or more clients together with my server, I have to wait exactly 32 seconds before the server picks up any new messages in the doMessage() method.
I'm using Netbeans together with Sailfin as my SIP server. Is there some kind of limitation or configurable delay or timeout between requests or responses in Sailfin I'm looking over?
I can post the server code, if needed.
Thanks
Here is the code of the server, i'll try to make a wireshark trace later.
public class ChatroomSipServlet extends SipServlet {
public final static String USER_LIST = "userList";
public final static String CHATROOM_SERVER_NAME = "chatroomservername";
public String serverAddress;
public SipFactory factory;
#Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
System.out.println("Chatroom sip servlet is gestart!");
try {
factory = (SipFactory) getServletContext().getAttribute("javax.servlet.sip.SipFactory");
System.out.println("Sip Factory: " + factory);
} catch (Exception e) {
throw new ServletException("Factory probleem!", e);
}
getServletContext().setAttribute(USER_LIST, new ArrayList<String>());
serverAddress = getServletConfig().getInitParameter(CHATROOM_SERVER_NAME);
System.out.println("serverAddress is: " + serverAddress);
}
#Override
public void destroy() {
try {
sendToAll(serverAddress, "Server sluit af!");
} catch (Throwable e) {
e.printStackTrace();
}
super.destroy();
}
protected void doMessage(SipServletRequest request) throws ServletException, IOException {
System.out.println(getDateTime() + " Bericht ontvangen");
request.createResponse(SipServletResponse.SC_OK).send();
String message = request.getContent().toString();
String from = ((SipURI) request.getFrom().getURI()).toString();
if (message.equalsIgnoreCase("/quit")) {
sendToUser(from, "Bye");
removeUser(from);
return;
}
if (!containsUser(from)) {
sendToUser(from, "Welkom in de chatroom. Typ '/quit' om af te sluiten.");
addUser(from);
}
if (message.equalsIgnoreCase("/who")) {
String users = "Lijst van de gebruikers:\n";
List<String> list = (List<String>) getServletContext().getAttribute(USER_LIST);
for (String user : list) {
users += user + "\n";
}
sendToUser(from, users);
return;
}
if (message.equalsIgnoreCase("/join")) {
return;
}
sendToAll(from, message);
}
protected void doErrorResponse(SipServletResponse response) throws ServletException, IOException {
// String receiver = response.getTo().toString();
String receiver = ((SipURI) response.getTo().getURI()).toString();
System.out.println(getDateTime() + " Errorresponse voor " + receiver);
removeUser(receiver);
}
protected void doSuccessResponse(SipServletResponse response) throws ServletException, IOException {
response.getApplicationSession().invalidate();
}
private void sendToAll(String from, String message) throws ServletParseException, IOException {
List<String> list = (List<String>) getServletContext().getAttribute(USER_LIST);
for (String user : list) {
SipApplicationSession session = factory.createApplicationSession();
System.out.println(getDateTime() + " Session created voor " + user);
SipServletRequest request = factory.createRequest(session, "MESSAGE", serverAddress, user);
String msg = from + " stuurt: \n" + message;
request.setContent(msg.getBytes(), "text/plain");
request.send();
}
}
private void sendToUser(String to, String message) throws ServletParseException, IOException {
SipApplicationSession session = factory.createApplicationSession();
SipServletRequest request = factory.createRequest(session, "MESSAGE", serverAddress, to);
request.setContent(message.getBytes(), "text/plain");
request.send();
}
private boolean containsUser(String from) {
List<String> list = (List<String>) getServletContext().getAttribute(USER_LIST);
return list.contains(from);
}
private void addUser(String from) {
List<String> list = (List<String>) getServletContext().getAttribute(USER_LIST);
list.add(from);
}
private void removeUser(String from) {
System.out.println(getDateTime() + " " + from + " wordt verwijderd uit de lijst.");
List<String> list = (List<String>) getServletContext().getAttribute(USER_LIST);
list.remove(from);
}
#Override
protected void doRegister(SipServletRequest req) throws ServletException, IOException {
System.out.println("Register request ontvangen: " + req.getTo());
int response = SipServletResponse.SC_OK;
SipServletResponse resp = req.createResponse(response);
resp.send();
}
private String getDateTime() {
DateFormat dateFormat = new SimpleDateFormat("[" + "HH:mm:ss" + "]");
Date date = new Date();
return dateFormat.format(date);
}
}
And the sip.xml
<sip-app>
<app-name>sip.chatvoorbeeld.ChatServer</app-name>
<display-name>Chatroom Sip Servlet</display-name>
<description>Chatroom Sip Servlet</description>
<servlet-selection>
<main-servlet>
ChatroomSipServlet
</main-servlet>
</servlet-selection>
<session-config>
<session-timeout>5</session-timeout>
</session-config>
<servlet>
<servlet-name>ChatroomSipServlet</servlet-name>
<display-name>ChatroomSipServlet</display-name>
<description>Chatroom SIP servlet</description>
<servlet-class>
sip.chatvoorbeeld.ChatroomSipServlet
</servlet-class>
<init-param>
<param-name>chatroomservername</param-name>
<param-value>sip:chatserver#192.168.56.1:5060</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
</sip-app>
I also tried an empty servlet, just with a doMessage() method that just prints out "message received". Same 32 seconds delay...
Wireshark gives me:
I send the message "test" to the server:
MESSAGE sip:chatserver#192.168.56.1:5060;transport=udp SIP/2.0
Call-ID: aba00c2646a9b4e6df3b15df19dbf58d#192.168.56.101
CSeq: 1 MESSAGE
From: "bobby" <sip:bobby#192.168.56.101:5095>;tag=textclientv1.0
To: "chatserver" <sip:chatserver#192.168.56.1:5060>
Via: SIP/2.0/UDP 192.168.56.101:5095;branch=branch1
Max-Forwards: 70
Contact: "bobby" <sip:bobby#192.168.56.101:5095>
Content-Type: text/plain
Content-Length: 4
test
Server sends back:
SIP/2.0 200 OK
Content-Length: 0
To: "chatserver"<sip:chatserver#192.168.56.1:5060>;tag=g9vdns7u-e
Cseq: 1 MESSAGE
Via: SIP/2.0/UDP 192.168.56.101:5095;branch=branch1
From: "bobby"<sip:bobby#192.168.56.101:5095>;tag=textclientv1.0
Call-Id: aba00c2646a9b4e6df3b15df19dbf58d#192.168.56.101
Server: Glassfish_SIP_2.0.0
MESSAGE sip:bobby#192.168.56.101:5095 SIP/2.0
Max-Forwards: 69
Content-Length: 43
To: <sip:bobby#192.168.56.101:5095>
Cseq: 1 MESSAGE
Via: SIP/2.0/UDP 192.168.56.1:5060;branch=z9hG4bKdaacb7673c871796474ca951221a6643db6c
Content-Type: text/plain
Call-Id: 192.168.56.1_11_6595680936174578736
From: <sip:chatserver#192.168.56.1:5060>;tag=g9vdns7u-g
sip:bobby#192.168.56.101:5095 stuurt:
test
Then my client again answers with an OK:
SIP/2.0 200 OK
To: <sip:bobby#192.168.56.101:5095>;tag=888
CSeq: 1 MESSAGE
Via: SIP/2.0/UDP 192.168.56.1:5060;branch=z9hG4bKdaacb7673c871796474ca951221a6643db6c;received=192.168.56.1
Call-ID: 192.168.56.1_11_6595680936174578736
From: <sip:chatserver#192.168.56.1:5060>;tag=g9vdns7u-g
Content-Length: 0
So far so good, everything works well.
But now I send a second message to the server "test2" and I get this:
Client to server:
MESSAGE sip:chatserver#192.168.56.1:5060;transport=udp SIP/2.0
Call-ID: 95ad65365378b9b6b5bd7ad3629f7b02#192.168.56.101
CSeq: 1 MESSAGE
From: "bobby" <sip:bobby#192.168.56.101:5095>;tag=textclientv1.0
To: "chatserver" <sip:chatserver#192.168.56.1:5060>
Via: SIP/2.0/UDP 192.168.56.101:5095;branch=branch1
Max-Forwards: 70
Contact: "bobby" <sip:bobby#192.168.56.101:5095>
Content-Type: text/plain
Content-Length: 5
test2
Then the server does respond with:
SIP/2.0 200 OK
Content-Length: 0
To: "chatserver"<sip:chatserver#192.168.56.1:5060>;tag=g9vdns7u-e
Cseq: 1 MESSAGE
Via: SIP/2.0/UDP 192.168.56.101:5095;branch=branch1
From: "bobby"<sip:bobby#192.168.56.101:5095>;tag=textclientv1.0
Call-Id: aba00c2646a9b4e6df3b15df19dbf58d#192.168.56.101
Server: Glassfish_SIP_2.0.0
But then the communication stops... I get a 200 OK, but the println() in my doMessage() method is not passed.
32 seconds is the normal timeout for transactions (denoted 64*T1 in RFC3261, where the default for T1 is 500ms.)
I have no direct idea for a solution to your problem, except that the timeout is very likely NOT a misconfiguration in SailFin. So please provide a wireshark trace and the server code!
To answer my own question, if someone ever encounters the same problem, i found this is a bug in the textclient against the following section 8.1.1.7 of RFC 3261.
The branch parameter value MUST be
unique across space and time for all
requests sent by the UA. The
exceptions to this rule are CANCEL and
ACK for non-2xx responses. As
discussed below, a CANCEL request will
have the same value of the branch
parameter as the request it cancels.
As discussed in Section 17.1.1.3, an
ACK for a non-2xx response will also
have the same branch ID as the INVITE
whose response it acknowledges.
The uniqueness property of the branch
ID parameter, to facilitate its use as
a transaction ID, was not part of RFC
2543.
The branch ID inserted by an element
compliant with this specification MUST
always begin with the characters
"z9hG4bK".
The following line in SipLayer.java
ViaHeader viaHeader = headerFactory.createViaHeader(getHost(),
getPort(), "udp", "branch1");
Will create every message with the "branch1" parameter. Making this parameter unique fixes the problem.
Related
I've created a route to allow me to forward a REST call. Everything is going well, except I cannot modify the HTTP headers of the response (actually I can't even get them untouched on the response).
// My processor
private void remplacerLiensDansHeader(final Exchange exchange, final String rootUrlPivotJoram, final String rootUrlRemplacement) {
// That is OK, I get the values just fine
ArrayList<String> oldLinks = exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
// This is also OK
final List<String> newLinks = anciensLiens.stream().map(lien -> lien.replace(rootUrlPivotJoram, rootUrlRemplacement)).collect(toList());
// No error, but apparently doesn't work
exchange.getMessage().setHeader(HEADER_LINK, newLinks);
exchange.getMessage().setHeader("test", "test");
}
// Route configuration
#Override
public void configure() throws Exception {
this.from(RestRouteBuilder.endPoint(createProducerJoramConfiguration))
.setExchangePattern(InOut)
.removeHeader(Exchange.HTTP_URI)
.toD(createProducerJoramConfiguration.getUrlDestination())
.setHeader("test", () -> "test") // that doesn't work either
.process(createProducerJoramConfiguration.getProcessor()); // this is the processor with the code above
}
This is the response I get (note that the response code is 200 and I think it's weird as the original is 201)
curl -v -XPost --user "xxx:yyyy" http://localhost:10015/zzzz/webservices/xxxxx
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10015 (#0)
* Server auth using Basic with user 'xxx'
> Post /zzzzz/webservices/eeee HTTP/1.1
> Host: localhost:10015
> Authorization: Basic pppppppppppppppppp==
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200
< Date: Tue, 31 Aug 2021 11:17:49 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Two things I've noticed:
if I add a body in the processor, then the body is present in the response,
if I remove the processor, the headers present in the "original response" are not present either.
I don't know what headers you exactly lose, but be aware that the Camel HTTP component has a default header filter (as lots of Camel components have).
If you don't specify your own HeaderFilterStrategy, the default HttpHeaderFilterStrategy is used.
This default filters the following headers:
content-length
content-type
host
cache-control
connection
date
pragma
trailer
transfer-encoding
upgrade
via
warning
Camel*
org.apache.camel*
With this filter, Camel wants to avoid that old HTTP headers are still present on outgoing requests (probably with wrong data).
The filtering of Camel headers is just to remove Camel specific stuff that is not relevant for HTTP.
Actually, the problem was the cxfrs component.
We manage to find an answer here : see : Response to REST client from Camel CXFRS route?
Here is the final solution.
Thanks to everyone that looked or answer, I hope that'll help someone else.
public class ModificationHeaderLinkProcessor implements Processor {
private static final String HEADER_LINK = "Link";
#Override
public void process(final Exchange exchange) {
List<String> newLinks= getNewLinks(exchange, oldUrl, newUrl);
ResponseBuilder builder = createHttpResponse(exchange);
builder.header(HEADER_LINK, newLinks);
exchange.getIn().setBody(builder.build());
}
private List<String> getNewLinks(final Exchange exchange, final String oldUrl, final String newUrl) {
ArrayList<String> oldLinks= exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
return oldLinks.stream().map(link-> link.replace(oldUrl, newUrl)).collect(toList());
}
private ResponseBuilder createHttpResponse(final Exchange exchange) {
ResponseBuilder builder = Response.status(getHttpStatusCode(exchange))
.entity(exchange.getIn().getBody());
clearUselessHeader(exchange);
exchange.getIn().getHeaders().forEach(builder::header);
return builder;
}
private void clearUselessHeader(final Exchange exchange) {
exchange.getIn().removeHeader(HEADER_LINK);
exchange.getIn().removeHeaders("Camel*");
}
private Integer getHttpStatusCode(final Exchange exchange) {
return exchange.getIn().getHeader(exchange.HTTP_RESPONSE_CODE, Integer.class);
}
private final String getPropertiesValue(CamelContext camelContext, String key) {
return camelContext.getPropertiesComponent().resolveProperty(key).orElseThrow();
}
}
The Java:
private void apiPasswordReset(final String msisdn) {
showLoading();
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(Constants.API_THREADPOOL_SIZE));
ListenableFuture<ResponseBase> response = service.submit(new Callable<ResponseBase>() {
#Override
public ResponseBase call() throws Exception {
return webService.passwordReset(
Constants.RESPONSE_TYPE_APPLICATION_JSON,
getResources().getString(R.string.api_key),
getResources().getString(R.string.channel_id),
getResources().getString(R.string.app_id),
getResources().getString(R.string.base_url),
getResources().getString(R.string.base_url_port),
getResources().getString(R.string.api_version_string),
msisdn
);
}
});
Futures.addCallback(response, new FutureCallback<ResponseBase>() {
#Override
public void onSuccess(final ResponseBase result) {
Log.i(TAG, "onSuccess()");
}
#Override
public void onFailure(Throwable t) {
Log.e(TAG, t.getMessage());
}
});
}
Response Model:
public class ResponseBase {
private boolean success;
private String response_code;
private String api_version;
private String message;
private String base_url;
}
The call:
#GET("/{endpoint}:{endpointPort}/api/{apiVersion}/users/{msisdn}/edit")
ResponseBase passwordReset(
#Header("Content-type") String contentType,
#Header("x-api-key") String apiKey,
#Header("channel-id") String channelId,
#Header("app-id") String appId,
#Path("endpoint") String endpoint,
#Path("endpointPort") String endpointPort,
#Path("apiVersion") String apiVersion,
#Path("msisdn") String msisdn
);
Request:
---> HTTP GET
x-api-key: 5ffc84c4-d1ad-29e8-4114-d84708bf96ac
channel-id: 1
app-id: android_001
Content-Type: application/json
---> END HTTP (no body)
Response:
<--- HTTP 200 (1013ms)
: HTTP/1.1 200 OK
Cache-Control: no-cache
Connection: Keep-Alive
Content-Length: 217
Content-Type: application/json
Date: Mon, 11 Apr 2016 08:31:40 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.7 (Ubuntu)
Set-Cookie: XSRF-TOKEN=eyJpdiI6Ikt5RWM4UFhDc1hrVytNRkNlc0QySVE9PSIsInZhbHVlIjoiVmZ4KzF5bVdkY2xwbmJsaGZMbDZ1NldpNVp6bGsyemIyVGVKUzhcLzIrZDN3R3pGRWRVd0FWdnlnTjhaZCtyQ2JacTcybllvMW1ZOElaMU5TZUNpYUp3PT0iLCJtYWMiOiI3YjBiZDM4MjM4OWMxMDRlOThiODU5ZDc1M2RhZTA5YmYyNDM0ZGE1NDQ0ZjQ0YWRmOTM5MjQ1NTI0ODkwNTAxIn0%3D; expires=Mon, 11-Apr-2016 10:31:40 GMT; Max-Age=7200; path=/
Set-Cookie: laravel_session=eyJpdiI6ImsyVXcrRHpHY2FOakhZZDhIYjRnR1E9PSIsInZhbHVlIjoiQTRcL2g0c1Ztdk9MbTl3MlNTblwvdHhicStrTHMzYlZmb3I2ZVc1NjBwNWIwYWQ5NjB2UXVyQzZBSUpLTzRcL0dta0gxUVR2ZnhaWnFoQStEUm93Q0lESlE9PSIsIm1hYyI6ImQxYmY4NDk2OTgzNDZlZDVlMmM0MTUyMzMzN2ZhMmQ3YzY1ZWYyMTZlMDA4NWY3ZDc1ZTIzMTc1NzdmMzM3YjkifQ%3D%3D; expires=Mon, 11-Apr-2016 10:31:40 GMT; Max-Age=7200; path=/; httponly
X-Android-Received-Millis: 1460363500117
X-Android-Response-Source: NETWORK 200
X-Android-Sent-Millis: 1460363499337
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Version OK 5.5.9-1ubuntu4.14
{"success":true,"response_code":"OK","api_version":1,"message":"PASSWORD RESET NOTIFICATION SENT SUCCESSFULLY","error":[],"base_url":"http:\/\/apiserver.com","data":[]}
The response data seems to fit the model. When I run this call with postman it returns valid json aswell. I have commented out all fields in the model and still get the same error.
Any help would be appreciated :)
Versions:
Android studio 2.0
Gradle 2.0
Retrofit 1.9.0
Guava 19.0
FIX:
Due to my own stupidity :|
Server was including text ouside the object in the response that should be part of the header. Thanks for the assist guys!
Postman response:
Version OK 5.5.9-1ubuntu4.14
{"success":true,"response_code":"OK","api_version":1,"message":"PASSWORD RESET NOTIFICATION SENT SUCCESSFULLY","error":[],"base_url":"http:\/\/apiserver.com","data":[]}
i am using netty 4.1 embeded in Java and trying to retrive Data from a clients POST request in the pipeline. I tried several options i found online but nothing works...
Maybe someone has a useful thought on this.
Regards and thanks for everyone who helps.
Pipeline:
p.addLast ("codec", new HttpServerCodec ());
p.addLast("decoder", new HttpRequestDecoder());
p.addLast("encoder", new HttpRequestEncoder());
p.addLast("handler",new InboundHandlerA());
Handler:
private static class InboundHandlerA extends ChannelInboundHandlerAdapter{
#Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Connected!");
ctx.fireChannelActive();
}
public void channelRead (ChannelHandlerContext channelHandlerCtxt, Object msg) throws Exception {
System.out.println(msg);
}
}
Recieving HTTP requests using netty is simple, you can do this with the following pipeline:
// Provides support for http objects:
p.addLast("codec", new HttpServerCodec());
// Deals with fragmentation in http traffic:
p.addLast("aggregator", new HttpObjectAggregator(Short.MAX_VALUE));
// Deals with optional compression of responses:
// p.addLast("aggregator", new HttpContentCompressor());
p.addLast("handler",new InboundHandlerA());
This can be used with a custom SimpleChannelInboundHandler<FullHttpRequest>:
public class InboundHandlerA extends SimpleChannelInboundHandler<FullHttpRequest> {
#Override
public void channelActive(ChannelHandlerContext ctx) {
super.channelActive(ctx);
System.out.println("Connected!");
}
// Please keep in mind that this method
will be renamed to messageReceived(ChannelHandlerContext, I) in 5.0.
#Override
public void channelRead0 (ChannelHandlerContext ctx,
FullHttpRequest msg) throws Exception {
// Check for invalid http data:
if(msg.getDecoderResult() != DecoderResult.SUCCESS ) {
ctx.close();
return;
}
System.out.println("Recieved request!");
System.out.println("HTTP Method: " + msg.getMethod());
System.out.println("HTTP Version: " + msg.getProtocolVersion());
System.out.println("URI: " + msg.getUri());
System.out.println("Headers: " + msg.headers());
System.out.println("Trailing headers: " + msg.trailingHeaders());
ByteBuf data = msg.content();
System.out.println("POST/PUT length: " + data.readableBytes());
System.out.println("POST/PUT as string: ");
System.out.println("-- DATA --");
System.out.println(data.toString(StandardCharsets.UTF_8));
System.out.println("-- DATA END --");
// Send response back so the browser won't timeout
ByteBuf responseBytes = ctx.alloc().buffer();
responseBytes.writeBytes("Hello World".getBytes());
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, responseBytes);
response.headers().set(HttpHeaders.Names.CONTENT_TYPE,
"text/plain");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
response.content().readableBytes());
response.headers().set(HttpHeaders.Names.CONNECTION,
HttpHeaders.Values.KEEP_ALIVE);
ctx.write(response);
}
}
The code above is printing out all the details on a incoming message, including the post data. If you require only the post data, you can add a simple if-statement to filter on a POST response type
i have this http header :
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 15 Mar 2016 17:08:29 GMT
And what i want is to add 2 parameter: mytotalcount and mytotalresult.
i have to add this into my response however it's not working i can't do:
response.Headers.Add("mytotalcount", "10");
Also can't do
request.setAttribute("mytotalcount","10");
So i am blocked any ideas please ? Thanks for your help !
Here is the part of the code:
public class SitesResponse {
private Result result;
private List<GeographicSite> site;
public SitesResponse () {
this.result = new Result();
this.site = new ArrayList<GeographicSite>();
}
public List<GeographicSite> getSite() {
return site;
}
public void setSite(List<GeographicSite> site) {
this.site = site;
}
}
Ensuite l'autre classe qui utilise SitesResponse
SitesResponse response = new SitesResponse();
et
if (testiing) {
try {response = siteManager.geographicSitesAPIV1(args);
response.getResult().setCode("error");
response.getResult().setLabel("no addr found");
And this is what i tried ... earlier
System.out.println(response.toString().getBytes().toString()+"azezae");
// request.Headers.Add("headername", "headervalue");
// request.setAttribute("X-Total-Count","10");
//response.setAttribute("X-Result-Count", "7");
//response.setIntHeader("mytotalcount", 5);
//////////////////////////////////////////////////:::::::
In picture where i want to add my two parameters result from SOAPui
Hello again Thank you for your time,
i was trying to edited the wrong class so what i did you just
resp.addIntHeader(att, 10);
resp.addIntHeader(att2, 5);
Thank you for your time and answers!
I have 500 internal server error, every time when i try to send POST request via Retrofit. When i sending GET request, it sending correctly. I'm sure that with serverside everyting is ok. What's wrong with my code ?
String ENDPOINT = "http://52.88.40.210";
//model for request
FriendModel ff = new FriendModel();
ff.setFriendNumber("380935275259");
ff.setId(516);
ff.setNumber("380936831127");
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(ENDPOINT)
.build();
WayfAPI api = adapter.create(WayfAPI.class);
api.getFriendsLocation(ff, new Callback<List<FriendLocationModel>>() {
#Override
public void success(List<FriendLocationModel> friendLocationModels, Response response) {
for (FriendLocationModel ff : friendLocationModels) {
Log.d("myLogs", "===========Successful==========");
Log.d("myLogs", "Id: " + ff.getId());
Log.d("myLogs", "Number: " + ff.getNumber());
Log.d("myLogs", "GeoLocation: : " + ff.getGeoLocation());
}
}
#Override
public void failure(RetrofitError error) {
Log.d("myLogs", "-------ERROR-------");
Log.d("myLogs", Log.getStackTraceString(error));
}
});
}
Declaration of request:
#Headers({
"Accept: application/json",
"Content-type: application/json"
})
#POST("/api/geo/getLoc")
public void getFriendsLocation(#Body FriendModel friendModel, Callback<List<FriendLocationModel>> response);
Exampe of request and response from Postman:
It seems that in postman you're sending an array of FriendModel, but in your code you're sending a single object.
Just change the object you're sending, and instead of sending a single object, send a List as the server expects
List<FriendModel> friendsList = new ArrayList<FriendModel>();
FriendModel ff = new FriendModel();
ff.setFriendNumber("380935275259");
ff.setId(516);
ff.setNumber("380936831127");
friendsList.add(ff);
You should also change this signature:
public void getFriendsLocation(#Body FriendModel friendModel, Callback<List<FriendLocationModel>> response);
to
public void getFriendsLocation(#Body List<FriendModel> friendModel, Callback<List<FriendLocationModel>> response);