Make Grails Application Readonly

For a customer project there was the requirement to make certain parts of the application readonly depending on the state of a central domain object, which I’ll call Lock. Long story short:

  • an implementation of AbstractPersistenceEventListener intercepts persistence events and throws a custom exception if changes are forbidden
  • because the event listener is not able to access database state (because it’s triggered halfway through hibernate doing its saving), the necessary data is preloaded in a request filter
  • a custom exception page for the specific exception displays a user-friendly message using information delivered by the exception object

The main part is the src/groovy/LockCheckPersistenceListener.groovy that intercepts persistence events.
There’s one catch – it only kicks in on domain object operations. If you’re using executeUpdate statements, you’ll have to check these in a different way (if there’s not many of them, perform checks at the individual places).

/**
 * For details, see http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping
 */
class LockCheckPersistenceListener extends AbstractPersistenceEventListener  {
 
    public LockCheckPersistenceListener(final Datastore datastore) {
        super(datastore)
    }
    @Override
    protected void onPersistenceEvent(final AbstractPersistenceEvent event) {
        if (!(event.entityObject instanceof Lock)) {
            switch(event.eventType) {
                case EventType.PreInsert:
                case EventType.PreUpdate:
                case EventType.PreDelete:
                    performReadonlyCheck();
                break;
            }
        }
    }
 
    private performReadonlyCheck() {
        //you'd better not perform database access where - you'll get Hibernate's StaleObjectException and
        //'org.hibernate.AssertionFailure null id in XYZ' when accessing the database in any way
        def lock = RequestContextHolder.currentRequestAttributes().getAttribute("activeLock", RequestAttributes.SCOPE_REQUEST);
        if (lock.islocked) {
            throw new LockException("lock.locked")
        }
    }
 
    @Override
    public boolean supportsEventType(Class

The persistence listener needs to be registered in Bootstrap.groovy’s init closure as follows

grailsApplication.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
            grailsApplication.mainContext.addApplicationListener new LockCheckPersistenceListener(datastore)
        }

Within the PersistenceListener, database access is not advisable as it leads to various Hibernate exception. Because the information about the lock needs to be retrieved database nevertheless, the relevant domain object is pre-loaded in a Grails filter that stores the object in request scope for later use.

The filter goes to grails-app/conf/lock/LockFilters.groovy

class LockFilters {
    def filters = {
        all(controller:'*', action:'*') {
            before = {
                //Store the active lock for later use (needed by LockCheckPersistenceListener which can't perform db access from within a hibernate listener
                RequestContextHolder.currentRequestAttributes().setAttribute("activeLock", Lock.findByLocked(true), RequestAttributes.SCOPE_REQUEST)
            }
        }
    }
}

In UrlMappings.groovy, you register a custom exception page for your exception so you can display an appropriate message.

"500"(controller: 'lock', action: 'lockedException', exception: LockException)

The controller action for the exception page is trivial (an empty closure).
The only noteworthy part of the custom exception gsp page is the retrieval of the exception, where in this case, the custom exception has a reason property that is used for a i18n message key.

(...snip..)
<h3>${message(code: request.exception.cause.reason)}</h3>
(...snip...)

This way, you get a neat page that is shown whenever a user tries to change anything while he isn’t allowed to.

This entry was posted in Software. Bookmark the permalink.