Portal HOWTO: Develop a module
This article describes how to develop a module. Requirements for developing a module are:
- Java 1.6
- MySQL or Oracle database
- IDE
- Know-how in Spring Framework, JPA, Wicket and Maven 2
The Devproof module development comes only with few proprietary interfaces. The core technologies are the Spring Framework, Hibernate (with JPA annotations) and Apache Wicket. You mainly develop with these frameworks.
Overview

The module which is always required is the Devproof Core. It contains all necessary core modules like user management, tag support and other common stuff. One module represents one Maven artifact. A module can have dependencies to other modules, e.g. the download and bookmark module require the deadlink module. It is possible to run Devproof Core standalone without any modules, so you have a blank portal framework.
Currently there is no Maven archetype, but you can clone it from Github or check out from SVN and take the blog module as your module template.
# Git users do git clone git://github.com/devproof/portal.git devproof # SVN users do svn checkout https://svn.github.com/devproof/portal/ devproof
After checking out you will get the folders:
module-article -> article module module-blog -> blog module module-bookmark - > bookmark module ... and more modules portal-build -> builds all the modules portal-core -> devproof core portal-webapp -> bundles all modules to a WAR file
Build the projects with:
cd devproof mvn clean install -Pall
You can also build with the profiles blog, download and so on (see Getting started). After building the application all defined modules are packaged as JAR files. The portal-webapp/target/ folder contains the whole packaged WAR file. The portal-webapp/target/sql/ contains the concatinated SQL files. Each built module contains its own SQL files in the JAR file. While bundling all SQL files (of the selected modules) will be extracted, concatinated and saved in this folder.
Module structure
If you browse in the existing modules, you will see that a module normally follows this structure:
.\mymodule | pom.xml | \---src +---main | +---java | | \---com | | \---mycompany | | \---mymodule | | \---dao (DAOs, only interfaces!) | | \---entity (JPA entities) | | \---page (wicket pages) | | \---panel (wicket panels) | | \---query (query objects, e.g. for the dataprovider) | | \---service (business logic) | | devproof-module.xml (configuration of the module) | | | +---resources | | \---sql (SQL files) | | \---com | | \---mycompany | | \---mymodule | | \---page (HTML, CSS and Properties files) | | \---panel (HTML, CSS and Properties files) | | | \---webapp | \---WEB-INF | web.xml (only for testing and developing) +---test ... (junit tests)
Naming conventions
The naming conventions are quite simple. There are only class naming conventions. The classes should end with these suffixes:
| Suffix | Sample | Description |
| Page | BlogPage | Wicket page |
| Panel | SearchBoxPanel | Wicket panels |
| Entity | BlogEntity | A JPA/hibernate persistence class |
| Query | BlogQuery | Query objects for the dataprovider |
| Dao | BlogDao | Data access object interface |
| Service | BlogService | Business logic interface |
| ServiceImpl | BlogServiceImpl | Business logic implementation |
| Constants | BlogConstants | Classes with constants |
Configuration
The whole configuration is located in the devproof-module.xml. This is only a normal spring context. Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- Module configuration -->
<bean class="org.devproof.portal.core.config.ModuleConfiguration">
<property name="name" value="Bookmark"/>
<property name="moduleVersion" value="${devproof.version}"/>
<property name="portalVersion" value="${devproof.version}"/>
<property name="author" value="devproof - Carsten Hufe"/>
<property name="url" value="http://devproof.org"/>
<!-- Hibernate/JPA entities -->
<property name="entities">
<list>
<value>org.devproof.portal.module.bookmark.entity.BookmarkEntity</value>
<value>org.devproof.portal.module.bookmark.entity.BookmarkTagEntity</value>
</list>
</property>
<property name="pages">
<list>
<bean class="org.devproof.portal.core.config.PageConfiguration">
<property name="mountPath" value="/bookmarks"/>
<property name="pageClass" value="org.devproof.portal.module.bookmark.page.BookmarkPage"/>
<property name="registerMainNavigationLink" value="true"/>
</bean>
<bean class="org.devproof.portal.core.config.PageConfiguration">
<property name="mountPath" value="/bookmark"/>
<property name="pageClass" value="org.devproof.portal.module.bookmark.page.BookmarkRedirectPage"/>
<property name="indexMountedPath" value="true"/>
</bean>
</list>
</property>
<!-- Register boxes -->
<property name="boxes">
<list>
<bean class="org.devproof.portal.core.config.BoxConfiguration">
<property name="boxClass" value="org.devproof.portal.module.bookmark.panel.BookmarkBoxPanel"/>
<property name="name" value="Latest Bookmark Box"/>
</bean>
</list>
</property>
</bean>
<!-- Dao -->
<!-- Services -->
<!-- DataProvider -->
</beans>
The module locator of the Devproof Portal searches for devproof-module.xml files and loads it automatically into the spring context. Then it is looking for all ModuleConfiguration classes. This class must be defined in every module. I think the most bean properties are self-explanatory. Here is a screenshot which describes some properties of the PageConfiguration class:

Application layers
The graphic shows the layer in a module:

Data Access Objects should only consumed by a Service or DataProvider. Normally there is no implementation of the DAO because you should use the provided Generic DAO. The service contains the whole business logic, which should heavy unit tested. A service can consume other services. A data provider provides standardised access to data for wicket listing components. Typically there is no need for an implementation because there is a Generic Data Provider available (see Useful stuff). Wicket pages and components can consume services and data provider. Wicket components are children of Wicket pages. This is a very simple, but powerful layer architecture. It is very easy to mock dependencies in a service and write unit tests for it. You can also write tests for Wicket pages. Furthermore there is no implementation of the DAO, so that you do not have the risk of business logic in the DAO. Finally the architecture is easy to understand for new developers.
Useful stuff
There are two very cool things coming with the Devproof Portal. The first is the Generic DAO and the second is the Generic Data Provider.
Generic DAO
As you have seen in the convention there is no DAO implementation convention, because there is no DAO implementation. Here is an example of a Generic DAO:
public interface DownloadDao extends GenericDao<DownloadEntity, Integer> {
@Query(value = "select distinct(d) from DownloadEntity d join d.allRights ar"
+ " where ar in (select rt from RoleEntity r join r.rights rt where r = ? and rt.right like 'download.view%') order by d.modifiedAt desc", limitClause = true)
public List<DownloadEntity> findAllDownloadsForRoleOrderedByDateDesc(RoleEntity role, Integer firstResult, Integer maxResult);
@BulkUpdate("update DownloadEntity d set d.hits = (d.hits + 1) where d = ?")
public void incrementHits(DownloadEntity download);
@BulkUpdate("update DownloadEntity d set d.numberOfVotes = (d.numberOfVotes + 1), d.sumOfRating = (d.sumOfRating + ?) where d = ?")
public void rateDownload(Integer rating, DownloadEntity download);
@BulkUpdate("update DownloadEntity d set d.broken = true where d = ?")
public void markBrokenDownload(DownloadEntity download);
@BulkUpdate("update DownloadEntity d set d.broken = false where d = ?")
public void markValidDownload(DownloadEntity download);
}
The GenericDao interface provides the default CRUD methods. If this does not suffice you can define own HQL queries. If you have parameters you can use the question mark (?) placeholder in the query. The first question mark represents the first method parameter, the second represents the second method parameter and so on. There is a little exception when limitClause is true in the Query annotation the last two method parameters are for the query limitations. The return type of the defined method must be the same what hibernate returns. With the BulkUpdate annotation you can define efficient update queries.
Defining the Generic DAO in the spring context:
<bean id="downloadDao" parent="baseGenericDao"> <property name="daoInterface" value="org.devproof.portal.module.download.dao.DownloadDao" /> <property name="entityClass" value="org.devproof.portal.module.download.entity.DownloadEntity" /> </bean>
DAOs should only consumed by services or data providers. It should never consumed by wicket pages or components!
Generic Data Provider
Generic Data Providers are useful whenever you display a wicket list component, e.g. the Wicket DataView component.
<bean id="bookmarkDataProvider" parent="persistenceDataProvider" scope="prototype"> <property name="entityClass" value="org.devproof.portal.module.bookmark.entity.BookmarkEntity" /> <property name="sort"> <bean class="org.apache.wicket.extensions.markup.html.repeater.util.SortParam"> <constructor-arg value="title"/> <constructor-arg value="true"/> </bean> </property> </bean>
This is a simple definition for the spring context and it provides paging and sorting for this BookmarkEntity. Additionally it optimizes the queries against the database.
How to run
Generate Eclipse meta files with Maven:
mvn eclipse:eclipse
You can execute the command in the specific module or portal-build folder. After importing you can create a new Eclipse run configuration:

Enter program arguments:

Finally press Run.
In this case you will run the Bookmark module. After making changes at the module you have not to rebuild the module with Maven. Only a restart is required if the hot code replacement fails.
If you have helpful suggestions or corrections, please contact me.

