1
\$\begingroup\$

I'm making my first steps in Java EE. This is a simple guestbook made using Java EE 7 technologies (JSF, CDI, EJB, JPA).

Does the code follows the standard best practices? Is there a possibility for improvement?

enter image description here

Guestbook entry entity

package learning.guestbook2;

import java.io.Serializable;
import java.time.OffsetDateTime;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

@Entity
@NamedQuery(name = "findAll", query = "SELECT g FROM GuestbookEntry g")
public class GuestbookEntry implements Serializable {

    private static final long serialVersionUID = -8020012557332377340L;
    private String content;
    private String name;
    private OffsetDateTime dateTime;

    @Id @GeneratedValue
    private long id;

    public GuestbookEntry() {
        dateTime = OffsetDateTime.now();
    }

    public String toString() {
        return "Guestbook entry from " + name + ", posted " + dateTime;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public OffsetDateTime getDateTime() {
        return dateTime;
    }

    public void setDateTime(OffsetDateTime dateTime) {
        this.dateTime = dateTime;
    }
}

EJB responsible for business logic

package learning.guestbook2;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import java.util.logging.Logger;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

@Stateless
public class GuestbookEntryEJB {
    @Inject
    protected Logger log;

    @PersistenceContext(unitName = "GuestbookEntries2")
    EntityManager em;

    public List<GuestbookEntry> findEntries() {
        TypedQuery<GuestbookEntry> query = 
                em.createNamedQuery("findAll", GuestbookEntry.class);
        List<GuestbookEntry> list = query.getResultList();
        return list;
    }

    public void save(GuestbookEntry entry) {
        em.persist(entry);
        em.flush();
        log.info("Guestbook entry saved: " + entry.toString());
    }

    public String getStringDateTime(GuestbookEntry entry) {
        DateTimeFormatter formatter = 
                DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
        LocalDateTime localDateTime = entry.getDateTime().toLocalDateTime();
        return localDateTime.format(formatter);
    }
}

Logger producer

package learning.guestbook2;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;

import java.util.logging.Logger;

public class LoggerProducer {
    @Produces
    public Logger produceLogger(InjectionPoint ip) {
        return Logger.getLogger(ip.getMember().getDeclaringClass().getName());
    }
}

Controller - CDI bean

package learning.guestbook2;

import java.util.List;

import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class GuestbookEntryController {
    @EJB
    private GuestbookEntryEJB entryEJB;
    private GuestbookEntry entry = new GuestbookEntry();

    public void save() {
        entryEJB.save(entry);
    }

    public List<GuestbookEntry> findEntries() {
        return entryEJB.findEntries();
    }

    public String getStringDateTime() {
        return entryEJB.getStringDateTime(entry);
    }

    public GuestbookEntry getEntry() {
        return entry;
    }

    public void setEntry(GuestbookEntry entry) {
        this.entry = entry;
    }

    public GuestbookEntryEJB getEntryEJB() {
        return entryEJB;
    }

    public void setEntryEJB(GuestbookEntryEJB entryEJB) {
        this.entryEJB = entryEJB;
    }
}

View - guestbook.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
        xmlns:h="http://xmlns.jcp.org/jsf/html"
        xmlns:ui="http://java.sun.com/jsf/facelets">
  <h:head>
    <title>#{msg.page_title}</title>
    <link rel="stylesheet" type="text/css" href="css.css"/>
  </h:head>
  <h:body>
    <h:form>
        <h:outputText value="#{msg.your_entry}" styleClass="header"/><br/>
        <br/>
        <h:outputText value="#{msg.your_name}&#160;"/>
        <h:inputText value="#{guestbookEntryController.entry.name}"/>
        <br/><br/>
        <h:outputText value="#{msg.your_msg}"/>
        <br/>
        <h:inputTextarea 
            rows="5" cols="100" 
            value="#{guestbookEntryController.entry.content}"/>
        <br/><br/>
        <h:commandButton 
            value="#{msg.submit}" action="#{guestbookEntryController.save()}"/>
        <br/><br/>
        <h:outputText value="#{msg.entries}" styleClass="header"/><br/>
        <br/>
        <ui:repeat 
            value="#{guestbookEntryController.findEntries()}" 
            var="value">
            <h:outputText value="#{msg.posted} 
                #{guestbookEntryController.getStringDateTime()} 
                #{msg.by} #{value.name}"
                styleClass="guestbookEntryHeader"/><br/>
            <h:outputText value="#{value.content}" styleClass="guestbookEntry"/>
            <br/><br/>
        </ui:repeat>
    </h:form>
  </h:body>
</html>

css.css

@CHARSET "UTF-8";

body {
    background-color: #ffffff;
    font-family: Verdana, "Verdana CE",  Arial, "Arial CE", "Lucida Grande CE", lucida, "Helvetica CE", sans-serif;
    font-size: 12px;
    color: #000000;  
}

.header {
    background-color: #ffffff;
    font-size: 14px;
    font-family: Verdana, "Verdana CE",  Arial, "Arial CE", "Lucida Grande CE", lucida, "Helvetica CE", sans-serif;
    font-weight: bold;
    color: #0000ff;  
}

.guestbookEntryHeader {
    background-color: #ffffff;
    font-family: Verdana, "Verdana CE",  Arial, "Arial CE", "Lucida Grande CE", lucida, "Helvetica CE", sans-serif;
    font-size: 12px;
    color: #777777; 
}

.guestbookEntry {
    background-color: #ffffff;
    font-family: Verdana, "Verdana CE",  Arial, "Arial CE", "Lucida Grande CE", lucida, "Helvetica CE", sans-serif;
    font-size: 12px;
    font-style: italic;
    color: #000000; 
}

Configuration files:

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
 http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="GuestbookEntries2" transaction-type="JTA">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <jta-data-source>jdbc/guestbookentries2</jta-data-source>
    </persistence-unit>
</persistence>

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="all">
</beans>

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                    http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
                    version="2.2">
    <application>
        <locale-config>
            <default-locale>en</default-locale>
        </locale-config>
        <resource-bundle>
            <base-name>MessagesBundle</base-name>
            <var>msg</var>
        </resource-bundle>
    </application>
</faces-config>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
            http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
    id="WebApp_ID" 
    version="3.1">
  <display-name>guestbook2</display-name>
  <welcome-file-list>
    <welcome-file>guestbook.xhtml</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
</web-app>
\$\endgroup\$
1
\$\begingroup\$

Regarding the UI:

  1. While colons at the end of labels are usual in rich client UIs I'd omit them on a web UI. See SE's profile and profile edit pages, for instance.
  2. Increase the width of the name field. What if Gloria, Princess of Thurn and Taxis visits your guest book? (And hopefully she doesn't want to enter her full name then :-)
  3. +1 for offering a resizeable text area. I've seen too many web pages where the designer's assumption about the text length to be expected was too small.
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.