2015-12-08 4 views
2

Я поддерживаю приложение с несколькими арендаторами, в котором специальные метаданные по запросам (заголовки, параметры) идентифицируют конкретных арендаторов. Каждый арендатор имеет пользовательские конфигурации в системе, которые переопределяют некоторые значения по умолчанию. Конфигурации исходят из базы данных, дополненной кэшем, с помощью EJB. Чтобы успешно найти одну такую ​​настраиваемую конфигурацию, необходим ключ и идентификатор арендатора. Если идентификатор арендатора отсутствует, ключ используется только для получения значения по умолчанию для ввода ключа.Передача данных Runtime (Meta) в метод производителя в CDI

От удаленных интерфейсов, которые получают эти запросы (сервлеты, веб-сервисы и т. Д.). Я хочу получить такие идентификаторы и контексты настройки (например, поместить свойства в EJBContext), чтобы методы производителей могли использовать для настройки соответствующих компонентов для обслуживания клиентов каждого арендатора. В идеале я хотел бы также рекомендовать CDI над EJB для этого случая настолько же разумно.

Я думал о линиях следующей стратегии, но я застрял.

  1. Создайте квалификатор @Config, чтобы контейнер CDI разрешался производителю конфигурации.
  2. Создайте аннотацию конфигурации @Key(String), через которую можно получить ключ поиска желаемой записи конфигурации.
  3. Создайте метод производителя, который принимает параметр InjectionPoint. InjectionPoint позволяет получить аннотацию @Key, объявленный тип целевого поля и класс, в котором объявлено это введенное поле (охватывающий класс). Слабый сценарий был бы, если InjectionPoint позволяет мне получить экземпляр входящего класса. Но, думая об этом, это не имеет смысла, поскольку экземпляр еще не готов, пока все его зависимости не будут созданы/расположены и не введены.

В этом случае CDI не предназначен для? Как это лучше всего реализовать?

ответ

1

Одним из возможных решений является извлечение значительных значений арендатора при обработке запроса, например. ServletFilter или перехватчиком и храните его в держателе ThreadLocal. Это будет работать только в том случае, если оба компонента (фильтр e.q и производитель CDI) выполняются в одном потоке, поэтому вы можете столкнуться с проблемами с EJB.
Вы можете получить идентификатор арендатора в своем методе @Produces и вернуть запись конфигурации на основе значения аннотации @Key и идентификатора арендатора.

Некоторые псевдо решение:

ThreadLocal Держатель фильтра Запрос

public class ThreadLocalHolder { 

    private static ThreadLocal<String> tenantIdThreadLocal = new ThreadLocal<>(); 

    public static String getTenantId(){ 
    return tenantIdThreadLocal.get(); 
    } 
    public static void setTenantId(String tenantid){ 
    return tenantIdThreadLocal.set(tenantid); 
    } 
} 

для извлечения арендатора

@WebFilter(value = "/*") 
public class TenantExtractorFilter implements Filter { 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
    HttpServletRequest req = (HttpServletRequest) request; 
    //obtain tenant id, and store in threadlocal 
    ThreadLocalHolder.setTenantId(req.getHeader("X-TENANT")); 
    chain.doFilter(request, response); 
    } 
} 

Конфигурация производителя входа

public class Producer { 

    //get a hold of some DAO or other repository of you config 
    private ConfigRepository configRepo; 

    @Produces 
    @Config 
    public String produceConfigEntry(InjectionPoint ctx) { 
    Key anno = //get value of @Key annotation from the injection point, bean, property... 
    String tenantId = ThreadLocalHolder.getTenantId(); 
    // adjust to your needs 
    return configRepo.getConfigValueForTenant(anno.value(), tenantId); 
    } 
} 

Если ThreadLocal не вариант, посмотреть на javax.transaction.TransactionSynchronizationRegistry - который работает независимо от пулов потоков, но требует присутствия сделки, очевидно.

Обновление 14.12.2015
Альтернативный запрос подход с использованием области видимости компонент в качестве владельца данных

RequestScoped держатель

@RequestScoped 
public class RequestDataHolder { 
    private String tenantId; 

    public String getTenantId() { 
    return this.tenantId; 
    } 

    public void setTenantId(String tenantId) { 
    this.tenantId = tenantId; 
    } 
} 

WebFilter

извлекает значения из запроса и сохраняет их в нашем держателе.

@WebFilter(value = "/*") 
public class TenantExtractorFilter implements Filter { 

    @Inject private RequestDataHolder holder; 

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
    HttpServletRequest req = (HttpServletRequest) request; 
    //obtain tenant id, and store in threadlocal 
    holder.setTenantId(req.getHeader("X-TENANT")); 
    chain.doFilter(request, response); 
    } 
} 

производитель CDI использует держатель данных и производит ожидаемую точку значение forinjection.

public class Producer { 

    //get a hold of some DAO or other repository of you config 
    private ConfigRepository configRepo; 
    @Inject 
    private RequestDataHolder dataHolder; 

    @Produces 
    @Config 
    public String produceConfigEntry(InjectionPoint ctx) { 
    Key anno = //get value of @Key annotation from the injection point, bean, property... 
    String tenantId = holder.getTenantId(); 
    // adjust to your needs 
    return configRepo.getConfigValueForTenant(anno.value(), tenantId); 
    } 
} 

Наш RequestDataHolder компонент может быть введен в любой CDI, EJB, JAXRS или сервлетов компонент, тем самым позволяя передавать переменную из контекста WEB в других контекстах.

Примечание: это решение требует правильной интеграции контейнера CDI с контейнерами EJB и WEB в соответствии с спецификацией CDI.

+0

Спасибо за замечательный ответ! В методе 'productionConfigEntry' вы разыменовали' ThreadLocal'. Вы имели в виду 'ThreadLocalHolder'? Как это делается в классе «Продюсер»? 'TransactionSynchronizationRegistry', я думаю, будет подталкивать меня к EJB для транзакций. Можно ли пройти этот маршрут с чистым CDI? Между тем, я буду играть с вашей предлагаемой стратегией и сообщать о своих выводах. Спасибо заранее! –

+0

Да, вы правы, должен был быть 'ThreadLocalHolder'. Именно этот статический перенос по контексту потока позволяет обмениваться контекстом между всеми компонентами, выполняемыми в одном потоке. Транзакции не ограничиваются контекстом EJB, поэтому в теории нет ничего, что помешало бы вам использовать его в чистом CDI. Возможно, здесь может быть отсутствие транзакции в веб-слое. Если ваш сервер поддерживает его, вы также можете вставить @RequestScoped beans в yoir EJB. Таким образом, вы можете использовать CDI в качестве держателя данных – yntelectual

+0

Если мне не гарантировано, что мои поддерживающие бизнес-компоненты (EJB/CDI beans) будут выполняться в том же потоке, что и обслуживаемые удаленные интерфейсы (сервлеты, jax-ws/rs) , то мне, вероятно, лучше подойдет подход TransactionSynchonizationRegistry. –

Смежные вопросы