Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@QueryParam and @Context HttpServletRequest context is not working properly. #228

Closed
smanish-kumar opened this issue Jan 16, 2019 · 6 comments

Comments

@smanish-kumar
Copy link

  • Framework version: 1.3
  • Implementations: Jersey / Spring
    Scenario: I have downloaded the sample code for jersey pet store project. Completed the maven build and tired to test using AWS SAM Local. I was able to publish the sample pet store project on AWS SAM Local. However when, i use curl -s http://127.0.0.1:3000/pets?limit=1 from terminal in AWS SAM Local environment, the value of "limit" is not available in the public Pet[] listPets(@QueryParam("limit") int limit) method of the PetsResource.java class. Also, getParameterNames() method of the HttpServletRequest is not returning parameter names when used in AWS SAM Local environment. The same is happening for spring pet store as well.

Expected behavior: when curl -s http://127.0.0.1:3000/pets?limit=1 is used, it should show only 1 record.

Also, the getParameterNames() method of the HttpServletRequest should return "limit" as parameter in PetsResource.java class when curl -s http://127.0.0.1:3000/pets?limit=1 is used.

Actual behavior : It is displaying 10 records as below.

[{"id":"c33b4204-7bc0-4c46-b69d-4cc46d62bf69","breed":"Jack Russell Terrier","name":"Bear","dateOfBirth":1133369382990},{"id":"cd774218-8150-420c-abf7-7826d9656097","breed":"Jack Russell Terrier","name":"Abby","dateOfBirth":1282063782990},{"id":"f5eb57fc-ca0b-4ee8-9616-bc30faf7a70b","breed":"Jack Russell Terrier","name":"Chloe","dateOfBirth":1389026982990},{"id":"24d84825-e897-45e7-829d-5f69ce8b0117","breed":"Jack Russell Terrier","name":"Jack","dateOfBirth":1265215782990},{"id":"63c876f5-a96f-4112-bd7c-8d3aa3e27f99","breed":"Dalmatian","name":"Gracie","dateOfBirth":1122742182990},{"id":"766bbd96-fe25-4f8c-aa04-d6795f617064","breed":"Jack Russell Terrier","name":"Bella","dateOfBirth":1091551782991},{"id":"fcf42666-c8e2-4e93-8674-c83994c6d16e","breed":"Dalmatian","name":"Sasha","dateOfBirth":1160844582991},{"id":"49d4955b-5994-48e0-be30-f64796016ea6","breed":"Jack Russell Terrier","name":"Lola","dateOfBirth":1161622182991},{"id":"99c37613-ed60-49a6-8cef-1efb6acec93d","breed":"Dalmatian","name":"Bella","dateOfBirth":1530722982991},{"id":"be3231a2-dfa8-4f26-9df6-7cfb067383be","breed":"Bernese Mountain Dog","name":"Bear","dateOfBirth":1166633382991}]my_machine:my-spring-service user1$

Also, getParameterNames() method of the HttpServletRequest is returning null.

Steps to reproduce

Full log output

@sapessi
Copy link
Collaborator

sapessi commented Jan 16, 2019

Are you running in SAM local? Could you log the event? The framework depends on the multiValueHeaders and query string parameter fields, are those present in the SAM local event?

@smanish-kumar
Copy link
Author

Hi Stefano, Many thanks for the comment. Yes you are correct. I am using SAM Local for this.

I just downloaded jersey pet store code from https://github.com/awslabs/aws-serverless-java-container/tree/master/samples/jersey/pet-store and following the steps mentioned in the https://aws.amazon.com/blogs/opensource/java-apis-aws-lambda/ blog.

I am not sure about the event as I am using "sam local start-api --template sam.yaml" to start the server. Then using the curl command to send the request to the API. The curl command I am using is "curl -s http://127.0.0.1:3000/pets?limit=1"

So, are you suggesting that there are some other configuration in the SAM Local, which I need to look.
Please advise.

@smanish-kumar
Copy link
Author

Hi Stefano, below are code files for the reference:

  1. StreamLambdaHandler.java
    import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
    import com.amazonaws.serverless.proxy.internal.testutils.Timer;
    import com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler;
    import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletContextSupplier;
    import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletRequestSupplier;
    import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletResponseSupplier;
    import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
    import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
    import com.amazonaws.services.lambda.runtime.Context;
    import com.amazonaws.services.lambda.runtime.RequestStreamHandler;

import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("unused")
public class StreamLambdaHandler implements RequestStreamHandler {
private static final ResourceConfig jerseyApplication = new ResourceConfig()
//.packages("my.service")
.register(PetsResource.class)
.register(JacksonFeature.class)
.register(new AbstractBinder() {
@OverRide
protected void configure() {
bindFactory(AwsProxyServletContextSupplier.class)
.to(ServletContext.class).in(RequestScoped.class);
bindFactory(AwsProxyServletRequestSupplier.class)
.to(HttpServletRequest.class).in(RequestScoped.class);
bindFactory(AwsProxyServletResponseSupplier.class)
.to(HttpServletResponse.class).in(RequestScoped.class);
}
});
private static final JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler
= JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);

public StreamLambdaHandler() {
    // we enable the timer for debugging. This SHOULD NOT be enabled in production.
//	System.out.println(" Constructor called !");
    Timer.enable();
}

@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
        throws IOException {
//	System.out.println(" Hi There !");

    handler.proxyStream(inputStream, outputStream, context);
}

}


  1. PetsResource.java

package my.service;

import my.service.resource.Pet;
import my.service.resource.PetData;

import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import java.util.Map;
import java.util.UUID;

@path("/pets")
public class PetsResource {
@context
private ServletContext myContext;
@context
private javax.servlet.http.HttpServletRequest myServletRequest;
@context
private javax.servlet.http.HttpServletResponse myServletResponse;
@post
@produces(MediaType.APPLICATION_JSON)
@consumes(MediaType.APPLICATION_JSON)
public Response createPet(final Pet newPet) {
if (newPet.getName() == null || newPet.getBreed() == null) {
return Response.status(400).entity(new Error("Invalid name or breed")).build();
}

    Pet dbPet = newPet;
    dbPet.setId(UUID.randomUUID().toString());
    return Response.status(200).entity(dbPet).build();
}

@GET
@Produces(MediaType.APPLICATION_JSON)
public Pet[] listPets(@QueryParam("limit") int limit) {
	Map pMap = myServletRequest.getParameterMap();
	System.out.println(" pMap = " + pMap.size());
	
	System.out.println(" Hi There limit is = " + limit);
    if (limit < 1) {
    	
        limit = 10;
    }

    Pet[] outputPets = new Pet[limit];

    for (int i = 0; i < limit; i++) {
        Pet newPet = new Pet();
        newPet.setId(UUID.randomUUID().toString());
        newPet.setName(PetData.getRandomName());
        newPet.setBreed(PetData.getRandomBreed());
        newPet.setDateOfBirth(PetData.getRandomDoB());
        outputPets[i] = newPet;
    }

    return outputPets;
}

@Path("/{petId}") @GET
@Produces(MediaType.APPLICATION_JSON)
public Pet getPetDetails() {
    Pet newPet = new Pet();
    newPet.setId(UUID.randomUUID().toString());
    newPet.setBreed(PetData.getRandomBreed());
    newPet.setDateOfBirth(PetData.getRandomDoB());
    newPet.setName(PetData.getRandomName());
    return newPet;
}

}


  1. sam.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Example Pet Store API written in jersey with the aws-serverless-java-container library

Globals:
Api:
# API Gateway regional endpoints
EndpointConfiguration: REGIONAL

Resources:
PetStoreFunction:
Type: AWS::Serverless::Function
Properties:
Handler: my.service.StreamLambdaHandler::handleRequest
Runtime: java8
CodeUri: target/serverless-jersey-example-1.0-SNAPSHOT-lambda-package.zip
MemorySize: 512
Policies: AWSLambdaBasicExecutionRole
Timeout: 120
Events:
GetResource:
Type: Api
Properties:
Path: /{proxy+}
Method: any

Outputs:
JerseyPetStoreApi:
Description: URL for application
Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/pets'
Export:
Name: JerseyPetStoreApi


  1. pom.xml


4.0.0

<groupId>test.service</groupId>
<artifactId>test-service</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Serverless Jersey API</name>
<url>https://github.com/awslabs/aws-serverless-java-container</url>

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <jersey.version>2.27</jersey.version>
    <jackson.version>2.9.7</jackson.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.amazonaws.serverless</groupId>
        <artifactId>aws-serverless-java-container-jersey</artifactId>
        <version>1.3</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>${jersey.version}</version>
        <exclusions>
            <exclusion>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
            </exclusion>
            <exclusion>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
            </exclusion>
            <exclusion>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>${jersey.version}</version>
        <!-- excluding redundant javax.inject dependency -->
        <exclusions>
            <exclusion>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

<profiles>
    <profile>
        <id>shaded-jar</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.3</version>
                    <configuration>
                        <createDependencyReducedPom>false</createDependencyReducedPom>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
    <profile>
        <id>assembly-zip</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <build>
            <plugins>
                <!-- don't build a jar, we'll use the classes dir -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.1.1</version>
                    <executions>
                        <execution>
                            <id>default-jar</id>
                            <phase>none</phase>
                        </execution>
                    </executions>
                </plugin>
                <!-- select and copy only runtime dependencies to a temporary lib folder -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>3.1.1</version>
                    <executions>
                        <execution>
                            <id>copy-dependencies</id>
                            <phase>package</phase>
                            <goals>
                                <goal>copy-dependencies</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>${project.build.directory}${file.separator}lib</outputDirectory>
                                <includeScope>runtime</includeScope>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>3.1.0</version>
                    <executions>
                        <execution>
                            <id>zip-assembly</id>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>
                            <configuration>
                                <finalName>${project.artifactId}-${project.version}</finalName>
                                <descriptors>
                                    <descriptor>src${file.separator}assembly${file.separator}bin.xml</descriptor>
                                </descriptors>
                                <attach>false</attach>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

@sapessi
Copy link
Collaborator

sapessi commented Jan 18, 2019

Just check on the SAM local repo - looks like this is indeed not supported yet in SAM local. I will try to contribute the fix for them.

@sapessi
Copy link
Collaborator

sapessi commented Feb 12, 2019

Checking in with @jfuss on whether they have an issue tracking multivalueQueryParams and headers support in SAM local?

@sapessi
Copy link
Collaborator

sapessi commented Jun 26, 2019

The new release of the SAM CLI supports the multi-value fields. You can install it from the repo's head: brew install aws-sam-cli --head. Closing this issue.

@sapessi sapessi closed this as completed Jun 26, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants