View Javadoc
1   package info.ejava.examples.ejb.cdisales.web;
2   
3   import java.io.Serializable;
4   import java.math.BigDecimal;
5   import java.util.ArrayList;
6   import java.util.Collections;
7   import java.util.List;
8   import java.util.Random;
9   
10  import info.ejava.examples.ejb.cdisales.bl.ProductCatalog;
11  import info.ejava.examples.ejb.cdisales.bl.Tx;
12  import info.ejava.examples.ejb.cdisales.bo.CurrentUser;
13  import info.ejava.examples.ejb.cdisales.bo.Member;
14  import info.ejava.examples.ejb.cdisales.bo.Product;
15  import info.ejava.examples.ejb.cdisales.bo.ProductCategory;
16  import info.ejava.examples.ejb.cdisales.ejb.InvalidProduct;
17  
18  import javax.annotation.PostConstruct;
19  import javax.annotation.PreDestroy;
20  import javax.enterprise.context.Conversation;
21  import javax.enterprise.context.ConversationScoped;
22  import javax.faces.component.UICommand;
23  import javax.faces.component.UIForm;
24  import javax.faces.model.SelectItem;
25  import javax.inject.Inject;
26  import javax.inject.Named;
27  
28  import org.slf4j.LoggerFactory;
29  import org.slf4j.Logger;
30  
31  @SuppressWarnings("serial")
32  
33  @Named("sellerController") //named used by the JSF page to access properties and methods
34  @ConversationScoped        //stays alive longer than a request and shorter than a session
35  /*
36      Properties within this class are referred to using #{sellerController.xxx}
37      syntax in the JSF page. 
38  */
39  public class SellerController implements Serializable {
40      private static final Logger logger = LoggerFactory.getLogger(SellerController.class);
41      
42      /**
43       * Gets injected and is place to stash errors to be displayed.
44       */
45      @Inject 
46      private ErrorController error;
47      
48      /** Can be used to manage the conversation */
49      @Inject
50      private Conversation conversation; 
51      
52      
53      /** A reference to back-end business logic that can manage products. This
54       * will be injected by CDI with the aid of a @Tx Qualifier 
55       */
56      @Inject @Tx
57      private ProductCatalog catalog;
58      
59      /***************************************
60       * Business data 
61       */
62      
63      /** 
64       * Current user will be injected by CDI into this property using a @Produces
65       * by one of our components. 
66       */
67      @Inject 
68      private CurrentUser user;
69      
70      /**
71       * This is initially populated using a call to the back-end for persisted 
72       * items for sale and then updated as products are added for sale during the 
73       * conversation.
74       */
75      private List<Product> products;
76      /**
77       * This is the product the page is currently working with. No argument 
78       * actions methods will be called and it is assumed they should be acting
79       * on the last item referenced by setProduct().
80       */
81      private Product product;
82  
83      /***************************************
84       * JSF data
85       */
86      /**
87       * Form used when adding a new product. We will hide this form 
88       * when the user is not actively adding a new product.
89       <pre>
90           <h:form binding="#{sellerController.form}" rendered="false">
91       </pre>
92       */
93      private UIForm form;
94      /**
95       * Form used when displaying the list of products
96       <pre>
97           <h:form binding="#{sellerController.tableForm}"
98       </pre>
99       */
100     private UIForm tableForm;
101     /**
102      * Command button used when user chooses to add a new product. We track this
103      * so we can hide it while the user is entering the product data.
104      <pre>
105         <h:commandButton binding="#{sellerController.addCommand}" accesskey="n" 
106      </pre>
107      */
108     private UICommand addCommand;
109     
110     @PostConstruct
111     public void init() {
112         logger.debug("*** SellerController({}):init ***", super.hashCode());
113         conversation.begin(); //got to start it since we declared it
114     }
115 
116     @PreDestroy
117     public void destroy() {
118         logger.debug("*** SellerController({}):destroy ***", super.hashCode());
119     }
120     
121     //data model methods
122     public Member getSeller() {
123         return user.getMember();
124     }
125     public List<Product> getProducts() {
126         logger.debug("getProducts()={}", products);
127         if (products==null) {
128             products = catalog.getSellerProducts(user.getMember(), 0, 0);
129         }
130         return products;
131     }
132     public void setProducts(List<Product> products) {
133         logger.debug("setProducts({})", products);
134         this.products=products;
135     }
136     
137     /**
138      * Associated getter/setter is called for inputText.value elements. The 
139      * initial value of the form field will be the value provided in getProduct.
140      <pre>
141         <h:inputText value="#{sellerController.product.name}"/>
142      </pre>
143      */
144     public Product getProduct() {
145         logger.debug("getProduct()={}", product);
146         return product; 
147     }
148     public void setProduct(Product product) {
149         logger.debug("setProduct({})", product);
150         this.product = product;
151     }
152     public List<SelectItem> getCategories() {
153         logger.debug("getCategories()");
154         List<SelectItem> list = new ArrayList<SelectItem>(ProductCategory.values().length);
155         for (ProductCategory pc: ProductCategory.values()) {
156             list.add(new SelectItem(pc, pc.getPrettyName()));
157         }
158         return list;
159     }
160     
161     
162     /** 
163      * The provider will supply a UICommand object for a form within the binding
164      * sellerController.addCommand. 
165     <pre>
166     <h:form>
167         <h:commandLink binding="#{sellerController.addCommand}" accesskey="n" 
168             action="#{sellerController.addNew}" 
169             value="Sell New Product"/>
170     </h:form>
171     </pre>
172     The provider will access that property through setAddCommand() and getAddCommand().
173     This controller gets a chance to modify the UICommand properties but the object
174     itself is provided by the JSF provider.
175      */
176     public UICommand getAddCommand() { 
177         logger.debug("getAddCommand()={}", addCommand);
178         return addCommand; 
179     }
180     public void setAddCommand(UICommand addCommand) {
181         logger.debug("setAddCommand(addCommand={})", addCommand);
182         this.addCommand = addCommand;
183     }
184     
185     /**
186      * This action method will instantiate a new "Product" bean, disable 
187      * the form containing this action, enable the form that will work on the 
188      * new Product bean. 
189      *    
190      * The action method is referred to by name in an action. The Return value is used
191      * to navigate to a new page.
192             action="#{sellerController.addNew}"
193             
194         1) optionally call business logic
195         2) return a value that will be used to choose the next page             
196      */
197     public String addNew() {
198         logger.debug("addNew()");
199         this.product = new Product(); //create a new Product instance
200         
201         //instead of having our user fill in everything -- lets make some stuff up
202         //to be initially displayed in the form
203         Random r = new Random();
204         product.setCategory(ProductCategory.values()[r.nextInt(ProductCategory.SPORT.ordinal())]);
205         product.setName("name" + r.nextInt(100));
206         product.setYear(1980 + r.nextInt(30));
207         product.setPrice(new BigDecimal(r.nextInt(10000)));
208         form.setRendered(true);       //activate the form that will operate on new Product
209         addCommand.setRendered(false);//disable the view that called this action method
210         return null;                  
211     }
212     
213     
214     
215     /*
216     <h:form binding="#{sellerController.form}" rendered="false">
217         <h:outputText value="Category"/>
218         <h:selectOneMenu value="#{sellerController.product.category}" required="true">
219             <f:selectItems value="#{sellerController.categories}"/>
220         </h:selectOneMenu>
221         
222         <h:outputText value="Name"/>
223         <h:inputText value="#{sellerController.product.name}" required="true"/>
224         
225         <h:outputText value="Year"/>
226         <h:inputText value="#{sellerController.product.year}"/>
227         
228         <h:outputText value="Price"/>
229         <h:inputText value="#{sellerController.product.price}"/>
230         
231         <p></p>
232         <h:commandButton value="Add Product" action="#{sellerController.add}"/>
233     </h:form>
234      */
235     public UIForm getForm() { 
236         logger.debug("getForm()={}, rendered={}", form, form==null?null : form.isRendered());
237         return form; 
238     }
239     public void setForm(UIForm form) {
240         logger.debug("setForm(form={}, rendered={})", form, form==null? null : form.isRendered());
241         this.form = form;
242     }
243     
244     /**
245      * This action method is used to add the populated "Product" bean to the 
246      * conversation. 
247      * 
248      * Action Method to start working with a new product. The exact name of this 
249      * method is referenced in the action element and the return value is used 
250      * to navigate to another page.
251      * 
252         The exact name of this command is used in commandButton.action        
253         <h:commandButton value="Add Product" action="#{sellerController.add}"/>
254         
255         1) optionally call business logic
256         2) return a value that will be used to choose the next page
257      */
258     public String add() {
259         logger.debug("add(): product={}", product);
260         products.add(product);
261         Collections.sort(products, new Product.ProductASC());
262         form.setRendered(false);
263         addCommand.setRendered(true);
264         return "/seller/seller-products";
265     }
266     
267     
268     /*
269     <h:form binding="#{sellerController.tableForm}">
270      <h:dataTable value="#{sellerController.products}" var="product">
271        <h:column>
272             <f:facet name="header">
273                 <h:column>
274                     <h:outputText value="Name"></h:outputText>
275                 </h:column>
276             </f:facet>
277             <h:outputText value="#{product.name}"/>
278          </h:column>
279          
280          <h:column>
281              <f:facet name="header">
282                  <h:column>
283                      <h:outputText value="Actions"></h:outputText>
284                  </h:column>
285              </f:facet>
286              <h:panelGrid columns="2">
287                  <h:commandLink value="delete" action="#{sellerController.delete}">
288                      <f:setPropertyActionListener 
289                          target="#{sellerController.product}" 
290                          value="#{product}"/>
291                  </h:commandLink>
292              </h:panelGrid>
293          </h:column>
294          
295      </h:dataTable>
296     </h:form>
297      */
298     public UIForm getTableForm() { 
299         logger.debug("getTableForm()={}, rendered={}", tableForm, tableForm==null?null:tableForm.isRendered());
300         return tableForm; 
301     }
302     public void setTableForm(UIForm tableForm) {
303         logger.debug("setTableForm(tableForm={}, rendered={})", tableForm, tableForm==null ? null : tableForm.isRendered());
304         this.tableForm = tableForm;
305     }
306     /*
307      * Action Method: The exact name of this method is referenced in the action
308      *     attribute and the return value is used to navigate to another page.
309         <h:commandLink value="delete" action="#{sellerController.delete}">
310         
311         1) optionally call business logic
312         2) return a value that will be used to choose the next page
313      */
314     public String delete() {
315         logger.debug("delete(): product={}", product);
316         products.remove(product);
317         catalog.remove(product);
318         return null;
319     }
320     
321     public String save() {
322         logger.debug("save", product);
323         //create a relationship to the current user as the seller
324         product.setSeller(user.getMember());
325         //save to DB
326         products.remove(product);
327         try {
328             product = catalog.addProduct(product);
329             //replace with merged instance
330             products.add(product);
331             Collections.sort(products, new Product.ProductASC());
332             return null;
333         } catch (InvalidProduct ex) {
334             String errorMsg = "error saving product:" + product;
335             error.setError(errorMsg);
336             error.setException(ex);
337             return "error";
338         }
339     }
340 }