Our Blog

As mentioned in day 2 of our series, we have a main service interface that came from an initial discussion of what we think a pastebin should do.Â What can be easier than creating an entity model for a pastebin application, right? It’s just a big text area that holds a piece of text (content) that someone wants to share with the rest of the world, or at least with someone else who might be interested in looking at it. However, there are some specific things that we wanted to accomplish in our application that came from a series of ideas by the different team members. Of course, although not really a requirement, we wanted to build this application using Apache Wicket.

Requirements

Here is a list of the ideas (or requirements) we wanted for our pastebin, in no particular order:

A paste item must be identified by a unique id and/or timestamp, and it should contain text and optionally a language identifier (useful for syntax highlighting).

A Paste item may be a Reply of another Paste item (can have a parent item).

A Paste item may have one or more replies (children items).

A Paste item may be private. Private items will be identified by a special random string token of a defined length.

A Paste item may be associated with a specific user (author).

A given author that has been identified to the system will be able to see all the paste items that he/she created.

Models/Entities

From the set of requirements we can get a sense of how the entities are going to be created and how they’re going to relate to each other, as seen in the following diagram:

This looks like a very simple entity model, but it fits with what we want to accomplish for this particular project. During the brainstorming session many features were discussed, like the ability to edit and/or delete an item that you own (identified by some session token saved in a cookie), the ability to upload images, have an API for external clients that want access to the pastebin, etc. This might be implemented in the future, but were omitted for the first iteration of the project. However, some of the basis for the functionalities is there, like having a client token to identify the different clients accessing the server and be able to show them in the future.

So now that we have a base, let’s get to do some actual coding. As mentioned before, we decided on using Hibernate as our ORM mapping. This is how we define the PasteItem class (the getters and setters have been omitted from the class to make it easier to read):

Service layer

We defined a primary service interface that the Wicket front-end will use to interact between the web pages and the rest of the application. The service layer classes are usually responsible for implementing the required business rules of the application, and they can be as complex or as simple as required, depending on what the actual requirements of the application are. Wikipedia defines a business rule as:

Business rule is a statement that defines or constrains some aspect of the business. It is intended to assert business structure or to control or influence the behavior of the business. Individual business rules that describe the same facet of an enterprise are usually arranged intoÂ business rulesets. Business rules describe the operations, definitions and constraints that apply to an organization in achieving its goals.

In layman's terms, a business rule is anything that affects the way we act on our data. This is how our service implementation looks like:

import com.mysticcoders.mysticpaste.model.LanguageType;
import com.mysticcoders.mysticpaste.model.PasteItem;
import com.mysticcoders.mysticpaste.persistence.PasteItemDao;
import com.mysticcoders.mysticpaste.utils.TokenGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class PasteServiceImpl implements PasteService {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static final int DEFAULT_TOKEN_LENGTH = 10;
private PasteItemDao itemDao;
private int tokenLength;
public PasteServiceImpl() {
this.tokenLength = DEFAULT_TOKEN_LENGTH;
}
public PasteServiceImpl(PasteItemDao itemDao, int tokenLength) {
this.itemDao = itemDao;
this.tokenLength = tokenLength;
}
@Transactional(readOnly = true)
public List getLatestItems(String clientToken, int count, int startIndex, boolean threaded)
throws InvalidClientException {
logger.trace("Service: getLatestItems. clientToken = {}, count = {}, startIndex = {}, threaded = {}",
new Object[]{clientToken, count, startIndex, threaded});
List results = null;
if (threaded) {
results = itemDao.findThreaded(count, startIndex);
} else {
results = itemDao.find(count, startIndex);
}
if (null == results) {
logger.warn("Found no items in database.");
results = new ArrayList();
}
return results;
}
@Transactional(readOnly = true)
public PasteItem getItem(String clientToken, long id) throws InvalidClientException {
return itemDao.get(id);
}
@Transactional(readOnly = true)
public PasteItem findPrivateItem(String clientToken, String privateToken) throws InvalidClientException {
return itemDao.findByToken(privateToken);
}
@Transactional(readOnly = true)
public List findItemsByLanguage(String clientToken, LanguageType languageType, int count,
int startIndex, boolean threaded)
throws InvalidClientException {
List results = null;
if (threaded) {
results = itemDao.findByLanguageThreaded(languageType, count, startIndex);
} else {
results = itemDao.findByLanguage(languageType, count, startIndex);
}
if (null == results) {
results = new ArrayList();
}
return results;
}
@Transactional(rollbackFor = Exception.class)
public long createItem(String clientToken, PasteItem item) throws InvalidClientException {
if (null != item && item.isPrivate()) {
item.setPrivateToken(TokenGenerator.generateToken(getTokenLength()));
}
// set created Timestamp
item.setTimestamp(new Date(System.currentTimeMillis()));
return itemDao.create(item);
}
public long getLatestItemsCount(String clientToken) throws InvalidClientException {
return itemDao.getPasteCount();
}
public PasteItemDao getItemDao() {
return itemDao;
}
public void setItemDao(PasteItemDao itemDao) {
this.itemDao = itemDao;
}
public int getTokenLength() {
return tokenLength;
}
public void setTokenLength(int tokenLength) {
this.tokenLength = tokenLength;
}
}
The business rules for the pastebin are very light in nature. One of the business rule we have from the requirements is to generate a random string token to use as the private paste identifier, so that instead of having a sequential id (which can be guessed), it is identified by this string and a special url.
We're using slf4j as our logging mechanism. This allows us to statically map our logging to one of log4j, jdk, etc., and it also allows us to have very simple logging messages that will help us 'debug' the application in a sense. Nowadays it is considered bad practice to use System.out.println() messages as we don't have control over them (i.e. they will always appear). Having a logging mechanism with separate message levels allows us to control what we want to show.
It is also interesting to note that in order to support a transactional set of methods, we used Spring's @Transactional annotation. This annotation allows us to mark a method (and its underlying method calls) as part of one transaction, and optionally to mark said transaction as read-only. Also, take care that marking a method as transactional is often not enough, as we have to specify under which conditions the transaction needs to rollback (in our case, every time an exception is thrown). This is because by default, spring only rolls back transactions when runtime exceptions are thrown.

Wiring everything together

Once we have the service and the persistence layer, we need a way to put everything together in order for our application to work. Since we are using Spring, we only need to define our beans in the applicationContext.xml:

The Service is configured as a bean, with a reference to the Dao implementation and to a variable that will be replaced by maven when building for the appropriate platform, as mentioned in day 1. The Dao is configured with a reference to Hibernate's sessionFactory bean, and the rest is the configuration for Hibernate (the data source, the transaction manager, etc.).
Since we're using annotations, we need to set the property annotatedClasses with a list of the Hibernate (or JPA) configured entity classes, in this case the PasteItem class. In order for the @Transactional annotation to work we need to tell Spring that our transaction is driven by those annotations with the tag. I've been involved in previous projects where the other developers think they are doing transactions because they used the annotation, but forgot to add this piece to the configuration, thus resulting in database inconsistencies.

Conclusion

Designing the backend involves very little Wicket (or nothing at all as we saw here), but it's very important to separate our different layers to make the application easier to maintain. Using Spring and Hibernate is win-win situation because we leave many configuration options to Spring, and we are able to provide an easy to use and easy to understand implementation of our service and persistence layer.
EDIT: Added the wicketApplication bean to the Spring configuration file.