2010-06-15 1 views
26

Я просто познакомился с внедрением веб-сервисов REST на Java с использованием JAX-RS, и я столкнулся с следующей проблемой. Один из моих классов ресурсов требует доступа к серверу хранения, который абстрагируется от интерфейса StorageEngine. Я хотел бы ввести текущий экземпляр StorageEngine в класс ресурсов, обслуживающий запросы REST, и я подумал, что хорошим способом сделать это будет использование аннотации @Context и соответствующего класса ContextResolver. Это то, что я до сих пор:Использование @Context, @Provider и ContextResolver в JAX-RS

В MyResource.java:

class MyResource { 
    @Context StorageEngine storage; 
    [...] 
} 

В StorageEngineProvider.java:

@Provider 
class StorageEngineProvider implements ContextResolver<StorageEngine> { 
    private StorageEngine storage = new InMemoryStorageEngine(); 

    public StorageEngine getContext(Class<?> type) { 
     if (type.equals(StorageEngine.class)) 
      return storage; 
     return null; 
    } 
} 

Я использую com.sun.jersey.api.core.PackagesResourceConfig открыть для себя поставщиков и классов ресурсов автоматически, и в соответствии с бревна, он отлично подбирает класс StorageEngineProvider (временные метки и ненужные вещи упущены намеренно):

INFO: Root resource classes found: 
    class MyResource 
INFO: Provider classes found: 
    class StorageEngineProvider 

Однако значение storage в моем классе ресурсов всегда null - ни конструктор StorageEngineProvider, ни его метод getContext называют Джерси, когда-либо. Что я здесь делаю неправильно?

ответ

19

Я не думаю, что есть специальный способ JAX-RS, чтобы делать то, что вы хотите. Ближайший будет делать:

@Path("/something/") 
class MyResource { 
    @Context 
    javax.ws.rs.ext.Providers providers; 

    @GET 
    public Response get() { 
     ContextResolver<StorageEngine> resolver = providers.getContextResolver(StorageEngine.class, MediaType.WILDCARD_TYPE); 
     StorageEngine engine = resolver.get(StorageEngine.class); 
     ... 
    } 
} 

Однако, я думаю, что @ javax.ws.rs.core.Context аннотаций и javax.ws.rs.ext.ContextResolver действительно для типов, связанных с JAX-RS и поддерживая поставщиков JAX-RS.

Возможно, вам захочется найти реализации Java Context и Dependency Injection (JSR-299) (которые должны быть доступны в Java EE 6) или другие схемы внедрения зависимостей, такие как Google Guice, чтобы помочь вам здесь.

+2

FWIW вы также можете сделать немного короче: @ Контекст ContextResolver storageEngineResolver; –

+0

Как смешно, что так сложно сделать что-то столь же нормальное, как предоставление альтернативных реализаций поддержки в тестовых и производственных контекстах! По крайней мере, это работает без необходимости перетаскивать некоторые другие рамки. –

+1

Решение Doug (FROM COMMENTS) не сработало для меня (с RestEasy). –

13

Внесите a InjectableProvider. Скорее всего, путем расширения PerRequestTypeInjectableProvider или SingletonTypeInjectableProvider.

@Provider 
public class StorageEngineResolver extends SingletonTypeInjectableProvider<Context, StorageEngine>{ 
    public MyContextResolver() { 
     super(StorageEngine.class, new InMemoryStorageEngine()); 
    } 
} 

позволит у вас есть:

@Context StorageEngine storage; 
+2

Джерси, но хорошее предложение. CXF делает что-то подобное с ['ContextProvider'] (http://sberyozkin.blogspot.co.uk/2012/03/custom-jax-rs-contexts-in-cxf-260.html). –

0

Я нашел другой путь. В моем случае я хочу предоставить пользователю, который в настоящее время зарегистрирован как пользовательский объект из моего уровня persitence. Это класс:

@RequestScoped 
@Provider 
public class CurrentUserProducer implements Serializable, ContextResolver<User> { 

    /** 
    * Default 
    */ 
    private static final long serialVersionUID = 1L; 


    @Context 
    private SecurityContext secContext; 

    @Inject 
    private UserUtil userUtil; 

    /** 
    * Tries to find logged in user in user db (by name) and returns it. If not 
    * found a new user with role {@link UserRole#USER} is created. 
    * 
    * @return found user or a new user with role user 
    */ 
    @Produces 
    @CurrentUser 
    public User getCurrentUser() { 
     if (secContext == null) { 
      throw new IllegalStateException("Can't inject security context - security context is null."); 
     } 
     return userUtil.getCreateUser(secContext.getUserPrincipal().getName(), 
             secContext.isUserInRole(UserRole.ADMIN.name())); 
    } 

    @Override 
    public User getContext(Class<?> type) { 
     if (type.equals(User.class)) { 
      return getCurrentUser(); 
     } 
     return null; 
    } 

} 

Я только использовал implements ContextResolver<User> и @Provider, чтобы получить этот класс обнаруженную JAX-RS и получить SecurityContext инъекции. Чтобы получить текущего пользователя, я использую CDI с моим квалификатором @CurrentUser. Так что на каждом месте, где я нужен текущий пользователь я типа:

@Inject 
@CurrentUser 
private User user; 

И действительно

@Context 
private User user; 

не работает (пользователь является недействительным).

0

Образец, который работает для меня: добавьте некоторые поля в подкласс приложения, которые предоставляют объекты, которые необходимо ввести. Затем использовать абстрактный базовый класс, чтобы сделать «инъекцию»:

public abstract class ServiceBase { 

    protected Database database; 

    @Context 
    public void setApplication(Application app) { 
     YourApplication application = (YourApplication) app; 
     database = application.getDatabase(); 
    } 
} 

Всех ваши услуги, которые должны получить доступ к базе данных, теперь может продлить ServiceBase и иметь базу данных автоматически доступные через защищенное поле (или поглотитель, если вы предпочитают это).

Это работает для меня с Undertow and Resteasy. Теоретически это должно работать во всех реализациях JAX-RS, поскольку впрыск приложения поддерживается стандартным AFAICS, но я не тестировал его в других настройках.

Для меня преимущество перед решением Брайанта заключалось в том, что мне не нужно писать какой-то класс резольвера, чтобы я мог получить доступ к своим синглонам с приложениями, подобными базе данных.

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