1

I have several servlets, which

  1. take JSON-encoded requests as inputs,
  2. process them and
  3. return responses to the client as JSON-encoded objects.

Up to now I used Android as client (sample Android code see below).

Now I want to write a plain old Java program, which would send requests and receive the responses (do the same as the Android code). For this purpose I wrote a Java test (code see below, section Java code) and ran it.

At the client side I get this error:

21:43:38.930 [main] ERROR r.a.c.t.TestAcceptanceProcedure1 - 
java.io.IOException: Server returned HTTP response code: 405 for URL: http://myserver/myapp/rest/GetUserIdService
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) ~[na:1.6.0_23]
    at testclient.TestAcceptanceProcedure1.test(TestAcceptanceProcedure1.java:47) ~[test-classes/:na]

In the server log, I see this message:

WARNING: No operation matching request path "/myapp/rest/GetUserIdService" is found, Relative Path: /, HTTP Method: GET, ContentType: */*, Accept: text/html,image/gif,image/jpeg,*/*,*/*;q=.2,. Please enable FINE/TRACE log level for more details

Question: How should I change my Java test to fix this error?

Note that the server is up and running (when I execute the Android code, it works).

Android code:

Sending the request and receiving the response:

final GetSimulationStatusRequest request = new GetSimulationStatusRequest();
final String json = Utils.convertToJson(request, getClass());
final String serverUrl = Utils.getServerUrl(this, "GetSimulationStatusService");

final IGetSimulationStatusAsyncTask getSimulationStatusTask = 
        asyncTaskFactory.createGetSimulationStatusAsyncTask(getWebServiceHelper());

Utils.setRequestAndServerUrl(json, serverUrl, getSimulationStatusTask);

final GetSimulationStatusResponse simulationStatusReponse = 
        getSimulationStatusTask.get();

Utils.convertToJson:

public static String convertToJson(final Object aRequest, Class<? extends Activity> aClass) {
    final ObjectMapper mapper = new ObjectMapper();
    String json = null;

    try {
        json = mapper.writeValueAsString(aRequest);
    } catch (final JsonProcessingException exception) {
        Log.e(aClass.getSimpleName(), exception.getLocalizedMessage(),
                exception);
    }
    return json;
}

Utils.setRequestAndServerUrl:

public static void setRequestAndServerUrl(final String aJson,
        final String aServerUrl, final IAsyncTask aTask) {
    aTask.addNameValuePair("request", aJson);
    aTask.sendRequest(new String[] { aServerUrl });
}

GetSimulationStatusAsyncTask:

public class GetSimulationStatusAsyncTask 
    extends AsyncTask<String, String, GetSimulationStatusResponse> 
    implements IGetSimulationStatusAsyncTask {
    private static final String TAG = GetSimulationStatusAsyncTask.class.getSimpleName();
    private IWebServiceTaskHelper helper;
    private ICcpResponseParser<GetSimulationStatusResponse> responseParser = 
            new CcpResponseParser<GetSimulationStatusResponse>();


    public GetSimulationStatusAsyncTask(final IWebServiceTaskHelper aHelper) {
        helper = aHelper;
    }

    @Override
    public void addNameValuePair(final String aName, final String aValue) {
        helper.addNameValuePair(aName, aValue);
    }

    @Override
    protected GetSimulationStatusResponse doInBackground(String... aArgs) {
        return (GetSimulationStatusResponse)Utils.processResponse(this.helper, TAG, responseParser, 
                GetSimulationStatusResponse.class, aArgs);
    }

    @Override
    public void sendRequest(final String[] aArgs) {
        execute(aArgs);
    }
}

Java code:

@Test
public void test() throws JsonProcessingException, MalformedURLException {
    final GetUserIdRequest request = new GetUserIdRequest();

    request.setDeviceId("PC1");

    final String requestAsString = convertToJson(request);
    final String serverUrl = getServerUrl("GetUserIdService");

    final URL url = new URL(serverUrl);
    HttpURLConnection connection = null;
    InputStream inputStream = null;
    try {

        connection = (HttpURLConnection) url.openConnection();
        connection.addRequestProperty("request", requestAsString);
        connection.connect();

        inputStream = connection.getInputStream();
        final String responseAsString = IOUtils.toString(inputStream);

        LOGGER.debug("responseAsString: " + responseAsString);

    } catch (final IOException exception) {
        LOGGER.error("", exception);
    }
    finally
    {
        IOUtils.closeQuietly(inputStream);
    }
}

private String convertToJson(final GetUserIdRequest aRequest) throws JsonProcessingException {
    final ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(aRequest);
}

private String getServerUrl(final String aServiceName)
{
    return "http://myserver.com/myapp/rest/" + aServiceName;
}

Update 1 (09.10.2013 12:23 MSK):

@Path("/GetSimulationStatusService")
public class GetSimulationStatusService extends BaseCcpService  {
  private GetSimulationStatusRequestParser requestParser = 
      new GetSimulationStatusRequestParser();

  @POST
  @Produces("text/plain")
  public String getSimulationStatus(@FormParam("request") final String aRequestJson) 
      throws JsonProcessingException
  {
    final GetSimulationStatusRequest request = requestParser.convert(aRequestJson);

    final GetSimulationStatusResponse response = new GetSimulationStatusResponse();

    response.setRequestId(request.getId());
    response.setCycle(getPersistence().getCurrentCycle(request.getUserId()));
    response.setLabourForce(getPersistence().getLabourForceSimulationParameter(
        request.getUserId()));

    return getObjectMapper().writeValueAsString(response);
  }
}

Update 2 (09.10.2013 20:48 MSK): When I change the code like shown below, I get 500 HTTP response. At the server side, the aRequest argument of the method GetUserIdService.getUserId is null.

        connection = (HttpURLConnection) url.openConnection();
        connection.addRequestProperty("request", requestAsString);
        connection.setRequestMethod("POST"); // Added this line
        connection.connect();

Update 3 (09.10.2013 23:15): This one works:

@Test
public void test() throws JsonProcessingException, MalformedURLException 
{
    final GetUserIdRequest request = new GetUserIdRequest();

    request.setDeviceId("PC1");

    final String requestAsString = convertToJson(request);
    final String serverUrl = getServerUrl("GetUserIdService");

    final URL url = new URL(serverUrl);
    HttpURLConnection connection = null;
    InputStream inputStream = null;
    OutputStream outputStream = null;
    try {

        connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
        connection.connect();

        outputStream = connection.getOutputStream();

        IOUtils.write("request=" + requestAsString, outputStream);

        inputStream = connection.getInputStream();
        final String responseAsString = IOUtils.toString(inputStream);

        LOGGER.debug("responseAsString: " + responseAsString);

    } catch (final IOException exception) {
        LOGGER.error("", exception);
    }
    finally
    {
        IOUtils.closeQuietly(inputStream);
        IOUtils.closeQuietly(outputStream);
    }
}
Mentiflectax
  • 13,367
  • 41
  • 152
  • 285

1 Answers1

1

The 405 HTTP error code means that given method (GET) is not supported by the endpoint. Probably instead of GET request you want to send POST. I don't know what kind of request is sent by Android client.

Do you have endpoint specification/documentation?

Here you'll find information how to invoke POST using plain Java API. If you can use external libraries in your test then it can be achieved a lot easier using RESTEasy.

Community
  • 1
  • 1
ragnor
  • 2,388
  • 1
  • 21
  • 23
  • Ad endpoint spec: Do you mean files like `web.xml` or `beans.xml` from the server? – Mentiflectax Oct 09 '13 at 08:12
  • No, I mean API spec i.e. https://dev.twitter.com/docs/api/1.1. If the API is under your control you can post code of service that handle /myapp/rest/GetUserIdService. If you don't have an access to source code of the API, try with POST request. – ragnor Oct 09 '13 at 08:21
  • You are probably right that I should change the request type to `POST`. I'll try it in the evening. And I posted the code of the service (update 1). – Mentiflectax Oct 09 '13 at 08:24
  • The `@POST` annotation on `getSimulationStatus` indicates that you have to send POST not GET request to the endpoint. – ragnor Oct 09 '13 at 09:10
  • I tried to set request type to POST. See update 2 for the results. – Mentiflectax Oct 09 '13 at 16:51
  • You are one step closer to get correct result, the endpoint is now invoked but the body of your request is empty. The `connection.addRequestProperty` method is not for setting body of the request instead use `connection.getOutputStream().write(...)` – ragnor Oct 09 '13 at 17:00