JavaUsing the Vaadin Framework for Enterpise Apps

Using the Vaadin Framework for Enterpise Apps

For any framework to reign in the enterprise arena is a daunting task. Developers lay their feet cautiously before culling a framework for their mission-critical business application. Vaadin is inching towards it gradually. Forums are flooded with problems. This is a good news because developers are trying and flaws are being surfaced for the Vaadin community to revamp the architecture, code, and so forth. This intensity is easily visible; see the changes made from versions 6 to 7. A lot of prominent restructural work went into the framework. To roll in the enterprise arena, Vaadin should efficiently work with the giants like JEE, Spring, ORM, and the like. Here, I have tried to implement a simple application integrating Hibernate with Vaadin by using the core APIs with no third-party plug-in.

Why Vaadin, After All?

The primary reason why Vaadin really exists is that developers sometimes do not want to switch between Java and other techs such as JavaScript, XML, HTML, and so on while coding. Let the interpreter, compiler, parsers, whatever, do their job implicitly; we’ll speak only Java while riding our favorite IDE. If this is your choice, your insanity is attuned with the Vaadin framework. Do not be alarmed at the initial setback; purge out your problems. Vaadin has a thriving community of trouble-eeking experts. The components look stunning in a browser even without any customized themes. One thing for sure: You’ll love it and pray as well for this framework to support your needs because sometime bugs show their true color and you feel helpless.

Enterprise Peek

We should not forget that Vaadin is basically a UI framework. Supporting dependency injection or EJB is its additional feature. First, we should see if its components work as smoothly as expected by providing the right APIs. Sometimes, the enterprise UI needs are critical to the verge of being idiotic, like dragging a picture into the text field and the picture transformed into its name or dragging a Excel sheet into a table. Working with nested layouts, for example, can be a nightmare at times. For fine tuning, we may have to stoop to CssLayouts or Custom layouts. Supporting these types of insane requirements can drive you nuts, both for the framework and developers. The reason for such high expectations from Vaadin is because it opened the horizon for a desktop-like-web-UI-framework where your main contender is Swing/JavaFX (because Vaadin is desktop-like). Forget the claims; see the performance in your machine and the ease of APIs to use. Do the APIs provide most of the enterprise’s needs? Not quite exactly. Now, there are straight ways of doing things, and there are roundabout ways. Vaadin has a roundabout way of enterprise support. This is fine and acceptable from a UI framework’s perspective, but a misbehaving component is a real pain in the neck. Understandably, it is quite a challenge for any web UI framework to have consistent support for a variety of browsers, not to mention older versions as well. But, in the end, developers are guillotined and need a reliable framework to save losing their heads. Vaadin needs to win their hearts with not only looks but also with performance both inclusive; no compromise.

Walking the Walk with ORM

The application I tried is a very simple one; you’ll find such in many Vaadin tutorials. But, I tried to make it as simple as possible without any third-party plug-ins, with every bit of manual configuration. The enterprise framework used here is JEE with Glashfish Application Server 4.1, Hibernate 4.3.1, and Vaadin 7.2.6. Although not tried, I think, we can implement EJB and dependency injection here as well. The code is self explanatory with basic Vaadin UI components. The data container class used for the table is BeanItemContainer. Some obvious codes are omitted for brevity.

//...import statements
@Entity
public class Employee implements Serializable {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private int empId;
   private String firstName;
      //...lastName, phone, email, address
   @Temporal(TemporalType.DATE)
   private Date birthDate;
   @ManyToOne
   private Department department;

   //...constructors, getters and setters
}

Listing 1: Employee.java

//...import statements

@Entity
public class Department implements Serializable{
   @Id @GeneratedValue(strategy=GenerationType.AUTO)
   private int departmentId;
   private String departmentName;
   @OneToMany
   private List<Employee> employees=new ArrayList<>();

   //...constructors, getters and setters
}

Listing 2: Department.java

//...import statements

public abstract class DAO {

   private static final Logger log =
      Logger.getAnonymousLogger();
   private static final ThreadLocal<Session> session =
      new ThreadLocal<>();
   private static final SessionFactory sessionFactory =
      HibernateUtil.openSession().getSessionFactory();

   public static Session getSession() {
      Session session = (Session) DAO.session.get();
      if  (session == null) {
         session = sessionFactory.openSession();
         DAO.session.set(session);
      }
      return session;
   }
   protected void begin() { getSession().beginTransaction(); }
   protected void commit() { getSession().getTransaction().commit(); }

   protected void rollback() {
      try {
         getSession().getTransaction().rollback();
      } catch (HibernateException ex) {
         log.log(Level.WARNING, "Cannot rollback", ex);
      }
      try {
         getSession().close();
      } catch (HibernateException ex2) {
         log.log(Level.WARNING, "Cannot close", ex2);
      }
      DAO.session.set(null);
   }

   public static void close() { getSession().close();
       DAO.session.set(null); }
}

Listing 3: DAO.java

//...import statements

public class EmployeeDAO extends DAO {
   public List<Employee> list() {
      List<Employee> list = new ArrayList<>();
      try {
         begin();
         Query hql =
            getSession().createQuery("from Employee");
         list = hql.list();
         commit();
      } catch (HibernateException ex) {
         rollback();
      }
      return list;
   }
   public Employee create(String firstName, String lastName,
      String phone, Date birthDate, String address, String email,
      String department) {
      DepartmentDAO dao = new DepartmentDAO();
      Employee emp = new Employee(firstName, lastName, phone,
         birthDate, address, email, dao.getByName(department.trim()));
      try {
         begin();
         getSession().save(emp);
         commit();
      } catch (HibernateException ex) {
         rollback();
          emp = null;
      }
      return emp;
   }

   public void remove(Employee emp) {
      try {
         begin();
         getSession().delete(emp);
         commit();
      } catch (HibernateException ex) {
         rollback();
      }
   }

   public Employee getEmpById(int id) {
      Employee emp = null;
      try {
         begin();
         Query hql = getSession()
            .createQuery("from Employee where id=:id");
         emp = (Employee) hql.uniqueResult();
         commit();
      } catch (HibernateException ex) {
         rollback();
      }
      return emp;
   }

   public Employee update(Employee emp) {
      try {
         begin();
         getSession().update(emp);
         commit();
      } catch (HibernateException ex) {
         rollback();
         emp=null;
      }
      return emp;
   }
}

Listing 4: EmployeeDAO.java

//...import statements

public class DepartmentDAO extends DAO{
   public List<Department> list(){
      List<Department> list=new ArrayList<>();
      try{
         begin();
         Query hql=getSession().createQuery("from Department");
         list=hql.list();
         commit();
      }catch(HibernateException ex){
         rollback();
      }
      return list;
   }

   public Department getById(int departmentId){
      Department d=null;
      try{
         begin();
         Query hql = getSession().createQuery("from
            Department where departmentId=:departmentId");
         hql.setInteger("departmentId", departmentId);
         d=(Department)hql.uniqueResult();
         commit();
      }catch(HibernateException ex){
         rollback();
      }
      return d;
   }

   public Department getByName(String departmentName){
      Department d=null;
      try{
         begin();
         Query hql = getSession().createQuery("from
            Department where departmentName=:departmentName");
         hql.setString("departmentName", departmentName);
         d=(Department)hql.uniqueResult();
         commit();
      }catch(HibernateException ex){
         rollback();
      }
      return d;
   }

   public Department create(String name){
      Department d=new Department(name);
      try{
         begin();
         getSession().save(d);
         commit();
      }catch(HibernateException ex){
         rollback();
         d=null;
      }
      return d;
   }

}

Listing 5: DepartmentDAO.java

//...import statements

public class HibernateUtil {

   private static final SessionFactory sessionFactory;
   private static final StandardServiceRegistry serviceRegistry;

   static {
      try {
         Configuration config = new
            Configuration().configure("hibernate.cfg.xml");
         serviceRegistry =
            new StandardServiceRegistryBuilder()
            .applySettings(config.getProperties()).build();
         sessionFactory =
            config.buildSessionFactory(serviceRegistry);
      } catch (Throwable ex) {
         System.err.println("Initial
            SessionFactory creation failed." + ex);
         throw new ExceptionInInitializerError(ex);
      }
   }

   public static Session openSession() {
      return sessionFactory.openSession();
   }
}

Listing 6: HibernateUtil.java

<?xml ...>
   <session-factory>
      <property name="hibernate.dialect">
         org.hibernate.dialect.MySQLDialect</property>
      <property name="hibernate.connection.driver_class">
         com.mysql.jdbc.Driver</property>
      <property name="hibernate.connection.url">
         jdbc:mysql://localhost:3306/mydatabase?
         zeroDateTimeBehavior=convertToNull
      </property>
      <property name="hibernate.connection.username">
         user1</property>
      <property name="hibernate.connection.password">
         secret</property>
      <property name="hibernate.connection.pool_size">1
      </property>
      <property name="show_sql">true</property>
      <property name="hbm2ddl.auto">update</property>

      <!-- Mapping files -->
         <mapping class="org.example.entity.Employee" />
         <mapping class="org.example.entity.Department" />
   </session-factory>
</hibernate-configuration>

Listing 7: hibernate.cfg.xml

//...import statements

@PreserveOnRefresh
public class ApplicationMain extends UI{>

   private final VerticalLayout vlayout=new VerticalLayout();
   TabSheet tabSheet=new TabSheet();

   @WebServlet(value = "/*", asyncSupported = true)
   @VaadinServletConfiguration(productionMode =
      false, ui = ApplicationMain.class)
   public static class Servlet extends VaadinServlet {
      private static final long serialVersionUID = 1L;
   }

   @Override
   protected void init(VaadinRequest request) {
      vlayout.addComponent(createMenuBar());
      vlayout.addComponent(tabSheet);
      setContent(vlayout);
   }

   private MenuBar createMenuBar(){
      MenuBar menubar=new MenuBar();
      menubar.setSizeFull();
      MenuBar.MenuItem empMenu=menubar.addItem("File", null);
      empMenu.addItem("Employee", new MenuHandler());
      empMenu.addItem("Department", new MenuHandler());

      //...other menus

      return menubar;

   }

   private class MenuHandler implements MenuBar.Command{

      @Override
      public void menuSelected(MenuBar.MenuItem selectedItem) {
         switch (selectedItem.getText()) {
            case "New Employee":
               EmployeeEditor ed=new EmployeeEditor();
               tabSheet.addTab(ed, "Employee Details");
               tabSheet.getTab(ed).setClosable(true);
               break;
            case "New Department":
               DepartmentEditor editor=new DepartmentEditor();
               UI.getCurrent().addWindow(editor);
               break;
          }

      }

   }

}

Listing 8: ApplicationMain.java

//...import statements

public class EmployeeEditor extends HorizontalSplitPanel
   implements ComponentContainer {

   private final BeanItemContainer<Employee> employees =
      new BeanItemContainer<>(Employee.class);
   private final Table employeeTable = new Table();

   private final EmployeeDAO empDAO = new EmployeeDAO();
   private final DepartmentDAO deptDAO = new DepartmentDAO();

   private final TextField firstNameField
      // ...other TextFields: lastNameField, phoneField,
      // addressField, emailField

   private final PopupDateField birthDateField =
      new PopupDateField("Birth Date");
   private final ComboBox departmentCombo =
      new ComboBox("Department");

   private final Button addNewButton =
      new Button("Add New Employee");
      //...other Buttons: updateButton, removeButton, refreshButton

   public EmployeeEditor() {
      refreshEmployeeList();
      refreshDepartmentList();
      employeeTable.setContainerDataSource(employees);
      employeeTable.setVisibleColumns(new
         Object[]{"empId", "firstName", "lastName",
         "phone", "birthDate", "address", "email"});
      employeeTable.setSelectable(true);
      employeeTable.addValueChangeListener(
         (Property.ValueChangeEvent event) -> {
         Employee emp = (Employee) employeeTable.getValue();
         populateFields(...);
      });
      VerticalLayout vlayout=new VerticalLayout();
      vlayout.setMargin(true);
      vlayout.addComponent(removeButton);
      removeButton.addClickListener((Button.ClickEvent event) -> {
         Employee emp = (Employee) employeeTable.getValue();
         if (emp != null) {
            empDAO.remove(emp);
            refreshEmployeeList();
         }
      });
      vlayout.addComponent(employeeTable);
      vlayout.setSizeUndefined();
      addComponent(vlayout);
      addComponent(createForm());
      setSplitPosition(60, Unit.PERCENTAGE);
   }

   private FormLayout createForm() {
      FormLayout form = new FormLayout();
      form.setMargin(true);
      form.setSizeFull();
      HorizontalLayout hlayout = new HorizontalLayout();
      hlayout.addComponent(updateButton);
      updateButton.addClickListener((Button.ClickEvent event) -> {
         Employee emp = (Employee) employeeTable.getValue();
         if (isValidFieldData()) {
            emp.setFirstName(firstNameField.getValue().trim());

            //....

            emp.setEmail(emailField.getValue());
            emp.setDepartment(deptDAO.getByName((String)
               departmentCombo.getValue()));
            if (empDAO.update(emp) == null) {
               Notification.show("Cannot update. Try
                  again later.");
            } else {
               Notification.show("Employee updated");
               refreshEmployeeList();
            }
         }
      });
      hlayout.addComponent(refreshButton);
      refreshButton.addClickListener((Button.ClickEvent event) -> {
         populateFields("", "", "",
            new Date(), "", "", "");
         refreshDepartmentList();
         refreshEmployeeList();
      });
      form.addComponent(hlayout);
      form.addComponent(firstNameField);
      firstNameField.setRequired(true);
      firstNameField.setRequiredError("First Name cannot be empty");
      //...

      form.addComponent(departmentCombo);
      departmentCombo.setRequired(true);
      departmentCombo.setRequiredError("Select department");
      form.addComponent(addNewButton);
      addNewButton.addClickListener((Button.ClickEvent event) -> {
         if (isValidFieldData()) {
            Employee e = empDAO.create(firstNameField.getValue(),
               //...
               emailField.getValue(), (String) departmentCombo.getValue());
            if (e != null) {
               employees.addBean(e);
            }
         }
      });
      return form;
   }

   private boolean isValidFieldData() {

      //...validating fields
   }

   private void refreshEmployeeList() {
      employees.removeAllItems();
      employees.addAll(empDAO.list());
   }

   private void refreshDepartmentList() {
      for (Department d : deptDAO.list()) {
         departmentCombo.addItem(d.getDepartmentName());
      }
   }

   private void populateFields(String fname, String lname,
      String ph, Date bdate, String add, String mail, String dept) {
      firstNameField.setValue(fname);
      //...
      departmentCombo.setValue(dept);
   }

}

Listing 9: EmployeeEditor.java

//...import statements

public class DepartmentEditor extends Window {

   private final TextField deptNameField =
      new TextField("Department Name");
   private final Button saveButton = new Button("Save");
   private final DepartmentDAO deptDAO = new DepartmentDAO();

   public DepartmentEditor() {
      FormLayout form = new FormLayout();
      form.setMargin(true);
      form.addComponent(deptNameField);
      form.addComponent(saveButton);
      saveButton.addClickListener((Button.ClickEvent event) -> {
         Department d = deptDAO.getByName(deptNameField.getValue().trim())
         if (d == null) {
            if(deptDAO.create(deptNameField.getValue().trim())==null){
               Notification.show("Cannot save. Try again later");
            }else{
               Notification.show("New Department Added");
            }
         }
      });
      this.setContent(form);
      this.setSizeUndefined();
      this.center();
   }
}

Listing 10: DepartmentEditor.java

Output

VFramework1
Figure 1: Output of the preceding code

Conclusion

The BeanItemContainer does not have the most expected explicit refresh method to reflect immediate database changes. The addBean() method is quite helpful, but a getBean() method is expected as well. As the layout nesting increases, the browser (tried in Chrome) rendering behaves erratically. Also, it is confusing at times working with the setSizeFull() method between nested layouts. These are some of the glitches I think will be overcome soon, hopefully. Overall, when you see the outcome, it feels great. But to catwalk through the enterprise ramp, Vaadin is yet to be ready.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories