Code Monkey home page Code Monkey logo

jrestless's Introduction

JRestless

JRestless allows you to create serverless applications using JAX-RS.

Build Status codecov GitHub issues GitHub closed issues License

SonarQube: SonarQube - Quality Gate SonarQube - Coverage SonarQube - Bugs SonarQube - Vulnerabilities SonarQube - Tests SonarQube - Duplicated Blocks SonarQube - Technical Debt SonarQube - Code Smells

Table of Contents

Description

JRestless is a framework allowing you to build serverless JAX-RS applications or rather to run JAX-RS applications in FasS (Function as a Service) environments like AWS Lambda. This is achieved by providing a generic Jersey container that handles requests in the form of POJOs. For each FaaS environment there is a separate module acting as an integration layer between the actual environment and the generic Jersey container.

Since this framework is just a wrapper around or rather a container for Jersey, you can use almost all JAX-RS features plus Jersey's custom extensions like Spring integration - not Spring MVC, though since this functionality is provided by JAX-RS itself.

Supported Environments:

Motivation

The motivation for this project is to avoid a cloud vendor lock-in and to allow developers to run and test their code locally.

Features

  • Almost all JAX-RS features can be used (JSON/XML/text/... requests/responses, container request/response filters, etc.). Example: aws-gateway-showcase
  • Jersey extensions can be used. For example:
  • AWS Gateway Functions can also consume and produce binary data. Example: aws-gateway-binary
  • AWS Gateway Functions use the data added to the request by authorizers (Custom Authorizers or Cognito User Pool Authorizers) to create a Principal (CustomAuthorizerPrincipal or CognitoUserPoolAuthorizerPrincipal) within the SecurityContext containing all claims. Examples: aws-gateway-security-cognito-authorizer and aws-gateway-security-custom-authorizer
  • AWS Gateway Functions can use a CORS filter. Example: aws-gateway-cors
  • Injection of provider and/or function type specific values via @javax.ws.rs.core.Context into resources and endpoints:
    • All AWS functions can inject com.amazonaws.services.lambda.runtime.Context.
    • AWS Gateway Functions can also inject the raw request GatewayRequest
    • AWS Service Functions can also inject the raw request ServiceRequest
    • AWS SNS Functions can also inject the raw request SNSRecord
  • It's worth mentioning that AWS Gateway Functions is designed to be used with API Gateway's proxy integration type for Lambda Functions. So there are no limitations on the status code, the headers and the body you return.

Function Types

AWS

  • Gateway Functions are AWS Lambda functions that get invoked by AWS API Gateway. Usage example: aws-gateway-usage-example. Read More....
  • Service Functions are AWS Lambda functions that can either be invoked by other AWS Lambda functions or can be invoked directly through the AWS SDK. The point is that you don't use AWS API Gateway. You can abstract the fact that you invoke an AWS Lambda function away by using a special feign client (jrestless-aws-service-feign-client). Usage example: aws-service-usage-example. Read More....
  • SNS functions are AWS Lambda function that get invoked by SNS. This allow asynchronous calls to other Lambda functions. So when one Lambda function publishes a message to one SNS topic, SNS can then invoke all (1-N) subscribed Lambda functions. Usage example: aws-sns-usage-example. Read More....

Fn Project

Note: the framework is split up into multiple modules, so you choose which functionality you actually want to use. See Modules

Usage Example

AWS Usage Example

All examples, including the following one, can be found in a separate repository: https://github.com/bbilger/jrestless-examples

JRestless does not depend on the serverless framework but it simplifies the necessary AWS configuration tremendously and will be used for this example.

Install serverless (>= 1.0.2) as described in the docs https://serverless.com/framework/docs/guide/installing-serverless/

Setup your AWS account as described in the docs https://serverless.com/framework/docs/providers/aws/guide/credentials/

Create a new function using serverless

mkdir aws-gateway-usage-example
cd aws-gateway-usage-example
serverless create --template aws-java-gradle --name aws-gateway-usage-example
rm -rf src/main/java # remove the classes created by the template
mkdir -p src/main/java/com/jrestless/aws/examples # create the package structure

Replace serverless.yml with the following contents:

service: aws-gateway-usage-example-service

provider:
  name: aws
  runtime: java8
  stage: dev
  region: eu-central-1

package:
  artifact: build/distributions/aws-gateway-usage-example.zip

functions:
  sample:
    handler: com.jrestless.aws.examples.RequestHandler
    events:
      - http:
          path: sample/{proxy+}
          method: any

Replace build.gradle with the following contents:

apply plugin: 'java'

repositories {
  jcenter()
  mavenCentral()
}
dependencies {
  compile(
    'com.jrestless.aws:jrestless-aws-gateway-handler:0.6.0',
    'org.glassfish.jersey.inject:jersey-hk2:2.26',
    'org.glassfish.jersey.media:jersey-media-json-jackson:2.26'
  )
}
task buildZip(type: Zip) {
  archiveName = "${project.name}.zip"
  from compileJava
  from processResources
  into('lib') {
    from configurations.runtime
  }
}
build.dependsOn buildZip

Create a new JAX-RS resource and a response object (src/main/java/com/jrestless/aws/examples/SampleResource.java):

package com.jrestless.aws.examples;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/sample")
public class SampleResource {
  @GET
  @Path("/health")
  @Produces({ MediaType.APPLICATION_JSON })
  public Response getHealthStatus() {
    return Response.ok(new HealthStatusResponse("up and running")).build();
  }
  static class HealthStatusResponse {
    private final String statusMessage;
    HealthStatusResponse(String statusMessage) {
      this.statusMessage = statusMessage;
    }
    public String getStatusMessage() {
      return statusMessage;
    }
  }
}

Create the request handler (src/main/java/com/jrestless/aws/examples/RequestHandler.java):

package com.jrestless.aws.examples;

import org.glassfish.jersey.server.ResourceConfig;

import com.jrestless.aws.gateway.GatewayFeature;
import com.jrestless.aws.gateway.handler.GatewayRequestObjectHandler;

public class RequestHandler extends GatewayRequestObjectHandler {
  public RequestHandler() {
    // initialize the container with your resource configuration
    ResourceConfig config = new ResourceConfig()
      .register(GatewayFeature.class)
      .packages("com.jrestless.aws.examples");
    init(config);
    // start the container
    start();
  }
}

Build your function from within the directory aws-gateway-usage-example:

gradle build

This, amongst other things, creates a deployable version of your function (build/distributions/aws-gateway-usage-example.zip) using the dependent task buildZip.

Now you can deploy the function using serverless:

serverless deploy

If serverless is configured correctly, it should show you an endpoint in its output.

...
endpoints
  ANY - https://<SOMEID>.execute-api.eu-central-1.amazonaws.com/dev/sample/{proxy+}
...

Hit the endpoint:

curl -H 'Accept: application/json' 'https://<SOMEID>.execute-api.eu-central-1.amazonaws.com/dev/sample/health'
# {"statusMessage":"up and running"}

Fn Project Usage Example

A very good example of how to use JRestless on Fn Project can be found here: https://github.com/fnproject/fn-jrestless-example

Modules

JRestless is split up into multiple modules whereas one has to depend on the *-handler modules, only. jrestless-aws-gateway-handler is probably the most interesting one.

All modules are available in jcenter.

Alternative Projects

AWS

  • Java
    • lambadaframework provides similar functionality like JRestless. It implements some features of the JAX-RS standard and includes deployment functionality within the framework itself.
    • ingenieux/lambada Non-JAX-RS Java framework
    • aws-lambda-servlet run JAX-RS applications - uses Jersey and pretends to run in a servlet container
    • aws-serverless-java-container run JAX-RS (using Jersey), Spring (MVC) and Spark - pretends to run in a servlet container
  • JavaScript
  • Python
    • Zappa - run and deploy Python applications
    • Chalice - run and deploy Python applications

Limitations

AWS

  • for all function types
    • stateless only (you could utilize some cache like Redis, though)
    • AWS Lambda functions have a maximum execution time of 5 minutes
  • Gateway functions
    • AWS API Gateway has a timeout of 30 seconds
    • Multiple headers with same name are not supported

Fn Project

Release History

CHANGELOG

Meta

License

Distributed under Apache 2.0 license. See License for more information.

jrestless's People

Contributors

bbilger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jrestless's Issues

Proxy Custom Request Scoped Bindings

new AbstractBinder() {
  @Override
  protected void configure() {
    bindFactory(...tFactory.class)
      .to(....class)
      .prox(true) // <===== 
      .in(RequestScoped.class);
    }
  }
}

Restructure Modules

one aws-core project is enough; jrestless-aws-gateway-core and jrestless-aws-service-core is overkill

Problem serializing Response when deployed to AWS Lambda

Hi, I'm having an issue when I've deployed my Lambda to AWS and hooked it up to API Gateway.

I use the API Gateway Test functionality to test the Lambda. It makes the call, the code executes, but when I do the Response.ok()... I get the following messages in my CloudWatch log:

org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
SEVERE: MessageBodyWriter not found for media type=application/json, type=class com...

From what I've read, there's a problem with the glassfish jersey media implementation. So I'd like to just switch over to Jackson's implementation. Is this something you've run into or done in the past?

Thanks,
David

Add Support For SNS Events

add new event handler that support SNS events and passes them to Jersey

  • support injection of SNSEvent.SNS
  • use the message as body (headers: "Content-Type: application/json / text/plain?)
  • the topic name would be the name of the endpoint

(that's the point where we need a common AWS module)

JerseyTest fails with IllegalStateException in 0.4.0

When trying out version 0.4.0, I cannot get my previously running JerseyTests to work. They were running fine with 0.3.0. Sample code:

public class ResourceTest extends JerseyTest {
    @Override
    protected Application configure() {
        return new ResourceConfig()
                .register(GatewayFeature.class)
                .packages(RequestHandler.class.getPackage().getName());
    }

    @Test
    public void healthcheck() throws Exception {
        Response response = target("/health").request().get();
        assertThat(response.getStatusInfo()).isEqualTo(Response.Status.OK);
    }

With the following log output:

2016-12-17 11:56:40 ERROR ExceptionMapper:15 - Failure when processing request.
MultiException stack 1 of 1
java.lang.IllegalStateException: Proxiable context org.glassfish.jersey.process.internal.RequestScope@3e4f2b4d findOrCreate returned a null for descriptor SystemDescriptor(
	implementation=com.jrestless.aws.gateway.GatewayFeature$ReferencingGatewayRequestFactory
	contracts={com.jrestless.aws.gateway.io.GatewayRequest}
	scope=org.glassfish.jersey.process.internal.RequestScoped
	qualifiers={}
	descriptorType=PROVIDE_METHOD
	descriptorVisibility=NORMAL
	metadata=
	rank=0
	loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2@3a21dd9e
	proxiable=true
	proxyForSameScope=false
	analysisName=null
	id=125
	locatorId=1
	identityHashCode=310202442
	reified=true) and handle ServiceHandle(SystemDescriptor(
	implementation=com.jrestless.aws.gateway.security.CognitoUserPoolAuthorizerFilter
	contracts={javax.ws.rs.container.ContainerRequestFilter}
	scope=javax.inject.Singleton
	qualifiers={org.glassfish.jersey.internal.inject.Custom}
	descriptorType=CLASS
	descriptorVisibility=NORMAL
	metadata=
	rank=2000
	loader=null
	proxiable=null
	proxyForSameScope=null
	analysisName=null
	id=141
	locatorId=1
	identityHashCode=1492979664
	reified=true),531579585)
	at org.jvnet.hk2.internal.MethodInterceptorImpl.internalInvoke(MethodInterceptorImpl.java:92)
	at org.jvnet.hk2.internal.MethodInterceptorImpl.invoke(MethodInterceptorImpl.java:125)
	at org.jvnet.hk2.internal.MethodInterceptorInvocationHandler.invoke(MethodInterceptorInvocationHandler.java:62)
	at com.sun.proxy.$Proxy32.getRequestContext(Unknown Source)
	at com.jrestless.aws.gateway.security.AuthorizerFilter.getAuthorizerData(AuthorizerFilter.java:50)
	at com.jrestless.aws.gateway.security.AuthorizerFilter.filter(AuthorizerFilter.java:40)
	at org.glassfish.jersey.server.ContainerFilteringStage.apply(ContainerFilteringStage.java:132)
	at org.glassfish.jersey.server.ContainerFilteringStage.apply(ContainerFilteringStage.java:68)
	at org.glassfish.jersey.process.internal.Stages.process(Stages.java:197)
	at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:318)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
	at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384)
	at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:224)
	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:593)
	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:573)
	at java.lang.Thread.run(Thread.java:745)
Dec 17, 2016 11:56:40 AM org.glassfish.grizzly.http.server.NetworkListener shutdownNow
INFO: Stopped listener bound to [localhost:9998]

org.junit.ComparisonFailure: 
Expected :OK
Actual   :Internal Server Error

Maybe it has to do with the change from GatewayResourceConfig (in 0.3.0) to use the GatewayFeature? Please let me know if I can try out anything else, as I'm not too knowledgeable about Jersey's internals.

CHANGELOG not updated for 0.5.0

Hi bbliger,

It seems you forgot to update the changelog to include 0.5.0 release.

Thanks for the great work!!

PS: I was able to integrate dropwizard 1.0.6 with your project and deploy to AWS Lambda :-)

Handle BaseUri Correctly

baseUri is not equal to the basePath => handle correctly
deprecate constructors expecting baseUri

Examples: Add cognito oauth integration

Thanks so much for this software, looks really nice so far!

While going through the examples I was wondering if there was any chance of connecting with twitter/github/amazon oauth login, so one does not have the burden of managing users himself/herself.

note: I am pretty much a AWS newbie, so I might be just missing or misunderstanding things, so feel free to close if appropriate.

Add Execution Stage Information

Make the execution stage injectable.

  • add a new class ExecutionStage with a name property
  • derive the exection stage from com.amazonaws.services.lambda.runtime.Context#getInvokedFunctionArn()
    • invokedFunctionArn.split(":").length == 8 => invokedFunctionArn.length.split(":")[7] => alias
    • invokedFunctionArn.split(":").length == 7 => invokedFunctionArn.length.split(":")[6] => lambda function name => search for well known names like "dev", "development", "prod", "production" (serverless appends -- to the lambda function name)

the invokedFunctionArn looks like this arn:aws:lambda:<REGION>:<ID>:function:<LAMBDA FUNCTION NAME>:<ALIAS, optional>

DefaultGatewayRequest declared as final does not work with CDI

Hi,

I am trying to use jrestless with Weld integration (to use autodiscover feature) and I'm having the following exception:

WELD-001408: Unsatisfied dependencies for type GatewayRequest with qualifiers @default
at injection point [BackedAnnotatedParameter] Parameter 1 of [BackedAnnotatedConstructor] @Inject public com.jrestless.aws.gateway.security.CognitoUserPoolAuthorizerFilter(GatewayRequest) ...

It seems that the class com.jrestless.aws.gateway.io.DefaultGatewayRequest is final so it cannot be used with Weld. The specification states that final classes are not eligible for injection because of the way Proxy works.

Thanks!

onRequestFailure is never called

I've found that even when a request fails (eg. an exception is thrown in my handler), the onRequestSuccess hook is called with a status code of 500.

Is this what is meant to happen? What is the preferred method for dealing with exceptions? My desired approach would be to catch any exception in onRequestFailure and return an appropriate response.

I've noticed that in line 357 of JRestlessHandlerContainer.java, commit() is called. If I throw a RuntimeException here instead, my onRequestFailure hook is triggered and I can go on my merry way.

Hope this makes sense, and you can help!

How to get GatewayIdentity?

With 0.4.0 I have more of a question than an actual issue using my own authentication filter. Maybe just a little confused about the new factory setup. Here's what I had before for 0.3.0 to retrieve a gatewayIdentity in a filter:

public class AuthFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        GatewayIdentity gatewayIdentity = (GatewayIdentity) requestContext
               .getProperty(GatewayIdentityContextFactory.PROPERTY_NAME);

               
        ...
    }
}

How might I do this with 0.4.0? I am using IAM authentication for the API Gateway -- not custom or cognito userpool.

exceptions' stacktraces do not include the first line

My function is throwing an exception when its Jackson decoder tries to deserialize input JSON:

2017-05-15 22:47:56 <874465cf-39c0-11e7-8842-ab61849afd56> ERROR com.jrestless.core.container.JRestlessHandlerContainer:356 - container failure
Invalid date    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:62)
        at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:834)
        at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1093)

This specific exception is not the source of this issue. The issue is that the first line of the stacktrace is not being shown.

The relevant line seems to be here. Note that the first line of the stacktrace is written if we

LOG.error("first line: {}", error.getMessage());

Fix Maven issue

jrestless sounds really promising. Unfortunately it's not straightforward to get it running with Maven.

With the following pom.xml:

    <dependencies>
        <dependency>
            <groupId>com.jrestless.aws</groupId>
            <artifactId>jrestless-aws-gateway-handler</artifactId>
            <version>0.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>2.23</version>
        </dependency>
    </dependencies>
    
    <repositories>
        <repository>
            <id>jcenter</id>
            <url>http://jcenter.bintray.com</url>
        </repository>
    </repositories>

The build results in [ERROR] Failed to execute goal on project ***: Could not resolve dependencies for project ***: Could not find artifact org.glassfish.jersey.containers:project:jar:2.23 in jcenter (http://jcenter.bintray.com) -> [Help 1].

The problem is that org.glassfish.jersey.containers:project is being referenced within com.jrestless.core:jrestless-core-container as a dependency. Maven then thinks this is a dependency of type jar, where in fact it is of type pom. When trying to download the corresponding jar file, it gets a 404 from both jcenter and Maven Central and throws the error above.

The following workaround fixes the issue:

    <dependencies>
        <dependency>
            <groupId>com.jrestless.aws</groupId>
            <artifactId>jrestless-aws-gateway-handler</artifactId>
            <version>0.3.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish.jersey.containers</groupId>
                    <artifactId>project</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.23</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>2.23</version>
        </dependency>
    </dependencies>

It would be great if you could fix the dependencies to use jersey-server directly instead of relying on the project parent POM, so that the workaround is no longer needed in future versions. Thanks!

Running locally

What is the best way to run the lambda service locally? say inside tomcat or some? So to rest the endpoints like localhost:8080/api
thanks

Contribute

Jrestless look be a interesthing project and is very use full for me.
I I would like to contribute with jrestless.

Exist a roadmap ?
What is your pretension for the future of the project ?

Unexpected application root path behaviour

Currently the following combination :

  • API Gateway resource path = "/api/{+proxy}"
  • JAX-RS Resource class @path("/helloworld")

Does not resolve/work. It is expecting the root resource classes to have the same path prefix as the AG resource path (so @path("/api/..") in this example). This is unexpected. The combination above should result in :

https://mysite.com/{stage}/api/helloworld

Since the configuration implies the jersey application route is served by the {+proxy} section. It would be good if the Application instance root path is configurable (e.g. through @ApplicationPath which does not seem to be respected) or if the default is changed.

Weld integration

In order to Weld work with Jrestless I needed to change the packages to include META-INF/beans.xml.

Is it possible to include those files in next release?

Thanks!

clarify logging support

Firstly, thanks for authoring this project! It's greatly helped me get up to speed with Serverless!

I'm having some trouble with logging. I've tried following your example. However, after deploying such code and curling an endpoint, serverless logs -f $function_name doesn't indicate any logging is being done.

Here's my handler. I added a log to beforeHandleRequest, as well as to each method in my resource. None of these logs are showing up in serverless logs -f all (my function is named all).

Any idea why? Perhaps I should use a different command to obtain logs? Thanks.

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.