2013-11-10 6 views
0

Я сейчас в процессе преобразования приложения примера Virtual Trainer из учебника Ed Burns на JSF 2.0 из JSF Managed Beans в CDI. Большинство проблем, с которыми я столкнулся до сих пор, были связаны с определением области охвата и забыванием правильно вводить, но теперь я изо всех сил пытаюсь преодолеть последнее препятствие, связанное с извлечением CDI Bean (фактически класса сущности) из RequestMap. Из того, что я смог выяснить до сих пор, оказалось, что управляемый компонент, обработанный запросами, может быть извлечен очень просто, используя метод шаблона .get (String managedbeanname), предоставляемый реализацией карты. Однако, с CDI, bean-файл обернут Weld в экземпляре CreationalContextImp, и я не могу извлечь объект, который действительно есть, даже если он подтвердил, что он присутствует в RequestMap. Я могу получить вкратце доступ к прокси-объекту из RequestMap, но после обращения к .get («пользователь») он возвращается к нулевому значению, и я сомневаюсь, что это все равно принесет пользу, так как поля в прокси-сервере все обнуляются.Извлечь RequestScoped CDI bean из RequestMap

Я нашел сообщение от BalusC, в котором обсуждается использование класса фильтра для доступа к CDI-компонентам, хранящимся в SessionScope (How do I get a SessionScoped CDI bean from inside a Filter?). Кажется, это немного связано - есть ли более простое решение? Я также очень понимаю, что я, возможно, собираюсь обсуждать/смешивать стратегии Managed Bean vs CDI, поэтому не стесняйтесь устанавливать меня прямо ... Я также немного не понимаю, как использовать Entity Bean прямо таким образом, а не использовать Фасад. Является ли это причиной меня/может вызвать у меня проблему позже?

Окружающая среда: JEE7, Glassfish 4, Netbeans 7.4, Maven EE Archetype Исходный код, связанный с использованием управляемых компонентов, был закомментирован.

Абстрактный класс подложки боб:

@RequestScoped 
public abstract class AbstractBacking implements Serializable { 

    //@ManagedProperty(value="#{facesContext}") 
    private FacesContext facesContext; 

    //@ManagedProperty(value="#{requestScope}") 
    private Map<String, Object> requestMap; 

    //@ManagedProperty(value="#{sessionScope}") 
    private Map<String, Object> sessionMap; 

    @PostConstruct 
    public void init() { 
     this.facesContext = FacesContext.getCurrentInstance(); 
     this.sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap(); 
     this.requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap(); 
    } 

Страница регистрации поддержка боб:

@Named 
@RequestScoped 
public class RegisterBacking extends AbstractBacking implements Serializable { 

    private Object password1; 

    @Inject 
    private User newUser; 

    public String registerUser() { 
     String result = null; 
     User newUser = (User) getRequestMap().get("user"); 
     // set the password into the user, because we know the validator was 
     // successful if we reached here. 
     newUser.setPassword((String) getRequestMap().get("password1")); 
     try { 
      UserRegistry.getCurrentInstance().addUser(newUser); 
      // Put the current user in the session 
      setCurrentUser(newUser); 
      // redirect to the main page 
      result = "/user/allEvents?faces-redirect=true"; 
     } catch (EntityAccessorException ex) { 
      getFacesContext().addMessage(null, 
        new FacesMessage("Error when adding user" 
          + ((null != newUser) ? " " + newUser.toString() : "") + ".")); 

     } 

     return result; 

    } 

объект пользователя боб:

@Entity 
@Named 
@Table(name = "Users") 
@RequestScoped 
@NamedQueries({ 
    @NamedQuery(name = "user.getAll", query = "select u from User as u"), // @NamedQuery(name = "user.getTrainers", query = "select u from User as u where u.trainer = TRUE"), 
// @NamedQuery(name = "user.getUsersForTrainerId", query = "select u from User as u where u.personalTrainerId = :theId") 
}) 

public class User extends AbstractEntity implements Serializable { 

    protected String firstName; 
    protected String lastName; 
    @Temporal(TemporalType.DATE) 
    protected Date dob; 
    protected String sex; 
    protected String email; 
    private String serviceLevel = "medium"; 
    @Column(name = "userid", nullable = false) 
    private String userid; 
    private String password; 
    private boolean trainer; 
    private List<Long> subscribedEventIds; 
    private Long personalTrainerId; 

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) 
    private List<TrainingSession> sessions; 

    private boolean sessionsInitialized = false; 

    public User() { 
     this.init(); 
    } 

    public User(String firstName, String lastName, 
      String sex, Date dob, String email, String serviceLevel, 
      String userid, String password, boolean isTrainer) { 
     this.init(); 
     this.setFirstName(firstName); 
     this.setLastName(lastName); 
     this.setSex(sex); 
     this.setDob(dob); 
     this.setEmail(email); 
     this.setServiceLevel(serviceLevel); 
     this.setUserid(userid); 
     this.setPassword(password); 
     this.setTrainer(isTrainer); 
    } 
..... 
Getters/setters/etc 
..... 

Страница регистрации:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" 
     xmlns:h="http://java.sun.com/jsf/html" 
     xmlns:f="http://java.sun.com/jsf/core" 
     xmlns:ui="http://java.sun.com/jsf/facelets"> 
<body> 
<ui:composition template="template.xhtml"> 
    <ui:define name="content"> 
<h:form prependId="false"> 
    <h:panelGrid columns="3"> 

     <h:outputLabel for="fname" value="First Name:" /> 
     <h:inputText label="First Name" 
        id="fname" value="#{user.firstName}" 
        required="true"/> 
     <h:message for="fname" /> 

     <h:outputLabel for="lname" value="Last Name:" /> 
     <h:inputText label="Last Name" 
        id="lname" value="#{user.lastName}" 
        required="true"/> 
     <h:message for="lname" /> 

     <h:outputLabel for="sex" value="Sex:" /> 
     <h:selectOneRadio label="Sex" 
          id="sex" value="#{user.sex}" required="true"> 
      <f:selectItem itemLabel="Male" itemValue="male" /> 
      <f:selectItem itemLabel="Female" itemValue="female" /> 
     </h:selectOneRadio> 
     <h:message for="sex" /> 

     <h:outputLabel for="dob" value="Date of Birth:" /> 
     <h:panelGroup> 
      <h:inputText label="Date of Birth" 
        id="dob" value="#{user.dob}" required="true"> 
       <f:convertDateTime pattern="MM-dd-yy" /> 
      </h:inputText> (mm-dd-yy) 
     </h:panelGroup> 
     <h:message for="dob" /> 

     <h:outputLabel for="email" value="Email Address:" /> 
     <h:inputText label="Email Address" 
        id="email" value="#{user.email}" required="true" /> 
     <h:message for="email" /> 

     <h:outputLabel for="slevel" value="Service Level:" /> 
     <h:selectOneMenu label="Service Level" id="slevel" 
         value="#{user.serviceLevel}"> 
      <f:selectItem itemLabel="Medium" itemValue="medium" /> 
      <f:selectItem itemLabel="Basic" itemValue="basic" /> 
      <f:selectItem itemLabel="Premium" itemValue="premium" /> 
     </h:selectOneMenu> 
     <h:message for="slevel" /> 

     <h:outputLabel for="userid" value="Userid:" /> 
     <h:inputText required="true" id="userid" value="#{user.userid}" /> 
     <h:message for="userid" /> 

     <h:outputLabel for="password" value="Password:" /> 
     <h:inputSecret required="true" id="password" 
         validator="#{registerBacking.validatePassword1}" 
         value="#{requestScope.password1}" /> 
     <h:message for="password" /> 

     <h:outputLabel for="password2" value="Retype Password:" /> 
     <h:inputSecret required="true" id="password2" value="#{requestScope.password2}" 
         validator="#{registerBacking.validatePassword2}" /> 
     <h:message for="password2" /> 


    </h:panelGrid> 

    <p><h:commandButton value="Register" 
        action="#{registerBacking.registerUser}" /></p> 
</h:form> 

    </ui:define> 
</ui:composition> 
</body> 
</html> 
+0

В следующий раз предоставить меньше информации. Например, ваша организация не связана с вопросом. В центре внимания то, что вы хотите решить. –

+0

Понял - я думаю, если кому-то нужна дополнительная информация, они могли бы спросить – rustproofFish

ответ

3

Действительно, вы, кажется, смешиваете CDI Beans и JSF Beans, и, честно говоря, пример, который вы переводите, кажется довольно странным, я просто выброшу его из окна.

Потому что у JSF тоже есть своя инъекция зависимостей, и вы действительно не извлекаете материал самостоятельно из облачных карт, если у вас нет особого варианта использования (скажем, ServletFilter).

Чтобы уточнить, CDI использует принцип http://en.wikipedia.org/wiki/Inversion_of_control и «извлечь» материал - это анти-шаблон.

Далее в CDI-компонентах создаются и управляются контейнером CDI (Weld или OWB практически во всех случаях), и вы просто не можете извлекать CDI-компонент из externalContext, потому что это контекст JSF, и это совершенно отдельная вещь.

Таким образом, экземпляры, созданные JSF, не используются для инъекций в CDI Beans и наоборот. В любом случае, как я сказал ранее, извлечение самого материала - плохая практика. См. № 10 в этом списке: http://zeroturnaround.com/rebellabs/watch-out-for-these-10-common-pitfalls-of-experienced-java-developers-architects/

Так что вы хотите просто использовать @Inject (возможно, с квалификатором).

Временное решение Для особых случаев, когда Inject не будет работать:

  • контекст CDI не доступен в текущем потоке.
  • Текущий экземпляр не удалось КДИ

Первый сценарий happends, когда у вас есть ThreadLocal, например, QuartzJob. Контейнеры EJB могут предлагать способы получения контекстных потоков, но если вы находитесь в контейнере с одним сервлетом (tomcat), или поток генерируется без контекста CDI, по какой причине вы должны приложить эту информацию самостоятельно. Для этого используйте Deltaspike CDI Control.

http://deltaspike.apache.org/container-control.html

Этот пример не обновляется, сегодня вы должны использовать DependentProvider, чтобы ухватить ContextControl.

Пример на моем блоге здесь: http://www.kildeen.com/blog/post/2013-10-11/Batch%20Jobs%20in%20EE/

Второй сценарий это когда ServletContainer создал экземпляр и вы на простой контейнер (например, Tomcat), JSF создал экземпляр, что угодно.

Тогда инъекция не будет работать. В качестве обходного пути используйте BeanProvider от Deltaspike. http://deltaspike.apache.org/core.html Он может принудительно вводить инъекцию в ваш текущий экземпляр и что еще более полезно, он может извлечь компонент для вас. Если вы это сделаете, и вы по-прежнему страдаете от первого сценария, вы получите исключение, возможно, ContextNotActiveException.

Для правильной работы с JSF и CDI я бы рекомендовал использовать TomEE. Это проект Apache, поэтому он с открытым исходным кодом. Список рассылки и канал irc действительно активны, и он действительно растет, и даже сейчас это действительно здорово.

Конечно, это мое мнение, и другие предпочитают другие решения, такие как WildFly, построить его самостоятельно с Tomcat/Jetty, Glassfish и т.д.

Теперь все обычные правила JSF прикладывает (необходимо использовать конвенции для добытчиков/сеттеры и т. д.). Чтобы выставить свой компонент в выражения EL (что использует JSF), вы должны пометить его как @Named. Имя по умолчанию будет myBean, если класс имеет имя MyBean.

Теперь идут области. Всегда используйте @RequestScoped для каждого компонента, который у вас есть в приложении. Когда вы сталкиваетесь с проблемами, потому что эта область должна быть короткой, вы должны подумать о том, как долго она будет стоять, если данные, которые вы хотите сохранить, интересны для других bean-компонентов, и если вы хотите, чтобы они были доступны на всех вкладках браузера.

Если это пользовательская информация, то вероятность того, что это интересно для многих бобов. Таким образом, мы создаем новый класс под названием WebUser. Пользователь, вероятно, ожидает, что его информация будет сохранена в течение всей продолжительности его сеанса (и вам, возможно, придется отследить его и с этим объектом). Поэтому мы используем @SessionScoped с правильного пакета, который должен быть импортирован javax.enterprise.context.SessionScoped. Но внезапно какая-то логика не должна делиться между вкладками, поэтому вам нужна более тонкая область. CDI поставляется с @ViewScoped (CDI 1.1, JSF 2.2) и @ConversationScoped, но, скорее всего, вы хотите, чтобы области с Myfaces CODI рано или поздно. Теперь большинство CODI уже находятся в deltaspike, но не в области.Они, однако, отделились и как их импортировать, объясняется здесь: http://os890.blogspot.fr/2013/07/add-on-codi-scopes-for-deltaspike.html Вы увидите их в Дельтаспике раньше или позже.

Итак, теперь вы можете использовать множество разных областей, и вы выбираете это с умом. Материал, который следует читать только один раз из БД, можно сохранить с помощью @ApplicationScoped.

Например, вы можете прочитать системные настройки из базы данных и сохранить их в Настройщике, который является компонентом CDI, аннотированным @ApplicationScoped.

WelcomeBean является @RequestScoped и @Model и отвечает за создание логинов и учетных записей. Для того, чтобы выяснить, если новые учетные записи могут быть созданы это может выглядеть следующим образом:

@ViewScoped 
@Named 
public class WelcomeBean { 

    @Inject 
    private SettingManager settingManager; 


    private boolean allowCreateAccount; 


    public boolean isAllowCreateAccount() { 
     return allowCreateAccount; 
    } 


    // login and create account here 

    @PostConstruct 
    private void init() { 
     allowCreateAccount = settingManager.getBooleanSetting("registrationOpen"); 
    } 
} 

Facelet создать кнопку записи выглядит следующим образом:

<h:commandButton action="#{welcomeBean.createAccount}" value="login" disabled="#{welcomeBean.allowCreateAccount}"/> 

Теперь, когда пользователь выполняет вход вы можете сигнализировать это как событие. Прочитайте события CDI. На самом деле самые передовые примеры мало чем отличаются от этого простого.

+0

Во-первых, спасибо за полный ответ. Я подозревал, что, возможно, я смешиваю рамки. Должен признаться в некоторой путанице, поскольку JSF Managed Beans должен быть обесценен, и рекомендуется использовать CDI, но я не нашел на данный момент текста, который показывает конкретные примеры приложений на основе JSF с использованием CDI. Знаете ли вы о каких-либо ссылках или учебниках по этому вопросу? Большое спасибо Карлу. – rustproofFish

+0

Я всегда ссылаюсь на этот пример: https://github.com/struberg/lightweightEE Но на самом деле это просто начать. Я уточню свой ответ. Когда дело доходит до других примеров, я не знаю. Вы всегда можете задать мне вопросы непосредственно по irc karlkilden - это мое имя экрана, и вы можете найти меня на freenode. Вот мой главный канал irc: http://openwebbeans.apache.org/community.html В целом #myfaces и #openwebbeans #openejb будут хорошими местами, чтобы задавать вопросы. Как правило, помощники Apache рады помочь! –

+0

Karl - это отлично. Еще раз спасибо за то, что нашли время, чтобы полностью объяснить мне все. Я думаю, что до сих пор меня несколько путают до сих пор, пытаясь реализовать всю модель PRG, используя CDI, когда (если я жестоко честен), я не очень хорошо разбираюсь в структуре или шаблоне. Это и хороший случай «паралича путем анализа» заставило меня ходить кругами в течение некоторого времени, а не получать приличный рабочий пример и работать. Еще раз спасибо - я поймаю вас на OpenWebBeans, так как я уверен, что у меня будет больше вопросов! – rustproofFish

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