This package contains various helpers and common utilities or base classes. Browse the classes and check Javadoc for all details.   

MString

Holds multi-language strings in the db. Use MString instead of String where you have multilingual content editable in the backend.

There are two types of validation constraints available for MString properties:

  • @MString.MStringRequired - requires that this field should not be empty on all language tabs
  • @MString.MStringRequiredForVisibleTab - requires that this field should not be empty on language tabs where visible checkbox (Sichtbar) is set to true. This annotation can be used only inside classes that extends PageBlock, as long as only it has "visible" flag.

See ContentBlock.java and contentBlockEdit.scala.html:

 
Entity class:

@OneToOne (cascade=CascadeType.ALL)
@MString.MStringRequired(message = "cms.error.content.required")
private MString content = new MString();

Template:

@mstringForm(action = routes.BlockController.update(data.getId().toString(), backURL), 'class -> "form-horizontal") {
    @text(
        editForm,
        "title",
        "Titel",
        "",
        (a: String) => a
    )
    @mstringTextarea(
        data.getContent,
        editForm,
        "content",
        "Content",
        ""
    )

 

SmartForm

We have created a special class for our Forms: SmartForm. This class extends the normal form, but is much mightier.
Basically, the usage is the same:
- Create a form using SmartForm.form
- bind the form using fill()
- bind the request data using bindFromRequest
- check for errors
- and so on
There are 2 main advantages in this implementation:

  • The model you get with form.get() is the same that you bind with fill(), this means, you get an object that is bound to the entitymanger., means, you can call save directly on that without having problems with duplicate ids.
  • The other advantage is, it's compatible with MString and any class you want to persist.

BlockController.java:

    AbstractBlock targetBlock = AbstractBlock.find.byId(id);
    if (targetBlock == null) {
        return internalServerError("Block not found: " + id);
    }
    Form<AbstractBlock> boundForm = (Form<AbstractBlock>) SmartForm.form(targetBlock.getClass());
    boundForm = boundForm.fill(targetBlock);
    boundForm = boundForm.bindFromRequest();
    if (boundForm.hasErrors()) {
        flash("error", "Please correct the errors below");
        Logger.warn("Form errors found: " + boundForm.errors());
        return badRequest(targetBlock.editForm(boundForm));
    }
    boundForm.get().save();
    return redirect(backURL);

Note: Consider using the SecureForm subclass, which also prevents form parameter tampering (see below).

Data binding

Allows to use custom types in form data binding. You need to create and register your own databinder. Example:

DataBinding.registerDataBinder(MString.class, new MStringDataBinder());

After that, SmartForm will know how to convert MString to Form Data and vice-versa.

Example: MString is @OneToOne. MString is a bit special, it has a map of <string,string> in it, which is the language key and the translation string. We pass the values like this:
?content.de=german&content.en=english&content.id=<entity id>

Play forms can't handle that, so we wrote an adapter. This class converts from Map<String,String> to MString and vice versa
and also does validation.

Example: MStringDataBinder.java

Your databinder implement this interface:

    public interface DataBinder {
    public Object getFromData(String fieldName, Object instance, Map<String,String> data);
    public Map<String,String> toData(String fieldName, Object t);
    public Map<String,List<ValidationError>> validate(String fieldName, Object object);
}

QueryStream

The play-cms contains the feature provides a conversion between JDBC native result set and Java streams. It keeps an access to database record but it does it lazily so you can easily manage big amount of data without "OutOfMemory" limits and in functional style.

The available methods presented in QueryStream.java file. It looks like:

package ch.insign.commons.db;

import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * Add simple support for Java 8 Streams to JPA.
 *
 * @see {https://blogs.oracle.com/theaquarium/entry/jpa_and_java_se_8}
 */
public interface QueryStream<T> {

    /**
     * Return a stream of read-only objects.
     * <p>
     * The query will be executed against the Session,
     * and the resulting objects will not be tracked for changes.
     * The resulting objects are from the Session shared cache,
     * and must not be modified.
     * <p>
     * The stream should be closed to release resources.
     */
    Stream<T> getResultStream();

    /**
     * Applies a result stream to the given function and automatically releases resources.
     */
    <R> Optional<R> withResultStream(Function<Stream<T>, R> function);

    /**
     * Bind an argument to a named query parameter.
     */
    QueryStream<T> setParameter(String name, Object value);

}


To use it into your project you must inject the QueryStreamFactory interface which provides an initialization of QueryStream object, into your class using guice. 

Example:

/* your other imports */
import ch.insign.commons.db.QueryStreamFactory;

class ApplicationController extends Controller {

    private QueryStreamFactory queryStreamFactory;

    @Inject
    public ApplicationController(FormFactory formFactory, JPAApi jpaApi, QueryStreamFactory queryStreamFactory) {
        ...
        this.queryStreamFactory = queryStreamFactory;
    }

    /* Some other content */

    public Stream<User> getAllAdults() {
        return queryStreamFactory.create("SELECT u FROM User u WHERE u.age >= 30").getResultStream();
    }

}

You can create the QueryStream with three variants described as QueryStreamFactory API. It looks like:

package ch.insign.commons.db;

import javax.persistence.Query;

public interface QueryStreamFactory {

    <T> QueryStream<T> create(Query query, Class<T> resultClass);

    <T> QueryStream<T> create(String jpql, Class<T> resultClass);

    <T> QueryStream<T> createNamed(String name, Class<T> resultClass);

}

Advanced topic of QueryStream.

The default implementation based on cross-JPA-vendor limit/offset approach, but you can easily override it with your own implementation. For example, if you use EclipseLink vendor, you can do the next stuff:

1. Implement QueryStream interface:

/**
 * The QueryStream implementation using EclipseLink's ScrollableCursor.
 *
 * @see {http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination#Using_a_ScrollableCursor}
 * @param <T>
 */
public class EclipseLinkQueryStream<T> implements QueryStream<T> {
    private final static Logger logger = LoggerFactory.getLogger(EclipseLinkQueryStream.class);

    private final Query query;
    private final Class<T> type;
    private final Map<String, Object> parameters = new HashMap<>();

    // Default fetch size as a benchmark suggested in
    // http://makejavafaster.blogspot.com/2015/06/jdbc-fetch-size-performance.html
    private int fetchSize = 2000;

    EclipseLinkQueryStream(Query query, Class<T> type) {
        this.query = query;
        this.type = type;
    }

    @Override
    public Stream<T> getResultStream() {
        parameters.forEach(query::setParameter);

        Optional.ofNullable(fetchSize).ifPresent(size ->
                query.setHint(QueryHints.JDBC_FETCH_SIZE, size));

        query.setHint(QueryHints.READ_ONLY, HintValues.TRUE);
        query.setHint(QueryHints.SCROLLABLE_CURSOR, HintValues.TRUE);

        ScrollableCursor cursor = (ScrollableCursor)query.getSingleResult();

        return StreamSupport.stream(toSplitIterator(cursor, type), false)
                .onClose(cursor::close);
    }

    @Override
    public <R> Optional<R> withResultStream(Function<Stream<T>, R> function) {
        try(Stream<T> s = getResultStream()) {
            return Optional.ofNullable(function.apply(s));
        }
    }

    @Override
    public QueryStream<T> setParameter(String name, Object value) {
        parameters.put(name, value);
        return this;
    }

    public QueryStream<T> setFetchSize(int fetchSize) {
        this.fetchSize = fetchSize;
        return this;
    }


    private Spliterator<T> toSplitIterator(ScrollableCursor cursor, Class<T> type){
        return Spliterators.spliteratorUnknownSize(
                new ScrollableResultIterator<>(cursor, type),
                Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.CONCURRENT | Spliterator.IMMUTABLE
        );
    }

    private static class ScrollableResultIterator<T> implements Iterator<T> {

        private final ScrollableCursor cursor;
        private final Class<T> type;

        ScrollableResultIterator(ScrollableCursor cursor, Class<T> type) {
            this.cursor = cursor;
            this.type = type;
        }

        @Override
        public boolean hasNext() {
            return cursor.hasNext();
        }

        @Override
        public T next() {
            return type.cast(cursor.next());
        }
    }
}

2. Implement QueryStreamFactory interface:

public class EclipseLinkQueryStreamFactory implements QueryStreamFactory {
    private JPAApi jpaApi;

    @Inject
    private EclipseLinkQueryStreamFactory(JPAApi jpaApi) {
        this.jpaApi = jpaApi;
    }

    @Override
    public <T> QueryStream<T> create(Query query, Class<T> resultClass) {
        return new EclipseLinkQueryStream<>(query, resultClass);
    }

    @Override
    public <T> QueryStream<T> create(String jpql, Class<T> resultClass) {
        return new EclipseLinkQueryStream<>(jpaApi.em().createQuery(jpql, resultClass), resultClass);
    }

    @Override
    public <T> QueryStream<T> createNamed(String name, Class<T> resultClass) {
        return new EclipseLinkQueryStream<>(jpaApi.em().createNamedQuery(name, resultClass), resultClass);
    }
}

3. Override binding using Guice application loader:

public class YourApplicationLoader extends GuiceApplicationLoader {
    @Override
    public GuiceApplicationBuilder builder(Context context) {
        return super.builder(context)
                .overrides(
                       //your other bindings
                           bind(QueryStreamFactory.class).to(EclipseLinkQueryStreamFactory.class)
                );
    }
}

The usage of overrided context is the same to above.

Paginate

TODO

Configuration

Config values in .conf files are a source of runtime errors if not found, wrong type etc. To avoid such problems (and to make using config values much more intuitive for the developer), use the Configuration base class:

  1. Create a subclass and define your config namespace
  2. Add getters for all your config values
  3. Add a call to every getter to test()
  4. Create a config instance early, ideally on app startup - this will trigger the test()

Example:

import ch.insign.commons.util.Configuration;
 
/**
 * Managed CMS config values from application.conf
 */
public class CmsConfiguration extends Configuration {
    
@Override
    protected String configNamespace() {
        return "cms";
    }
  
    @Override
    protected void test() {
        deleteWithoutTrashBin();
        isResetRouteEnabled();
        defaultUrlPrefix();
    }
  
    public boolean deleteWithoutTrashBin() {
        return getConfig().getBoolean("deleteWithoutTrashBin");
    }
 
    public boolean isResetRouteEnabled() {
        return getConfig().getBoolean("enableResetRoute");
    }
  
    /**
     * The url to use if no virtual path was set for a page.
     * Default is /page/<id>
     */
    public String defaultUrlPrefix() {
        return getConfig().getString("defaultUrlPrefix");
    }

 

The corresponding entries in the application.conf file:

# CMS config values.
# Always access these using Cms.getConfig().*
cms.deleteWithoutTrashBin = false
cms.enableResetRoute = false
cms.defaultUrlPrefix = /pages/

Overriding default values:

If you work on a module, you can define your default settings in the module's application.conf, and the consumer of your module can override them individually in the main project's application.conf

 

SecureForm

When binding form data to a backend model class / entity directly using Play's .bindFromRequest() method, parameter tampering attacks are possible: The attacker can simply post any field the entity contains, e.g. isAdmin, along with the form and it would be saved.

There are basically 3 solutions to this:

  1. Use form class for each form, and then map / transfer the data to the model class manually.
  2. Define allowedFields varargs in bindFromRequest("field1", "field2") (fields not listed are silently ignored)
  3. Use SecureForm to automatically sign the form fields.

SecureForm usage:

 

1. Add the @formKey to your form

@()
@import ch.insign.commons.helper.html._
 
<form>
@formKey()
...

This will simply add a hidden field to your form that will hold the form signature.

 

2. Sign the html templates before sending the form to the client:

public static void editForm() {
    return ok(SecureForm.signForms(myForm.render()));
}

The generated hidden field looks e.g. like this:

<input type="hidden" name="_formsignature" value="rO0ABXNyABNqYXZhLnV0aWwuQXJyYXlMaXN0eIHSHXlGcm9tdAAJZGlzcGxheVRvdAAIdGl0bGUuZGV0AAh0aXRsZS5mc
nQACHRpdGxlLmlkeA==@rO0ABXQALnXvv71oGy5tLl5mJe+/vQJt77+9WE4E1oXvv70c77+977+9A1JKCVvvv73vv70=" />

 

The first part contains the serialized field list, the second part contains a SHA256 hash signature of the first part (salted with the application.secret conf value).

 

3. Validate form integrity when receiving the form submission (before / when binding)

MyController.java:

// Validate the integrity of the submitted form fields: Either by signed formKey or by passed allowedFields.
// Note: If using SmartForm, the security check is already done inside .bind(), no coding needed.
try {
    SecureForm.validateSubmittedForm(data, allowedFields, object);
  
} catch (InvalidKeyException e) {
    // We're strict about tampered submissions.
    throw new RuntimeException(e);
}
 
// Now you can bind as normal. Normal value validation happens here.
form.bindFromRequest();

 

Note:

  • If you use SecureForm (or SmartForm) instead of Play's Form, then it's automatically done in all bind() methods.
  • If you use the play cms, then it's automatically used in all block add/edit forms

 

Automatic field list handling

Field lists with brackets are adjusted automatically to allow for indexes, e.g.: <input name="field[]"> in the form sent to the client allows field[0], field[1] etc. to be submitted.

 

Wildcards for dynamic forms

In cases where you modify the form on the client-side, e.g. by JS, you can use the * wildcard:

@formKey("myDynAddedField, myJSFieldX*, myWhat*Field")

You can also define your additional allowed fields in a separate hidden field (e.g. if you're using a extended template which already contains @formKey, like the cms block edit templates):

@formKeyAdditional("myDynAddedField, myJSFieldX*, myWhat*Field")

 

TaskQueue

The TaskQueue helps with queueing persistent tasks, such as asynchroneous webservice calls where the recipient is not guaranteed to be available permanently.

Creating a Task

Tasks extend Task and are a Model subclass, so they're db-persisted.

@Entity
@Table(name = "task_address_change")
@DiscriminatorValue("AddressChangeTask")
public class AddressChangeTask extends Task {
    @Override
    public boolean execute() {
        try {
                 
            // Do your stuff
            // Return true if it succeeds.
            addLogEntry("Success!);
            return true;
             
        } catch (Exception e) {
             
            // Returning false will requeue the task until .retry() returns false.
            addLogEntry("Failed: " + e.getMessage(), Level.ERROR, e);
            return false;
        }
    }
    @Override
    public void failed() { 
        addLogEntry("Request failed permanently: " + this, Level.ERROR);
    }
}

 

Other Task methods / events: 

  • validate() - override to implement validation (validation is checked on queue().add())
  • retry() - decides whether a failed task should be requeued again.
  • onEnqueue() - called when the task was added to the queue initially
  • onRequeue() - called when the task execution failed and the task was again enqueued

Hint: If you want to create your own abstract task superclass, annotate it with @MappedSuperclass.

Create and enqueue a task instance:

// Create, configure and queue a task
AddressOrderChangeTask task = new AddressOrderChangeTask();
task.setNewAddress(address);
task.setMaxTries(5);
setRetryWaitPeriod(15*60);
SoapTransmissionActor.queue().add(task);

 

Creating and running the TaskQueue

The queue is run by an Akka actor which call the queue's process() method periodically.

Example Akka actor:

public class SoapTransmissionActor extends UntypedActor{
 
    private final static TaskQueue queue = new TaskQueue("SoapQueue");
     
    public static TaskQueue queue() {
        return queue;
    }
    // This needs to be called from
    public static void start() {
 
        // Re-add any persisted waiting messages and start processing queue.
        queue.load().start();
 
        // Create the actor
        Akka.system().scheduler().schedule(
            Duration.create(Configuration.getOrElse("akka.actor.SoapTransmissionActor.delay", 5), TimeUnit.SECONDS),
            Duration.create(Configuration.getOrElse("akka.actor.SoapTransmissionActor.interval", 2), TimeUnit.SECONDS),
            Akka.system().actorOf(Props.create(SoapTransmissionActor.class)),
            "Send Soap Requests to SAP",
            Akka.system().dispatcher(),
            null
        );
    }
    @Override
    public void onReceive(Object message) throws Exception {
        if (isSapReachable()){
            queue.process();
        } else {
            Logger.warn("SAP is not reachable! Current soap queue size: " + queue.size());
        }
        checkQueueSize();
    }
    private void checkQueueSize() {
        if (queue.size() > 300) {
            Logger.error("SoapTransmission queue size too big: " + queue.size());
        } else if(queue.size() > 100) {
            Logger.warn("SoapTransmission queue size: " + queue.size());
        }
    }

Usually, the queue is started at app startup, in Global.java's onStart():

if(Configuration.getOrElse("akka.actor.SoapTransmissionActor.enabled", false)) {
    SoapTransmissionActor.start();
}

 

Note: This example uses one actor. If you need parallel execution of tasks, you can just create more actors that .process() tasks, TaskQueue is thread-safe.

 

The task queue inspection backend

There is a readibly usable Task Queue log viewer UI in the play-cms package.

Notes:

  • Path: /admin/cms/taskqueue
  • Logging: Make sure you add enough meaningful log information at every step using .addLogEntry(). If you add a severity (e.g. Level.WARNING), it is also sent to the logger.
  • Custom task detail view template snippet: You can add a custom template partial by overriding reportTemplatePartial() (make sure you concatenate the superclass' output)

Cache

Wrapper over the Ehcache. This class gives more control over the cache rather than Play Framework Cache API. Cache class provides more powerful way to interact with Ehcache including ability to use custom objects as keys, easely access to underlying ehchace to use features such as search. Here is few examples of usage of the play-cms Cache:

// Recieve value from cache
cache.cached(
        OBJECT_CACHE,                       // Cache name
        obj.getComplexId(),                 // Entry key
        () -> generateCachebleEntry(obj));  // If value not present put new one

// Get net.sf.ehcache.Cache
cache.underlying(OBJECT_CACHE);

// Check if cache already contains entry
cache.isCached(OBJECT_CACHE, obj.getComplexId());

Cache object could be created with one of the constructors:

  • Cache(String name, URL file)
  • Cache(String name, Configuration configuration, CacheConfiguration cacheConfiguration)

These constructors covers two main ways of creation cache - by reading configuration file and by passing configuration objects.

We recommend to use providers in ordere to create the Cache objects. This would allow to keep your code clean. Example provider is listed below:

public class CustomCacheProvider implements Provider<Cache> {

    public static final String MANGER_NAME = "customCacheManager";
    private static final String CONFIG_FILE_PATH = "custom.ehcache.xml";

    private final Cache cache;

    @Inject
    public CustomCacheProvider(Environment environment) {
        this.cache = new Cache(MANGER_NAME, environment.classLoader().getResource(CONFIG_FILE_PATH));
    }

    @Override
    public Cache get() {
        return cache;
    }
}

public class CacheProviderModule extends Module {

    public final static String CUSTOM_CACHE = "customCache";

    @Override
    public Seq<Binding<?>> bindings(Environment env, Configuration configuration) {
        return seq(bind(Cache.class).qualifiedWith(CUSTOM_CACHE).toProvider(CustomCacheProvider.class));
    }
}


// Cache could be injected now with
@Named(CacheProviderModule.CUSTOM_CACHE) Cache customCache;