13 April 2020

Coding Standards for Liferay

This article is meant to provide guidance for myself and others on what I would recommend from my coding experience using Liferay. we will focus on Java initially and expand into Javascript. Most of this experience is carried forward from Liferay 6.2 but looks to work well with Liferay 7.1. We ran both versions using OpenJDK 1.8.

Our standards will be based on Effective Java 2nd Edition available from Amazon and from Alibris. A 3rd edition is available. It is for newer OpenJDKs and will not be referenced here. Most of our deviations from the book are an effort to make unit testing faster and easier to achieve.

SOLID

Keep in mind the principles represented by the acronym SOLID.

Unit Test All Logic

This includes a single if statement, loop, stream, lambda, switch, etc.

Avoid Integration Testing

Do not place logic in Liferay classes that need the Liferay environment to be tested. Move any logic to a separate class.

Use Dependency Inversion/Injection

In practice we use a manual version of Dependency Injection. Liferay based classes are used to instantiate all needed objects, without introducing logic. Then create a separate class to which you will pass all the needed objects to the constructor.

Validate All Constructor Parameters

All constructor parameters should be checked for nulls except where nulls are desireable. Other acceptable values should be checked for. In all cases throw and IllegalArgumentException where values do not meet expectations. Pass a string argument to the exception providing additional information, when needed. Liferay code cannot be relied upon to guarantee non-null values. Using the Optional class is the desired pattern. Example below.

Check constructor parameters
public class MyClass {
  private static final Log LOG = LogFactoryUtil.getLog(MyClass.class);

  private MyLambda<String, Connection> myLambda;

  public MyClass(myLambda<String, Connection> myLambda) {
  	this.myLambda=Optional.ofNullable(myLambda).orElseThrow(IllegalArgumentException::new);
    this.myLambda=Optional.ofNullable(myLambda)
  				.orElseThrow(() -> new IllegalArgumentException("Lambda cannot be null"));
  }
}

Favor Streams over Loops

When using lists, streams are to be favored over for loops or while loops.

Stream examples
myList.stream().filter(Objects::nonNull).collect(Collectors.toList());
layouts.stream().filter(Objects::nonNull).mapToLong(Layout::getPlid).forEach(this::updateLayout);
String[] oldPortletIdsArray = feedKeysMap.keySet().stream().toArray(String[]::new);
Optional<DDMStructure> fieldStructure = ddmStructures.stream().filter(Objects::nonNull)
							.filter(structure -> structure.hasField(fieldName)).findFirst();
entity.getMetaDataFields().stream()
				.filter(this::isAllowedField)
				.sorted((field1, field2) -> field1.getLabel().compareToIgnoreCase(field2.getLabel()))
				.map(this::getMetaFieldJSON)
				.collect(JSONArray::new, JSONArray::put, JSONArray::put);

Code Relationships Should Look Like a Star

If you favor composition over inheritance then your code should look like a star when diagramed. It shouldn’t look like a hiking trail or highway.

Good Diagram
Bad Diagram

Less Is More ~ Older posts are available in the archive.