Showing posts with label guice. Show all posts
Showing posts with label guice. Show all posts

Monday, August 28, 2017

WebSocket.acceptWithActor and @Inject() in the Actor (Play 2.5)

Leave a Comment

WebSocket.acceptWithActor instantiates a new Akka actor without making use of Guice.

With Play 2.4, using the injector for my actor was still possible by importing play.api.Play.current.

Snippet from ReactiveMongo documentation:

import scala.concurrent.Future  import play.api.Play.current // should be deprecated in favor of DI import play.api.libs.concurrent.Execution.Implicits.defaultContext  import play.modules.reactivemongo.ReactiveMongoApi import play.modules.reactivemongo.json.collection.JSONCollection  object Foo {   lazy val reactiveMongoApi = current.injector.instanceOf[ReactiveMongoApi]    def collection(name: String): Future[JSONCollection] =     reactiveMongoApi.database.map(_.collection[JSONCollection](name)) } 

But in Play 2.5, play.api.Play.current is deprecated. How can I still inject ReactiveMongoApi in my actor? What is the recommended way of using an instance of ReactiveMongoApi in my actor?

Here is my code which works with Play 2.4 because my custom actor class ClientActor has access to ReactiveMongoApi through current.injector.instanceOf[ReactiveMongoApi]:

@Singleton class Application @Inject() (system: ActorSystem) extends Controller {    val midiDiscoveryActor = system.actorOf(MidiDiscoveryActor.props, "midi-discovery-actor")   val midiActor = system.actorOf(MidiActor.props(midiDiscoveryActor), "midi-actor")    def index(page: String) = Action {     Ok(views.html.index(page))   }    def bidirectional = WebSocket.acceptWithActor[JsValue, JsValue] { request => out =>     ClientActor.props(out, midiActor, midiDiscoveryActor)   }  } 

2 Answers

Answers 1

I don't think this is possible. Quoting James Roper:

The helpers that Play provides for dependency injecting actors are suited for a limited number of use cases. Though, the helpers are really just very thin wrappers over some common requirements - they're not needed at all. In the case Play's WebSocket actor support, the thing is, generally you want to manually instantiate the actor since you have to somehow pass it the out ActorRef. So, you can either do this using Guice assisted inject, and define a factor interface that takes the out actor ref (and whatever other arguments you want to pass to it), or simply instantiate it manually, passing dependencies from the controller to the actor, for example:

class MyController @Inject() (myDep: MyDep) extends Controller {   def socket = WebSocket.acceptWithActor[String, String] { request => out =>     MyWebSocketActor.props(out, myDep)   } } 

Answers 2

Play 2.5 has built in support for DI.

MidiActor signature needs to be modified as said below.

class MidiActor@Inject() (configuration: Configuration,  @Named("midi-discovery-actor") midiDiscoveryActor: ActorRef) extends Actor with InjectedActorSupport{ ....... } 

Create new Module and enable in application.conf

play.modules.enabled += MyModule  class MyModule extends AbstractModule with AkkaGuiceSupport {   def configure = {     bindActor[MidiDiscoveryActor]("midi-discovery-actor")     bindActor[MidiActor]("midi-actor")   } } 

Change your controller as below

@Singleton class Application @Inject() (system: ActorSystem,@Named("midi-actor") midiActor: ActorRef, @Named("midi-discovery-actor") midiDiscoveryActor: ActorRef) (implicit ec: ExecutionContext)  extends Controller {    def index(page: String) = Action {     Ok(views.html.index(page))   }    def bidirectional = WebSocket.acceptWithActor[JsValue, JsValue] { request => out =>     ClientActor.props(out, midiActor, midiDiscoveryActor)   }  } 
Read More

Thursday, June 29, 2017

Firebase multi-tenancy with play framework depends on header value of HTTP request

Leave a Comment

I have a play framework project that provides APIs that shared between multiple front-ends, currently, I'm working on single front-end but I want to create a multi-tenant backend, each front-end got its own Firebase account.

My problem that I have to consider which firebase project to access depends on the request header value, that came with different values depends on the front end.

What I have now: FirebaseAppProvider.java:

public class FirebaseAppProvider implements Provider<FirebaseApp> {       private final Logger.ALogger logger;     private final Environment environment;     private final Configuration configuration;      @Inject     public FirebaseAppProvider(Environment environment, Configuration configuration) {         this.logger = Logger.of(this.getClass());         this.environment = environment;         this.configuration = configuration;     }      @Singleton     @Override     public FirebaseApp get() {         HashMap<String, String> firebaseProjects = (HashMap<String, String>) configuration.getObject("firebase");         firebaseProjects.forEach((websiteId, projectId) -> {             FileInputStream serviceAccount = null;             try {                 serviceAccount = new FileInputStream(environment.classLoader().getResource(String.format("firebase/%s.json", projectId)).getPath());             } catch (FileNotFoundException e) {                 e.printStackTrace();                 return;             }              FirebaseOptions options = new FirebaseOptions.Builder().setCredential(FirebaseCredentials.fromCertificate(serviceAccount))                     .setDatabaseUrl(String.format("https://%s.firebaseio.com/", projectId))                     .build();               FirebaseApp firebaseApp = FirebaseApp.initializeApp(options, projectId);              logger.info("FirebaseApp initialized");         });          return FirebaseApp.getInstance();     } } 

Also for Database: FirebaseDatabaseProvider.java

public class FirebaseDatabaseProvider implements Provider<FirebaseDatabase> {      private final FirebaseApp firebaseApp;     public static List<TaxItem> TAXES = new ArrayList<>();      @Inject     public FirebaseDatabaseProvider(FirebaseApp firebaseApp) {         this.firebaseApp = firebaseApp;         fetchTaxes();     }      @Singleton     @Override     public FirebaseDatabase get() {         return FirebaseDatabase.getInstance(firebaseApp);     }      @Singleton     public DatabaseReference getUserDataReference() {         return this.get().getReference("/usersData");     }      @Singleton     public DatabaseReference getTaxesConfigurationReference() {         return this.get().getReference("/appData/taxConfiguration");     }     private void fetchTaxes() {         DatabaseReference bundlesRef = getTaxesConfigurationReference().child("taxes");         bundlesRef.addValueEventListener(new ValueEventListener() {             @Override             public void onDataChange(DataSnapshot dataSnapshot) {                 TAXES.clear();                 dataSnapshot.getChildren().forEach(tax -> TAXES.add(tax.getValue(TaxItem.class)));                 Logger.info(String.format("==> %d taxes records loaded", TAXES.size()));             }              @Override             public void onCancelled(DatabaseError databaseError) {                 Logger.warn("The read failed: " + databaseError.getCode());             }         });     } } 

So I bind them as well from Module.java:

public class Module extends AbstractModule {      @Override     public void configure() {        bind(FirebaseApp.class).toProvider(FirebaseAppProvider.class).asEagerSingleton();         bind(FirebaseAuth.class).toProvider(FirebaseAuthProvider.class).asEagerSingleton();         bind(FirebaseDatabase.class).toProvider(FirebaseDatabaseProvider.class).asEagerSingleton();     }  } 

my ActionCreator:

public class ActionCreator implements play.http.ActionCreator {      @Inject     public ActionCreator() {     }      @Override     public Action createAction(Http.Request request, Method actionMethod) {         switchTenancyId(request);         return new Action.Simple() {             @Override             public CompletionStage<Result> call(Http.Context ctx) {                 return delegate.call(ctx);             }         };     }      private void switchTenancyId(Http.RequestHeader request) {         // DO something here     }      private Optional<String> getTenancyId(Http.RequestHeader request) {         String websiteId = request.getHeader("Website-ID");         System.out.println(websiteId);         return null;     } } 

What I want is when I use Database service, or auth service, I read the website id and decide which firebase project to access, I really tried the solution like this answer here: Multi tenancy with Guice Custom Scopes and Jersey

Please note I'm willing to use differents projects, not the same firebase project for each front-end.

But kinda lost, especially the request can be only accessed from controller or ActionCreator, so what I got from the question above is load providers by key into ThreadLocal and switch them for each request depends on the annotation, but I was unable to do this because of the lack of knowledge.


The minimized version of my project can be found here: https://github.com/almothafar/play-with-multi-tenant-firebase

Also, I uploaded taxes-data-export.json file to import inside firebase project for a test.

2 Answers

Answers 1

I believe Custom Scopes for this is overkill. I would recommend doing the Request-Scoped seeding from Guice's own wiki. In your case that would be something like

public class TenancyFilter implements Filter {     @Override     public void doFilter(ServletRequest request,  ServletResponse response, FilterChain chain) throws IOException, ServletException {         HttpServletRequest httpRequest = (HttpServletRequest) request;         String tenancyId = httpRequest.getHeader("YOUR-TENANCY-ID-HEADER-NAME");         httpRequest.setAttribute(                 Key.get(String.class, Names.named("tenancyId")).toString(),                 userId         );         chain.doFilter(request, response);     }      @Override     public void init(FilterConfig filterConfig) throws ServletException { }      @Override     public void destroy() { } }; 

It has to be bound in a ServletModule

public class YourModule extends ServletModule {     @Override     protected void configureServlets() {         filter("/*").through(TenancyFilter.class);     }      @Provides     @RequestScoped     @Named("tenancyId")     String provideTenancyId() {         throw new IllegalStateException("user id must be manually seeded");     } } 

Then anywhere you need to get the Tenancy ID you just inject

public class SomeClass {     private final Provider<String> tenancyIdProvider;      @Inject     SomeClass(@Named("tenancyId") Provider<String> tenancyIdProvider) {         this.tenancyIdProvider = tenancyIdProvider;     }      // Methods in request call tenancyIdProvider.get() to get and take action based on Tenancy ID. } 

Answers 2

Right, so I know Play a lot better than FireBase, but it seems to me you want to extract a tenancy ID from the request prior to feeding this into your FrieBase backend? Context when writing Java in play is Thread local, but even when doing things async you can make sure the Http.context info goes along for the ride by injecting the execution context. I would not do this via the action creator, unless you want to intercept which action is called. (Though I have a hackish solution for that as well.)

So, after a comment I'll try to elucidate here, your incoming request will be routed to a controller, like below (let me know if you need clearing up on routing etc):

Below is a solution for caching a retrieved FireBaseApp based on a "Website-ID" retrieved from the request, though I would likely put the tenancyId in the session.

import javax.inject.Inject; import java.util.concurrent.CompletionStage;  public class MyController extends Controller {     private HttpExecutionContext ec; //This is the execution-context.     private FirebaseAppProvider appProvider;     private CacheApi cache;     @Inject     public MyController(HttpExecutionContext ec, FireBaseAppProvider provider,CacheApi cache) {         this.ec = ec;         this.appProvider = provider;         this.cache = cache;      }     /**     *Retrieves a website-id from request and attempts to retrieve      *FireBaseApp object from Cache.     *If not found a new FireBaseApp will be constructed by      *FireBaseAppProvider and cached.     **/     private FireBaseApp getFireBaseApp(){      String tenancyId = request.getHeader("Website-ID);      FireBaseApp app = (FireBaseApp)cache.get(tenancyId);      if(app==null){        app=appProvider.get();        cache.put(tenancyId,app);        }      return app;     }         public CompletionStage<Result> index() {         return CompletableFuture.supplyAsync(() -> {            FireBaseApp app = getFireBaseApp();            //Do things with app.         }, ec.current()); //Used here.     } } 

Now in FireBaseAppProvider you can access the header via play.mvc.Controller, the only thing you need is to provide the HttpExecutionContext via ec.current. So (once again, I'm avoiding anything FireBase specific), in FireBaseProvider:

import play.mvc.Controller; public class FireBaseAppProvider {   public  String getWebsiteKey(){         String website = Controller.request().getHeader("Website-ID");         //If you need to handle a missing header etc, do it here.         return website;     }   public FireBaseApp get(){      String tenancyId = getWebsiteKey();      //Code to do actual construction here.   } } 

Let me know if this is close to what you're asking and I'll clean it up for you.

Also, if you want to store token validations etc, it's best to put them in the "session" of the return request, this is signed by Play Framework and allows storing data over requests. For larger data you can cache this using the session-id as part of the key.

Read More

Wednesday, April 20, 2016

RESTEasy Guice Provider

Leave a Comment

I'm having a small problem when trying to use Guice with ContainerRequestFilter, it throws a NullPointerException. I did a little digging into RESTEasy and it would appear that it can't find a constructor for MyFilter due to the @Context annotation not being present, the NullPointerException is thrown when trying to instantiate a null constructor.

My filter:

@Provider @PreMatching public class MyFilter implements ContainerRequestFilter {     private Dependency d;      @Inject     public MyFilter(Dependency d) {         this.d = d;     }      @Override     public void filter(ContainerRequestContext containerRequestContext) throws IOException {         if (d.doSomething()) {             Response r = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();             containerRequestContext.abortWith(r);         }     } } 

I've added the filter to my Application class:

@ApplicationPath("") public class Main extends Application {     private Set<Object> singletons = new HashSet<Object>();     private Set<Class<?>> c = new HashSet<Class<?>>();      public Main() {         c.add(Dependency.class);     }      @Override     public Set<Class<?>> getClasses() {         return c;     }      @Override     public Set<Object> getSingletons() {         return singletons;     } } 

My Guice configuration:

public class GuiceConfigurator implements Module {     public void configure(final Binder binder) {         binder.bind(Dependency.class);     } } 

My web.xml:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"          version="3.1">      <display-name>My App</display-name>      <context-param>         <param-name>resteasy.guice.modules</param-name>         <param-value>com.example.GuiceConfigurator</param-value>     </context-param>      <listener>         <listener-class>           org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener         </listener-class>     </listener> </web-app> 

This configuration is working for injecting my dependencies into resources, but I get a NullPointerException when trying to use it on a provider.

Any help would be appreciated.

1 Answers

Answers 1

It seems that even with RESTeasy/JAX-RS components, you still need to register it with the Guice binder. I wasn't sure at first, but looking at the test cases, it seems we still need to register our resources and providers with Guice to make it work.

I test it after adding the filter to the Guice module, and it works as expected.

public class GuiceConfigurator implements Module {     public void configure(final Binder binder) {         binder.bind(MyFilter.class);         binder.bind(Dependency.class);     } } 

To test, I went off the example from the RESTeasy project, added a filter with the constructor injection, and added the filter to the module binder. And it worked when adding the filter to the module and failed when not added.

Read More

Thursday, April 7, 2016

Weblogic 12c : Prefer-web-inf-classes and prefer-application-packages for Jersey

Leave a Comment

I have to use both (oddly enough ..) "prefer-web-inf-classes" and "prefer-application-packages properties of weblogic.xml on a Weblogic 12c Server (12.2.1)

It is REST application based on Jersey 1.9. * ( Jersey 1.x JAX-RS RI) and Guice.

1. Why use :prefer-web-inf-classes

If you have more than one WAR you have to place at the level of war/lib the libraries for guice-jersey / guice , other way you get an Multibindings Error.

It must be indicate also the prefer-web-inf-classes to true. This way works properly! I have tried to work in the same way using prefer-application-packages with packages (com.sun.jersey.guice.spi.container.servlet /com.google.inject.servlet, etc..) but no way.

Note: Is not possible to exclude this libraries at EAR level.

2. Why use :prefer-application-packages

To use Jersey 1.x JAX-RS RI on Weblogic 12c (12.2.1) so I have to indicate the following packages (other way Weblogic uses Jersey 2, and different version of Jackson libraries, etc. )

It works perfectly on Jersey 1.X if it is indicated this way. . I have probed two war separately, and works fine... but, remember my friend i have two war..so....

Summary

I can not use both properties (deploying error for using both properties on the weblogic.xml..), but its needed :

  1. For the problem with Guice Filter, need to put prefer-web-inf-classes to true to use guice-servlet.jar / jersey-guice.jar at war-lib level.
  2. To work with Jersey 1.x, need to use .....prefer-application-package

Question: How to combine both to use one of them??

1 Answers

Answers 1

  • Hello, world! Hi, There are two way to resolve this problem.

    1. You can used to combine both file in to one file and call it based on choose tag as requirements.

    2. You can just write tags to merge or import/ include your xml file in to one. Example shown. Hint: Do google how to merge and call xml file based on condition.

Read More