Coder Social home page Coder Social logo

Comments (11)

sapessi avatar sapessi commented on August 12, 2024 1

Thanks Daniel,

You are getting a 403 on the POST because there isn't an HTTP method defined in the /contacts resource in API Gateway. The ANY method you have defined there in the {proxy+} resource will only handle calls to sub-resources of contacts, for example /contacts/123. To handle requests to the contacts resource your API should look like this:

/
|_ /contacts
   |_ANY -> Lambda (POST/GET calls to /contacts)
   |_/{proxy+}
      |_ANY -> Lambda (calls to /contacts/123 - could be the same Lambda function for both

This is a start. I'll look into the code today but the 405 sounds like something with Jersey. I'll let you know what I find.

from aws-serverless-java-container.

sapessi avatar sapessi commented on August 12, 2024

Looking into it. Just to confirm, if you start this on your local machine using the embedded container in Jersey it responds correctly?

Could you also share a Swagger file with your API Gateway configuration (or a screenshot of the console?)

from aws-serverless-java-container.

sapessi avatar sapessi commented on August 12, 2024

I've tried to test something similar in local with Jersey 2.24. Looks like you cannot define a method with a sub-path in the interface, you have to define both in the implementation. Note that all of these tests are with the embedded Grizzly container in local. We'll get to Lambda and API Gateway next.

The DELETE method works for me when the interface is defined like this:

public interface ContactResource
{
    @DELETE // you could skip this, it's here just for clarity
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    public Response deleteContact(String id);

    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    public Response createContact(Map<String, String> co);
}

And the implementation looks like this

@Path("contacts")
public class ContactResourceImpl implements ContactResource {

    @DELETE
    @Path("{id}")
    public Response deleteContact(@PathParam("id") String id)
    {
        return Response.status(204).build();
    }

    public Response createContact(Map<String, String> co)
    {
        return Response.status(200).build();
    }
}

from aws-serverless-java-container.

davidmco65 avatar davidmco65 commented on August 12, 2024

So when I define in both Interface and Implementation, I get Duplicate definition errors. It thinks both the Interface and Implementation are definitions.

So I ran my JUnit tests, which I'm assuming fires up Grizzly. All tests worked. I've actually got more methods:

@HEAD
@Path("/ping")
@Consumes({MediaType.APPLICATION_JSON})
public Response ping(@QueryParam("numAdditionalInstancesToPing") int numAdditionalInstancesToPing);

@DELETE
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("/{proxy}")
public Response deleteContact(@PathParam("proxy") String id);

@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response createContact(ContactObject co);

@PUT
@Path("/{proxy}")
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public Response updateContact(@PathParam("proxy") String id, ContactObject co);

@GET
@Produces({MediaType.APPLICATION_JSON})
public Response getByCustId(@QueryParam("cid") String cid);

@GET
@Path("/{proxy}")
@Produces({MediaType.APPLICATION_JSON})
public Response getById(@PathParam("proxy") String id);

And it appears that all my tests work just fine.

Thanks,
David

from aws-serverless-java-container.

sapessi avatar sapessi commented on August 12, 2024

Is there a repo where we can take a look at the entire app? When I initialize my code with the @Path("{id}) only declared in the interface I get a 404 on the DELETE method, as if it couldn't find it.

On the 1st question, 403 is normally returned by API Gateway when you try to open a method that is not defined in the API. Would be good to see the API Gateway definition.

405 on the other hand, it's definitely not API Gateway unless you have explicitly defined that as a response code.

from aws-serverless-java-container.

davidmco65 avatar davidmco65 commented on August 12, 2024

Unfortunately, this is a private business app and I can't share all of the sources. I can share more of the code around this resource with a little editing.
Here's the API definition:
image

image

image

Complete (scrubbed) Resource interface:

package com.microstar.tap3.contact;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.microstar.tap3.contact.api.ContactObject;

public interface ContactResource
{

	/*
	 * Returns nothing; just serves to keep the Lambda warm.
	 * <p>
	 * This method always returns immediately with an 200 OK status and no body.
	 * 
	 * @return	200 OK status with no body.
	 */
	@HEAD
	@Path("/ping")
	@Consumes({MediaType.APPLICATION_JSON})
	public Response ping(@QueryParam("numAdditionalInstancesToPing") int numAdditionalInstancesToPing);
	
	@DELETE
	@Consumes({MediaType.APPLICATION_JSON})
	@Produces({MediaType.APPLICATION_JSON})
	@Path("/{proxy}")
	public Response deleteContact(@PathParam("proxy") String id);

	@POST
	@Consumes({MediaType.APPLICATION_JSON})
	@Produces({MediaType.APPLICATION_JSON})
	public Response createContact(ContactObject co);

	@PUT
	@Path("/{proxy}")
	@Produces({MediaType.APPLICATION_JSON})
	@Consumes({MediaType.APPLICATION_JSON})
	public Response updateContact(@PathParam("proxy") String id, ContactObject co);

	@GET
//	@Path("/{cid}")
	@Produces({MediaType.APPLICATION_JSON})
	public Response getByCustId(@QueryParam("cid") String cid);

	@GET
	@Path("/{proxy}")
	@Produces({MediaType.APPLICATION_JSON})
	public Response getById(@PathParam("proxy") String id);

}

And complete (scrubbed) Resource impl:

import java.util.*;

import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Path("/contacts")
public class ContactResourceImpl implements ContactResource
{
    private static final Logger 	LOG = LoggerFactory.getLogger(ContactResourceImpl.class);

    public Response deleteContact(String id)
    {
        return deleteContact(id, null);
    }

    /* (non-Javadoc)
     * @see com.microstar.tap3.contact.ContactResource#deleteContact(java.lang.String)
     */
    public Response deleteContact(String id, String cid)
    {
        LOG.debug("Entering contacts::DELETE /" + id);
        return Response.ok().build();
    }

    /* (non-Javadoc)
     * @see com.microstar.tap3.contact.ContactResource#createContact(com.microstar.tap3.contact.api.ContactObject)
     */
    public Response createContact(Map<String, String> co)
    {
        LOG.debug("Entering contacts::POST");
        return Response
                    .status(Status.CREATED)
                    .entity(co)
                    .build();
    }

    /* (non-Javadoc)
     * @see com.microstar.tap3.contact.ContactResource#updateContact(java.lang.String, com.microstar.tap3.contact.api.ContactObject)
     */
    public Response updateContact(String id, Map<String, String> co)
    {
        LOG.debug("Entering contacts::PUT /" + id);
        return Response
                    .ok()
                    .build();
    }

    /* (non-Javadoc)
     * @see com.microstar.tap3.contact.ContactResource#getByCustId(java.lang.String)
     */
    public Response getByCustId(String cid)
    {
        LOG.info("Entering contacts::GET ?cid=" + cid);
        return Response.ok()
                    .entity(Collections.EMPTY_MAP)
                    .build();
    }

    /* (non-Javadoc)
     * @see com.microstar.tap3.contact.ContactResource#getById(java.lang.String)
     */
    public Response getById(String id)
    {
        LOG.info("Entering contacts::GET /" + id);
        return Response
                    .ok()
                    .entity(Collections.EMPTY_MAP)
                    .build();
    }

    public Response ping(int numAdditionalInstancesToPing)
    {
        LOG.info("Entering ping::" + numAdditionalInstancesToPing);
        LOG.info("ContactResourceImpl: " + this);
        LOG.info("Thread: " + Thread.currentThread().getId());

        return Response
                .ok()
                .build();
    }
}

from aws-serverless-java-container.

davidmco65 avatar davidmco65 commented on August 12, 2024

Oh wow! I didn't know you had to do that! Thanks so much! That helps a lot. I'll try that out very shortly.

David

from aws-serverless-java-container.

sapessi avatar sapessi commented on August 12, 2024

I also edited your comment to add an even more scrubbed version of the impl class.

from aws-serverless-java-container.

sapessi avatar sapessi commented on August 12, 2024

Assuming the API Gateway configuration fixes your 403 on the POST. I'm now trying to replicate the 405 on the DELETE method without much luck.

When I start the server in local with your latest code, it responds to the DELETE method as expected. Same when I use Lambda and API Gateway.

This is the class I'm using to run the local tests:

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;

import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;

public class Service {
    public static void main(String[] args) throws IOException {
        System.out.println("Starting Embedded Jersey HTTPServer...\n");
        HttpServer httpServer = createHttpServer();

        httpServer.start();
    }

    private static HttpServer createHttpServer() throws IOException {
        ResourceConfig rc = new ResourceConfig()
                .packages("com.sapessi.testapi") // this is the package where I created your Contact class
                .register(JacksonFeature.class)
                .register(LoggingFeature.class);

        return GrizzlyHttpServerFactory.createHttpServer(getServerUri(), rc);
    }

    private static URI getServerUri() {
        return UriBuilder.fromUri("http://" + getLocalHostname() + "/").port(8085).build();
    }

    private static String getLocalHostname() {
        String hostName = "localhost";
        try {
            hostName = InetAddress.getLocalHost().getCanonicalHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return hostName;
    }
}

And this is my simple Lambda handler:

import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
    private ResourceConfig jerseyApplication = new ResourceConfig()
            .packages("com.sapessi.testapi")
            .register(JacksonFeature.class);
    private JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler
            = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);

    public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
        return handler.proxy(awsProxyRequest, context);
    }
}

from aws-serverless-java-container.

davidmco65 avatar davidmco65 commented on August 12, 2024

So adding the additional method under the /contacts resource made everything work!

Not sure I totally understand why, but as long as it works, I'm happy.

I'd also be interested in contributing to this project if you are looking for developers/maintainers. Let me know.

Thanks,
David

from aws-serverless-java-container.

sapessi avatar sapessi commented on August 12, 2024

Thanks David, I'm currently working on issues #15 and #16. However, if there are other features you need in the library, feel free to open a new issue and send a pull request. We are always looking for feedback on the library.

I'm going to close this issue for now. thanks for all the help to debug this.

from aws-serverless-java-container.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.