Guice throw-ва OutOfScopeException при стартиране на CompletableFuture

+2 гласа
55 прегледа
попитан 2016 април 12 в Java от Nikola.Nikolov. (3,100 точки)

Четох някъде, че класът CompletableFuture и неговите конструктури и методи и т.н. трябва да бъдат изпълнени през някакъв task. Техният Supplier ползва специфичен домейн service MessageService, който е session scope-нат. Service-a е инжектиран от Guice. 

public class MessageProcessingPage { 

    private MessageService messageService; 

    @Inject 

    public MessagProcessingPage (MessageService messageService) { 

        this.messageService = messageService; 

    } 

    // Called by request scoped thread. 

    public void onProcessMessagesButton () { 

        ExecutorService executorService = Executors.newFixedThreadPool(3); 

        CompletableFuture.supplyAsync( 

        // Called from a thread from the threadpool. 

        () -> {return messageService.retrieveMessageMetadataSet(x, y);} 

        , executorService); 

        ... 

    } 

    ... 

  

MessageService-а има session scope-нат MessageRestClient ,който е инжектиран 

@SessionScoped 

public class MessageService { 

    private MessageRestClient messageRestClient; 

    @Inject 

    public MessageRestClient (MessageRestClient messageRestClient) { 

        this.messageRestClient = messageRestClient; 

    } 

    public MessageMetaDataSet retrieveMessageMetadataSet(x, y) { 

        List<MessageMetaData> listOfMetaData = messageRestClient.retrieve(x, y, z); 

        ... 

    } 

    ... 

@SessionScoped 

public class MessageRestClient { 

    ... 

Но Guice се затруднява да инжектира MessageRestClient-а. 

java.util.concurrent.CompletionException: com.google.inject.ProvisionException: Unable to provision, see the following errors: 

1) Error in custom provider, com.google.inject.OutOfScopeException: Cannot access scoped [MessageRestClient]. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter as a servlet filter for this request. 

Четох за един метод ServletScopes  : public static <T> Callable<T> transferRequest(Callable<T> callable) 

https://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/servlet/ServletScopes.html#transferRequest-java.util.concurrent.Callable- 

Но не виждам начин да го използвам, понеже никакви Callables не влизат в програмата. Някой да помогне? 

1 отговор

+1 глас
отговорени 2016 април 13 от Daniel Ivanov (11,160 точки)
избран 2016 април 13 от Mitko Vasilev
 
Най-добър отговор

Когато искаш да обработиш servlet request в Guice , GuiceFilter-ът се е грижи да ти зададе правилния контекст (с  ThreadLocal) ,за да може да знае в кой request си и по тази причина ще ти сложи Scope-овете правилно. Инстанциите на класовете отбелязани със SessionScope всъщност са проксита , които могат да получат достъп до този Request и Session information-а от Guice и да работи както си трябва.

Task-а,който трябва да изпратиш към CompletableFuture работи в отделен thread вън от Guice control-а.

Понеже няма свободен ThreadLocal, от който да вземе информация Guice,няма и нито Request, нито информация за Session,което ще рече,че няма и SessionScope. Понеже проксито не може да знае каквото и да е за този session,ти throw-ва error-а който получаваш ти.

Методът transferRequest трябва да ти инжектира нужната информация,така че да ти работят scope-овете. Би трябвало да стане така (но не съм го изпробвал):

Callable<MessageMetaDataSet> c = transferRequest(
               () -> messageService.retrieveMessageMetadataSet(x, y));
   CompletableFuture.supplyAsync(
    () -> c.call()
    , executorService);
...