Как поместить в настраиваемую область / контекст (JobScoped - настраиваемую область CDI) конкретный экземпляр из запроса, чтобы сделать его инъекционным?

Говоря в двух словах, я бы хотел добавить в пользовательскую область конкретный экземпляр класса Configuration из запроса rest. Основная проблема заключается в том, что пользовательская область (JobScoped от JBerethttps://jberet.gitbooks.io/jberet-user-guide/content/custom_cdi_scopes/index.html) имеет право после начала работы. Я знаю, что есть возможность добавлять свойства при запуске задания, но мой класс Configuration объединяет множество конфигураций, и это довольно сложно, поэтому было бы очень неудобно преобразовывать эти файлы в класс Properties.

Подробности ниже:

Это псевдокод запроса на отдых:

@Path("/job")
public class RunJob {

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/start")
public String startJob(@FormDataParam("file") InputStream uploadedInputStream) {
    JobOperatorImpl jobOperator = (JobOperatorImpl) BatchRuntime.getJobOperator();

    Configuration config = new Configuration(uploadedInputStream);
    Properties properties = new Properties();
    jobOperator.start(job, properties);
}

Чего я хотел добиться, так это внедрить некоторые файлы конфигурации в контексте задания, как показано ниже:

public class MyReader implements ItemReader {

@Inject
private Configuration configFile;
}

Класс конфигурации представлен как ниже:

@JobScoped
public class Configuration {
 // some flags, methods etc
}

Я читал об Instance, Provider, но не знаю, как использовать их в моем случае. На самом деле я думаю, что их невозможно использовать, потому что задания идентифицируются по их имени, которое является динамическим и известным во время выполнения.

Между тем я нашел похожую ситуацию с моей:Могу ли я создать объект в области запроса и получить к нему доступ из любого места и избежать его передачи в качестве параметра в JAX-RS?

Но тогда возникает проблема с отсутствующим контекстом. Когда Job запускается, существует контекст JobScoped. В соответствии с вышеупомянутым решением, я отметил конфигурацию как RequestScoped, затем я получил:

org.jboss.weld.context.ContextNotActiveException: WELD-001303: нет активных контекстов для типа области видимости javax.enterprise.context.RequestScoped в org.jboss.weld.manager.BeanManagerImpl.get.text (BeanManagerImpl) 9 , (ContextualInstance.java:63) в org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance (ContextBeanInstance.java:83) в org.jboss.weld.bean.proxy.ProxyMethodHandler.get.stj (ProxyMethodH) $ Proxy $ _ $ _ WeldClientProxy.toString (неизвестный источник)

 rtbf18 июл. 2016 г., 10:14
Спасибо за «или объекты (получить / положить)». Это не то, что я хотел бы иметь, но кажется, что у меня нет выбора.
 Xavier Dury15 июл. 2016 г., 17:10
Я действительно не знаю, как работает пакетный API, но вы не можете просто указать свою конфигурацию в свойствах, которые вы передаете оператору? Свойства могут содержать строки (getProperty / setProperty) или объекты (get / put).

Ответы на вопрос(3)

Решение Вопроса

Как ввести значения в пакетные задания?Как заполнить контекстные значения для пакетных заданий?Как ввести RequestScope в пакетное задание?Как создать собственную область?Как войти в пользовательскую область?Как заполнить значение в пользовательской области?

Я постараюсь ответить на все отдельные вопросы, но имейте в виду, что я совсем недавно начал использовать CDI / Weld и не имею опыта работы с JBeret.

1. Как ввести значения в пакетные задания?

Я добавляю этот вопрос, потому что думаю,Configuration возможно, не нужно быть объектом с ограниченным доступом. ЕслиConfiguration не имеет ничего конкретного для сферы, это может быть@Singleton или же@Stateless также. Подумайте, например, из файлов конфигурации, ресурсов или переменных среды, которые не изменятся во время выполнения. Нераспределенные (или синглтон-зависимые) зависимости можно просто впрыскивать в пакеты, используя обычные@Inject поля, без необходимости@JobScoped аннотаций.

2. Как заполнить контекстные значения для пакетных заданий?

Так что, если фактическое значение зависит от контекста и не может быть введено в@Singleton мода? По материалам JBeretдокументацияпредпочтительно передать всю конфигурациюProperties, Затем их можно прочитать изJobContextили вводится с помощью@BatchProperty аннотаций. Это работает только для предварительно определенного списка типов, которые сериализуются из String.

@Named
public class MyBatchlet extends AbstractBatchlet {

    @Inject
    @BatchProperty(name = "number")
    int number;

}
3. Как войти в@RequestScope в пакетной работе?

Я думаю, что вы не должны.@RequestScope только для запросов. Если у вас есть зависимости, зависящие от@RequestScope это должно быть доступно за пределами запроса, рассмотрите возможность введения настраиваемой области.

Если вам действительно нужно ввести@RequestScope программно, вы можете определить свой собственный контекст для него и ввести этот контекст (см. часть 4 ниже) или ввести контекст по умолчанию, как указано вэтот пост Дэн Хейвуд, в его попытке попасть в@RequestScope в Java SE.

4. Как создать собственную область?

Это довольно легко создать пользовательскую область. Однако для пользовательской области требуется реализация контекста области. Я обнаружил, что это немного неясно в документации. К счастью, есть библиотекабиблиотека с микроскопом, Для этого примера вам нужно толькоmicroscoped-core зависимость, которая обеспечиваетScopeContext реализация, которая используется в их пользовательских областях. Мы будем использовать этоScopeContext для нашего простого объема, а также.

Сначала мы должны создать аннотацию Scope:

@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface CustomScoped {}

Во-вторых, мы должны создать расширение:

public class CustomScopedExtension implements Extension, Serializable {

    public void addScope(@Observes final BeforeBeanDiscovery event) {
        event.addScope(CustomScoped, true, false);
    }

    public void registerContext(@Observes final AfterBeanDiscovery event) {
        event.addContext(new ScopeContext<>(CustomScoped.class));
    }

}

Обратите внимание, что мы используемScopeContext from microscoped here. Furthermore, you should register your extension by adding the full classname toMETA-INF / услуги / javax.enterprise.inject.spi.Extension`.

5. Как войти в пользовательскую область?

Теперь нам нужно войти в нашу сферу. Мы можем сделать это с помощью небольшого кода, который вы можете разместить, например, в сетиFilter или метод перехватчик. Код используетBeanManager экземпляр, который можно получить с помощью@Inject:

ScopeContext<?> context = (ScopeContext<?>) beanManager.getContext(CustomScoped.class);
context.enter(key);
try {
     // continue computation
} finally {
    context.destroy(key);
}
6. Как заполнить значение в пользовательской области?

Я задавал себе тот же вопрос, и это решение я придумал. Смотрите также мой вопрос о том, как правильно посеять из пользовательских областей Weld CDI:Значение семени в пользовательской области Weld CDI , У меня есть решение для вашей проблемы, хотя:

@Singleton
public class ConfigurationProducer {

    private final InheritableThreadLocal<Configuration>  threadLocalConfiguration =
    new InheritableThreadLocal<>();

    @Produces
    @ActiveDataSet
    public ConfigurationConfiguration() {
       return threadLocalConfiguration.get()
    }

    public void setConfiguration(Configuration configuration) {
         threadLocalConfiguration.set(configuration);
    }    

}

Теперь из вашего перехватчика, написанного выше, вы можете залитьConfigurationProducer и использоватьConfigurationProducer #setConfiguration(Configuration) установитьConfiguration для текущей темы. Я все еще ищу лучшие варианты здесь.

 wilx18 дек. 2018 г., 14:06
Вы решили использоватьScopeContext<>, Можно ли реализовать то же самое сorg.jboss.weld.contexts.AbstractSharedContext?
 Jan-Willem Gmelig Meyling18 дек. 2018 г., 14:22
Я был уверен, что в то время я был очень незнаком с реализацией пользовательских областей видимости и никогда больше не нуждался в этом.
 rtbf25 окт. 2016 г., 12:57
Я пометил ваш ответ как решение, потому что он был «наиболее полезным в поиске моего решения»
 rtbf18 июл. 2016 г., 15:41
Спасибо за ответ. Интересно, смогу ли я адаптировать его к текущей пользовательской области, которая является JobScoped, как я упоминал в вопросе Тогда я бы начал с пункта 5 и, надеюсь, избегал добавления сторонних библиотек, которые не только зависят от меня;) и могут быть отклонены ...
 Jan-Willem Gmelig Meyling26 окт. 2016 г., 14:33
Спасибо! Какое решение вы нашли?
 rtbf26 окт. 2016 г., 15:17
Я описал это ниже:stackoverflow.com/questions/38395462/...
 Jan-Willem Gmelig Meyling18 июл. 2016 г., 15:44
Вы также можете скопировать класс ScopeContext, это еще одна зависимость, но вам нужны только 1 или 2 класса. JobScope реализован с помощью JobScopeContext. Возможно, этот Контекст уже дает вам инструменты для этой работы. Увидеть:github.com/jberet/jsr352/blob/... , И, пожалуйста, укажите возможные отклонения коллег на обсуждение, люблю видеть конечный результат :)

я хотел бы еще раз поблагодарить вас, Ян-Виллем Гмелиг Мейлинг, потому что ваш ответ был очень полезным. В любом случае, я хотел использовать данную область видимости от JBeret, которая называется JobScoped, сегодня она может использоваться только на уровне TYPE. Я сделал подобный обходной путь, как предложил Ян-Виллем Гмелиг Мейлинг, но:

можно использовать JobScopedне нужно импортировать дополнительные библиотеки, все работает в CDI

Решение:

1) Класс конфигурации:

@JobScoped
public class Configuration
{...}

2) У JobListener магия случается. Дополнительные комментарии излишни.

Пусть мой код говорит сам за себя;)

import javax.batch.api.listener.AbstractJobListener;

public class MyJobListener extends AbstractJobListener{

    @Inject
    private Configuration jobScopedConfiguration;


    @Override
    public void beforeJob() throws Exception {
        enrichJobScopedConfigurationWithRequestConfiguration();
        ...
        super.beforeJob();
    }

    private void enrichJobScopedConfigurationWithRequestConfiguration(){
    Configuration requestConfiguration =
                (Configuration) BatchRuntime.getJobOperator().getJobExecution(currentExecutionId).getJobParameters()
                        .get("configuration");
        jobScopedConfiguration.updateWith(requestConfiguration);
    }

Теперь я могу ввести свою конфигурацию в любой пакетный артефакт jberet / java в контексте задания, например:

public class MyReader implements ItemReader {

@Inject
private Configuration configFile;
}

ользовательского объекта в задании путем вызова:

javax.batch.runtime.context.JobContext#setTransientUserData(myObject);

Для простых случаев этого должно хватить. Вы можете определить работу слушателя, ввестиJobContext в свой класс слушателя работы, и внутри егоstartJob() метод, установить временные данные пользователя. Затем он будет доступен для всего выполнения задания и может быть привязан к другим значениям. Для более сложных случаев использования,@JobScoped это лучший выбор.

 rtbf30 сент. 2016 г., 13:34
Ваша идея очень похожа на мое окончательное решение, но я не вижу возможности setTransientUserData во время начала работы. Конечно, это может быть отправлено как свойство в java.util.Properties, как и я. Получение конфигурации также не очень удобно, потому что вам нужно внедрить JobContext, а затем получить Object из этого и привести его. Дополнительно Если вы хотите отправить больше объектов, вам нужно будет обернуть их в некоторый контейнер, потому что transientUserData по умолчанию является одним объектом.
 Jose Tepedino24 мар. 2019 г., 14:56
Вы можете сохранить много значений, если вы установите значение transientUserData как коллекцию, например, карта объектов.

Ваш ответ на вопрос