Showing posts with label Grails2. Show all posts
Showing posts with label Grails2. Show all posts

Sunday, July 15, 2018

Add Custom Scope to a Grails 2 Application . . .

Grails services are Spring managed singleton beans by default. Singleton is one of the five different scopes (singleton, prototype, request, session, and globalSession) that Spring framework offers for managed beans. Prior to Spring 2.5, there were only 2 standard scopes: singleton and prototype. Spring 2.5 added 3 additional scopes: requestsession and globalSession for use in web-based applications. Grails adds two more scopes to the mix: flow and flash.

Sometimes, you might run into a situation that none of these scopes meet your requirements. For instance, when you have a multi-tenant or multi-client application, what you may need is a client scope, a separate bean instance for each client. Recently, I ran into this situation. Addressing an existing performance issue drove me into this situation, making an use case for a custom scope.

We have a Grails 2.5.4 multi-client application with clientId included in URL mappings for end-points like: /clients/$clientId/resource. There has been a performance issue with one of the end-points backed by a service. The service has a heavy-weight method which builds and caches client data once for each client with an expiration time set to expire cache few hours once built. It takes few minutes to build this data due to it's nature and some data rule complexities. Once built and cached, it rebuilds data from the cache really fast. One of the performance improvements identified upfront was to limit concurrent calls to that method. Obviously, there is no point in allowing concurrent builds for the same client. The solution to put in place was to allow one-and-only-one concurrent call for any given client, but allow concurrent calls for different clients one per each client. Making the method synchronized is an easy way to limit concurrent calls, but it only solves half of the problem. As service is a singleton bean and synchronized method uses this as the object lock. With that, concurrent calls get executed serially, across all clients. But, we need to allow concurrent executions for different clients, but not for the same client. It is still possible to achieve this with synchronized method, but only when there is one service instance per client. This opened the need for a custom scope. Since Spring 2.0, the concept of scoping beans is made extensible and is the way to add custom scope.

Spring maintains a cache of all scoped beans in it's container. Every scope except prototype has it's own in-memory cache of bean instances which is initialized & populated when the application gets started and maintained by spring container. All spring managed beans either get instantiated or get proxies created during the application startup. Obviously, prototype scoped beans don't need any kind of cache as they get created and injected into all beans that are auto-wired with Singleton beans. A Grails service being singleton bean is also stateless and hence it's methods can be executed by multiple concurrent threads.

Spring has a good documentation of all these details and the API is well-documented as well. For creating a custom scope, all we need to do is: 1) Create a custom Scope object by implementing org.springframework.beans.factory.config.Scope interface and 2) Register custom scope with Spring container. 3) Scope required beans at this custom scope. Sounds simple, in a Grails application it should even be simpler. Lets get through step-by-step implementation details of adding a new custom scope: Client scope to a Grails 2 application.

Environment: Grails 2.5.4, Spring 4.1.8, Java 8 on MacOS High Sierra 10.13.5

Step-1 Implement custom Scope

This is straight forward. Just implement the interface and provide implementation for all essential methods. Remember, the implementation should also maintain it's own cache for this custom scoped beans. Also, make sure that any needed scoped context (in this case, it is clientId) is available and accessible in this implementation when a reference to the bean scoped at this custom scope is needed.

src/groovy/com/giri/grails/scope/ClientScope.groovy
package com.giri.grails.scope import grails.plugin.springsecurity.SpringSecurityService import grails.util.Holders import groovy.util.logging.Log4j import org.springframework.beans.factory.ObjectFactory import org.springframework.beans.factory.config.Scope /** * Custom scope bean for client-scoped services registered in resources.groovy. * * All services(Spring beans) that need client-scope should define static property of scope like: * static scope = ClientScope.SCOPE_NAME * * @see resources.groovy * * @author gpottepalem * Created on July 15, 2018 */ @Log4j class ClientScope implements Scope { static final String SCOPE_NAME = 'clientScope' /** * Client scoped bean store. * A synchronized multi-thread-safe map of various beans scoped with {@link ClientScope#SCOPE_NAME} * Spring framework depends on this store for maintaining beans defined with this custom client-scope. * e.g. Two clients with one common service and two different services * [ client-1 : [ * 'ClientService' : clientServiceObjectRef, * 'OtherService' : otherServiceObjectRef * ], * client-2 : [ * 'ClientService' : clientServiceObjectRef, * 'SomeOtherService' : someOtherServiceObjectRef * ] * ] */ private Map<Integer, Map<String, Object<?> clientScopedBeansMap = [:].asSynchronized() /** * Helper method, returns client-scoped beans for a given client. * @param clientId the client id * @return A map of client-scoped beans for the given client. */ private Map<String Object> getClientScopedBeans(Integer clientId) { if(!clientScopedBeansMap[clientId]) { clientScopedBeansMap[clientId] = [:].asSynchronized() log.debug "No client scoped bean found for client:$clientId, just created new map" } return clientScopedBeansMap[clientId] } /** * Helper method, returns clientId taking it from authenticated user. * @return clientId of the current user logged in */ private Integer getClientId() { (Holders.grailsApplication.mainContext.getBean('springSecurityService') as SpringSecurityService).authentication?.clientId } @Override Object get(String name, ObjectFactory<?> objectFactory) { synchronized (this) { Integer clientId = getClientId() Map<String Object> clientScopedBeans = getClientScopedBeans(clientId) if (!clientScopedBeans[name]) { clientScopedBeans[name] = objectFactory.object log.debug "Added new instance: ${clientScopedBeans[name]} for bean: $name for client:$clientId to the bean store" } return clientScopedBeans[name] } } @Override Object remove(String name) { Map<String Object> scopedBeanMap = getClientScopedBeans(getClientId()) return scopedBeanMap.remove(name) } @Override void registerDestructionCallback(String s, Runnable runnable) { // nothing to register } @Override Object resolveContextualObject(String s) { return null } @Override String getConversationId() { return SCOPE_NAME } }

Step-2 Register custom scope

Register custom scope in:
grails-app/conf/spring/resources.groovy
import com.giri.grails.scope.ClientScope import org.springframework.beans.factory.config.CustomScopeConfigurer beans = { ... // Custom scope: per-client clientScope(ClientScope) // register all custom scopes customScopeConfigurer(CustomScopeConfigurer) { scopes = [(ClientScope.SCOPE_NAME) : ref('clientScope')].asImmutable() } ... }

Step-3 Scope a client-specific service with custom scope

Say, we have a service ClientService that we need to scope at clientScope. Just define a static scope property set with this custom scope like:
grails-app/services/com/giri/ClientService.groovy
package com.giri import com.giri.grails.scope.ClientScope class ClientService { static scope = ClientScope.SCOPE_NAME def clientDataBuilderService //DI //delegates to clientDataBuilderService synchronized Map buildClientData(String clientId) { clientDataBuilderService.buildData(clientId) } ... }

Step-4 Custom-scoped service - Dependency Injection

Grails supports Spring Dependency Injection by convention. A property name that matches the class name of a Spring managed bean gets injected automatically. Unlike Spring applications, you don't need @Autowired annotation on a property or a setter method. But for custom scoped beans, the actual custom scoped bean might get instantiated lazily when the client context (in this case, clientId) is available in the application (either taken from the request, session, security authentication etc.). This context is not known at the start of the application. So, without creating proxies, dependency injection may not be possible. This requires the scoped bean to be programmatically resolved, unlike auto-wired by Grails convention. The following is a way to get a handle to the scoped bean instance. The assumption here is, that the context needed for creating a scoped bean for specific client (clientId) is available in the security context and all end-points are secured.

grails-app/controllers/com/giri/ClientController.groovy
package com.giri import grails.converters.JSON import grails.util.Holders class ClientController { ... def index(String clientId) { ClientService clientService = Holders.grailsApplication.mainContext.getBean('clientService', ClientService.class) clientService.buildClientData(clientId) as JSON } ... }

TIP: Unit Testing

Without scoped bean dependency injected by following Grails naming convention for DI and by referring it using Grails ApplicationContext puts us into a limitation in unit tests. The actual scoped bean instance is needed which can otherwise be mocked if it was injected. Typically, Grails doesn't load bean definitions as the complete ApplicationContext is not needed in unit-tests. The following are two options that Grails offers to get away with this and have bean definitions loaded and ApplicationContext available for unit-tests:

Option-1
test/unit/com/giri/ClientControllerSpec.groovy
package com.giri import grails.test.mixin.TestFor import spock.lang.Specification @TestFor(ClientController) class ClientControllerSpec extends Specification { static loadExternalBeans = true //loads beans defined in resources.groovy and beans are available in applicationConext ClientService clientService def setup() { //controller.clientService = Mock(ClientService) //doesn't work clientService = applicationContext.getBean('clientService') clientService.clientDataBuilderService = Mock(ClientDataBuilderService) } void "test index"() { when: controller.index('client-1') then: (1.._) * clientService.clientDataBuilderService.buildData('client-1') >> ['abcd' : 1234] and: response.json == [ "abcd": 1234 ] } }

Option-2
package com.giri import grails.test.mixin.TestFor import spock.lang.Specification @TestFor(ClientController) class ClientControllerSpec extends Specification { ClientService clientService def setup() { //controller.clientService = Mock(ClientService) //doesn't work //define bean to get into applicationContext as it is not injected to mock it out defineBeans { clientService(ClientService) } clientService = applicationContext.getBean('clientService') clientService.clientDataBuilderService = Mock(ClientDataBuilderService) } void "test index"() { when: controller.index('client-1') then: (1.._) * clientService.clientDataBuilderService.buildData('client-1') >> ['abcd' : 1234] and: response.json == [ "abcd": 1234 ] } }

Summary

To add custom scope to a Grails Application, all you need to know is some Spring Framework details and Grails integration with Spring. There is still one improvement that can be made to this solution, getting scoped beans injected by following Grails convention. This particular use-case requires client context (clientId) to be available for scoped bean cache maintenance. This makes the case for a proxy to be generated for this custom-scoped beans. A proxy bean needs to be generated and injected for scoped bean at the start of application. The proxy bean should be able to retrieve the actual target bean from the scoped cache and delegate method calls to that target object. This might simply need some additional Spring configurations, I guess. I left it out for now, to explore later.

References

Wednesday, June 27, 2018

Log exceptions with exceptional details . . .

Logging is generally considered a cross-cutting concern. So as logging exceptions is. An appropriate level of details added to exception log is always very helpful and gives a jump-start in investigating the root cause of an exception.

Grails provides good exception logging, filters out unnecessary stack traces (configurable by setting grails.full.stacktrace property) by default, that otherwise would be too long. It also provides configurable way of adding additional details to the exception log message. Additional details like request parameters can be added to exception log message (by setting the property  grails.exceptionresolver.logRequestParameters = true in Config.groovy) which is by default enabled in only development env). Also, certain sensible request parameters and values can be masked from exception logging by setting an additional property grails.exceptionresolver.params.exclude = ['password']).

In a Web or RESTful API application, request parameters added to exception log message cover all http methods like GET that send parameters as part of the query string. However, http methods like POST typically send parameters in the request body, but not in the query string. Logging request body along with exception message is as useful as logging request parameters. Grails doesn't provide a configurable way of adding this level of details to exception log message. But this can easily be added with minimal coding, configuration and by leveraging the underlying Spring framework.

Environment: Grails 2.5.4, Java 8 on MacOS High Sierra 10.13.5

Imagine, a Grails application providing various RESTful end-points that take JSON payload as the body in POST requests. If some exception arises while processing a request, an exception log that includes request payload received will be very helpful to investigate the root cause. This requires accessing the original http request body in an exception resolver at the time when the exception is raised. Grails default exception resolver class is: GrailsExceptionResolver. This class is extendable. All we need to do is override one of it's methods that forms the exception log message.

Extend GrailsExceptionResolver and override getRequestLogMessage(Throwable e, HttpServletRequest request). This method already has HttpServletRequest parameter. So, getting access to request body is possible, but it is not available. The problem is, by this time, the request body must have already been read either by calling getInputStream() or getReader() method in order to consume and process the JSON payload. Once read, it's not available anymore and subsequent reads only result in an exception: IllegalStateException. The only way to deal with this limitation is to wrap the original HttpServletRequest into a wrapper HttpServletRequestWrapper, cache it and pass it along the filter chain by making cached request available for multi-reads.

This is bit low-level to dive into in a Grails application. Grails 2 offers a high-level Filters support but it has limitations to use in this solution. So, we have to dive little deeper and put a solution by introducing a Servlet filter that caches and wraps the original request to make it available for multi-reads. But, remember Grails underpins Spring framework. So, it's always a layer to look into for customizations like this and see if there is anything readily available for adopting. In this case, there is a filter org.springframework.web.filter.CommonsRequestLoggingFilter which wraps the request into  org.springframework.web.util.ContentCachingRequestWrapper and is just appropriate for this solution to leverage.

Now, we have all pieces of the puzzle. Let's put these together into a solution.

Step-1

Extend GrailsExceptionResolver and overwrite getRequestLogMessage(Throwable e, HttpServletRequest request)method. The following is an example of extended exception resolver:
src/groovy/grails/logging/LogRequestBodyExceptionResolver.groovy
package com.giri.grails.logging import com.giri.grails.web.RequestCacheFilterUtil import org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver import org.springframework.http.HttpMethod import org.springframework.http.MediaType import javax.servlet.http.HttpServletRequest /** * Exception resolver, adds request body to exception log message by getting it from the request body cached by * {@link org.springframework.web.util.ContentCachingRequestWrapper} * * @see {@link org.springframework.web.util.ContentCachingRequestWrapper} * @see /src/templates/war/web.xml * @see /conf/spring/resources.groovy * * @author gpottepalem * Created on Jun 27, 2018 */ class LogRequestBodyExceptionResolver extends GrailsExceptionResolver { static final List LOG_PAYLOAD_FOR_HTTP_METHODS = [HttpMethod.POST, HttpMethod.PUT]*.name() static final List LOG_PAYLOAD_FOR_HTTP_CONTENT_TYPES = [MediaType.APPLICATION_JSON_VALUE] /** * Enhances Grails log message for logging exceptions by adding the original request payload to the exception * message * @param e the exception * @param request request * @return enhanced log message that includes request payload (body) */ @Override String getRequestLogMessage(Throwable e, HttpServletRequest request) { String logMessage = super.getRequestLogMessage(e, request) String payload = RequestCacheFilterUtil.getCachedRequestPayload(request) if (request.method in LOG_PAYLOAD_FOR_HTTP_METHODS && request.contentType in LOG_PAYLOAD_FOR_HTTP_CONTENT_TYPES) { logMessage += "\n${request.method} ${request.contentType} payload: ${payload}\n" } return logMessage } }

Here is the utility class that the above code snippet uses:
src/groovy/grails/web/RequestCacheFilterUtil.groovy
package com.giri.grails.web import org.springframework.web.util.ContentCachingRequestWrapper import org.springframework.web.util.WebUtils import javax.servlet.http.HttpServletRequest /** * Utility class. Provide methods for working on the cached request payload. The original request once per * request is cached by {@link ContentCachingRequestWrapper} in the filter * {@link org.springframework.web.filter.CommonsRequestLoggingFilter} setup in web.xml * * @see /src/templates/war/web.xml * * @author gpottepalem * Created on Jun 27, 2018 */ class RequestCacheFilterUtil { /** * Utility method to get payload of the cached request. * * @param request the request * @return payload as String * @throws UnsupportedEncodingException */ static String getCachedRequestPayload(final HttpServletRequest request) throws UnsupportedEncodingException { String payload ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class) if (wrapper) { byte[] buffer = wrapper.getContentAsByteArray() if (buffer) { payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding()) } } return payload } }

Once we have extended Grails provided GrailsExceptionResolver class as shown above, we need to override Grails provided exception resolver and register this as our application's exception handler. A bean definition in resources.groovy like the following will exactly do that:
grails-app/conf/spring/resources.goovy
beans = { ... // custom exception handler to log request body in addition to log message for exceptions exceptionHandler(LogRequestBodyExceptionResolver) { exceptionMappings = [ 'java.lang.Exception': '/error' ] } }

That is one side of this problem which takes care of logging request body along with the exception message for exceptions.

Step-2

The other side of the problem is to put a solution to make request body available for multiple reads. Typically once getInputStream() or getReader() is called to consume request body from  HttpServletRequest the next call will throw an exception preventing it to call multiple times. This needs a way to cache the request and wrap it using HttpServletRequestWrapper to make it's body readable multiple times. Fortunately Spring comes with ContentCachingRequestWrapper that exactly does this. But this needs to be done in a filter to wrap the request. Grails filters will not let this level of customization with request or response objects available in filters. This needs a servlet filter configuration. Spring comes with few handy implementations of filters that use ContentCachingRequestWrapper in them. One such useful filter class is: CommonsRequestLoggingFilter that allows request customizations. This filter can be setup in web.xml as a servlet filter that in-turn caches and wraps HttpServletRequest.

A typical Grails application doesn't include web.xml as it gets created when the application war file is built. In order to get this filter definition into the generated web.xml that gets bundled into war file, we need to install Grails templates. These are the templates that Grails framework uses for code generations. For customizing any code generation, we need to install and make changes to the corresponding template, in this case templates/war/web.xml file is the one we add a filter into.

Running the command: grails install-templates, installs all templates that Grails framework itself uses to generate code under src/templates dir. For this solution, we don't need all installed template files. We can delete all except web/web.xml once templates.

Edit war/web.xml and add the following filter entry:
... <!-- ================ Custom filters BEGIN ================ --> <!-- Filter provides org.springframework.web.util.ContentCachingRequestWrapper which in turn caches and makes request body multi-readable. We leverage the cached request and access request body in LogRequestBodyExceptionResolver for logging enhanced exception message that includes request payload. --> <filter> <filter-name>commonsRequestLoggingFilter</filter-name> <filter-class>org.springframework.web.filter.CommonsRequestLoggingFilter</filter-class> <init-param> <param-name>includePayload</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>includeClientInfo</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>includeQueryString</param-name> <param-value>true</param-value> </init-param> </filter> <!-- URL patterns the filter is applied to --> <filter-mapping> <filter-name>commonsRequestLoggingFilter</filter-name> <url-pattern>/sites/*</url-pattern> <url-pattern>/api/*</url-pattern> <url-pattern>/admin/*</url-pattern> </filter-mapping> <!-- ================ Custom Filters END ================= --> ...

This makes every request go through this filter for listed URL patterns.

With this, we have enhanced Grails provided exception resolver that adds request body to exception log message which gets logged when an exception arises while processing a http request that includes body.

TIP

As this solution leverages Spring provided request logging filter that offers logging request parameters, body and client-info of every request for debug level when enabled, we can as well have debug log level enabled for this class in development in Config.groovy:

log4j = { environments { development { ... debug 'org.springframework.web.filter.CommonsRequestLoggingFilter' ... } } }

This will log every request that goes through this filter with extra enabled details in debug mode.

References


  • Grails Filters Doc
  • Grails install-templates command
  • Grails Goodness: Exception Methods in Controllers
  • Logging http request and response with Spring 4


  • Saturday, May 13, 2017

    Running Tests - differences between Grails 2 and Grails 3 . . .

    Grails - A highly-productive and rapid application development framework for developing modern applications on JVM which not only promotes several good design & development practices but also promotes testing. It takes testing as an integral part of development by making many hard things simple. It also makes writing & running tests as fun as development ;)

    Grails 3 got so much better than Grails 2. However, there are several differences between Grails 2 and Grails 3 applications and testing is no exception. Due to these differences, confusions arise running tests while switching between Grails 2 and Grails 3 applications. Grails documentation covers this in detail, but it's handy to have all these at one place, and hence I write this blog post ;)

    Grails 2 way

    Usage grails [environment] test-app [SpecificTest ...] [test-phase:[spock]...]

    [environment] is optional, defaults to test, when specified it can be just dev (shortcut for development) or -Dgrails.env=<environment> where <environment> can be development, test or custom-env .

    [SpecificTest ...] is optional and when specified, it can take one or more SpecificTests to run separated by a space, each SpecificTest can also take wild card * at either class-level or package-level.

    [test-phase:[spock] ...] is optional, defaults to all test phases, unit:, integration: and functional:test phases.  When specified it can be one, any two or all three of test phases separated by space.  The extra spock is only needed for running spock specifications.

    Examples # run all tests: unit, integration and functional in test environment grails test-app # run all tests: unit, integration and functional in development environment grails dev test-app grails -Dgrails.env=development test-app # run all tests in 'specific-env' grails -Dgrails.env=<specific-env> test-app # run all unit tests in test environment grails test-app unit: # run all unit and functional tests in test environment grails test-app unit: functional: # run all unit tests in a specific package in test environment grails test-app com.giri.* unit: # run all unit & integration tests in a specific package in test environment grails test-app com.giri.* unit: integration: # run selected unit tests in test environment grails test-app Test1 Test2 unit: # run all unit tests in a package, particular test method of a test-case & specific test-case in test environment grails test-app com.giri.* Test1.testMethod1 MyTest2 unit: # run all spock unit test specifications in test environment grails test-app unit:spock

    Grails 3 - Grails way

    Grails 3 changed it's build system from GANT (Groovy ANT) to Gradle. It provides Grails commands for Gradle tasks. Gradle doesn't distinguish integration and functional tests and hence in Grails 3 both integration and functional test-phases are combined into integration test phase. In other words, both integration and functional tests are run as part of integration test-phase. Unlike Grails 2 which takes test-phase as test-phase:, Grails 3 takes test-phase as -test-phase.

    Note the difference: at the end of test-phase vs. - in the beginning of the test-phase. Also, there is no need for giving any special indication for Spock specifications.

    Usage grails [environment] test-app [SpecificTest ...] [-test-phase...]

    [environment] is optional, defaults to test, when specified it can be dev (shortcut for development) or -Dgrails.env=environment where environment can be development, test or custom-env .

    [SpecificTest ...] is optional and when specified, it can take one or more SpecificTests to run separated by space, each SpecificTest can also take wild card * at either class-level or package-level

    [-test-phase ...] is optional, defaults to all: -unit and -integration. When specified it can be one, or both test phases separated by space.

    Examples (with grails command) # run all tests: unit, integration and functional in test environment grails test-app # run all tests: unit, integration and functional in development environment grails dev test-app grails -Dgrails.env=development test-app # run all tests in 'specific-env' grails -Dgrails.env=<specific-env> test-app # run all unit tests grails test-app -unit # run all unit, integration & functional tests. Equivalent to not specifying test-phases at all grails test-app -unit -integration # run all unit tests in a specific package grails test-app com.giri.* -unit # run all unit, integration and functional tests in a specific package grails test-app com.giri.* -unit -integration # run selected unit tests grails test-app Test1 Test2 -unit # run all unit tests in a package, particular test method of a test-case & specific test-case grails test-app com.giri.* Test1.testMethod1 MyTest2 -unit

    Grails 3 - Gradle way

    Grails 3 has Gradle as it's build system and hence one can also use gradle tasks directly to run tests. There are only two test-phases: unit(test) and integration (integrationTest). Integration test-phase covers both integration and functional. Following are examples of running tests with gradle command:

    Examples (with gradle command, it's recommended to use gradle wrapper: gradlew) # run all unit tests, units tests don't need to be run in a specific environment ./gradlew test # run all unit tests in a specific package ./gradlew test --tests com.giri.* # run all unit tests in a specific package matching the specific test class name pattern ./gradlew test --tests com.giri.My*Spec # run specific unit test spec ./gradlew test --tests com.giri.Test1Spec # run specific unit test-spec and a specific feature method (when method name is in JUnit style) ./gradlew test --tests com.giri.Test1Spec.testFeatureMethod # run specific unit test-spec and a specific feature method (method name is in Spock style) ./gradlew test --tests com.giri.Test1Spec."test feature method" # run all integration and functional tests in development environment ./gradlew -Dgrails.env=development integrationTest #run integration and functional tests in 'specific-env' ./gradlew -Dgrails.env=<specific-env> integrationTest # clean & run all integration and functional tests in development environment. Continue on failing unit tests ./gradlew -Dgrails.env=development clean test integrationTest --continue # assemble but skip running tests (assemble depends on integrationTest) ./gradlew -Dgrails.env=development assemble -x integrationTest NOTE # run all unit tests in a package, a particular test method of a test-case and a specific test-case I haven't found a way to get this... #./gradlew test --tests com.giri.* Test1.testMethod1 MyTest2

    Knowing all these differences and possible ways of running tests will definitely be a time saver in a developer's day-to-day development.

    My Other posts on Grails Testing

    References

    Sunday, December 18, 2016

    Upgrading Grails-2.2.1 to Grails-3: Static Assets take a BIG move . . .

    I recently upgraded a Grails 2.2.1 web-app to Grails 3.2.1. It was a BIG move forward. Moving static assets (JavaScripts, CSS and Images) to their new assets directory, organizing & setting up directives/manifests in their new home directory, making all required changes to views & templates, testing all views for styles & images, and testing views with AJAX functionality involving JavaScripts... overall, it took a considerable amount my time during the whole upgrade efforts.

    Following are some key points I have from my efforts:
    • Grails 3 doesn't come with Resources plugin and hence you will not have ResourceTagLib in your classpath. If you want, you can probably still use Resources plugin in Grails-3 app, but Asset-Pipeline plugin seems to be the viable option.
    • Static assets which can be handled by Asset-Pipeline plugin need a move from web-app directory to grails-app/assets directory, their new home. Files under web-app/css, web-app/js and web-app/images now go into grails-app/assets/stylesheetsgrails-app/assets/javascripts and grails-app/assets/images respectively.
    • Grails-3 comes with jquery-2.2.0.min.js and bootstrap.js(3.3.6). If your Grails 2.2.1 app was dependent on these, you probably had jquery-1.7.2.min.js and bootstrap.js(2.2.2). If so, you will be better off retaining older versions to start with the upgrade process to eliminate this new variance in upgrading-equation.
    • The recommended approach to upgrade Grails 2.x app to Grails 3.x is to first create a new Grails-3 application and start copying all artifacts from old to new locations. Grails-3 documentation's Upgrading section has very well documented details on old and new locations. With respect to static assets, when a new Grails-3 app is created, you will notice images, javascripts and stylesheets sub directories under grails-app/assets. Also, you will have a bunch of static assets already sitting in there. You may have to do some cleanup with these files.
    • Modularize your assets
      1. AppResources.groovy is Resources plugin’s way of modularizing JavaScripts and CSS files by grouping these static assets into modules. But Asset-Pipeline plugin minifies compresses all JavaScript & CSS files, also enables browser cache, and hence static assets are only served once for all pages. So, it may not be required to group/ modularize static assets. If truly needed, for every module (e.g. module1) in AppResources.groovy, an equivalent manifest/directive with module-name.js (e.g. modul1.js) file can be created listing all it’s dependencies.
      2. The directive files application.js and application.css are main manifest files for JavaScripts and CSS.
      3. If you have modularized static assets in Grails-2 app, your main static resource AppResources.groovy should be your reference for re-organizing your static assets in Grails-3 app to minimize changes in views and view templates.
      4. You can modularize your static assets in Grails-3 the same way as in Grails-2 app with no need for ApprRsources.groovy file but with equivalent module manifests/directives created.
      5. Create a one-to-one asset-pipeline directive/manifest file for each of your module defined in AppResources.groovy. For instance, if you have, let's say a common module defined listing all it's dependency resources (both JavaScripts and CSS files), create common.js and common.css asset-pipeline directives that list required JavaScript and CSS dependencies respectively in Grails-3 app.
      6. If you have many modules in your application, your grails-app/assets/javascripts and grails-app/assets/stylesheets will get cluttered and mixed with manifest files and actual assets. You will be better off keeping directives separate from actually assets by keeping actual javascript and css assets under grails-app/assets/javascripts/lib and grails-app/assets/stylesheets/shared sub-directories respectively so that asset-pipeline manifest files can be under main grails-app/assets/javascripts and grails-app/assets/stylesheets directories.
    • Modify views & view templates and change resources tags to equivalent asset-pipeline tags
      1. Replace all <r:script> </r:script> with <asset:script type=”text/javascript”> </asset:script>
      2. Replace <r:external file="/static/images/favicon.ico"/> with <asset:link rel='shortcut icon' href="favicon.ico" type="image/x-icon"/>
      3. Remove all <r:layoutResources/> in <head></head> and replace all <r:layoutResources/> at the very bottom of layout pages with <asset:deferredScripts/> (This is Asset-Pipeline plugin's equivalent of Resources plugin’s deferring scripts to the bottom of the page)
      4. If you have modularized assets in Grails 2.2.1 app, for example, for module 'module1'  replace all <r:require module=”module1”/> with <asset:stylesheet src=”module1”/> and <asset:javascript src=”module1”/> if module1 has both JavaScripts and CSSs in it.
    • If there are other static assets like pdf files that are referenced in views by grails resource tag or it's equivalent method call, it can safely be moved from Grails-2's web-app/pdf to Grails-3's grails-app/assets/pdf and be served by Assets-Pipeline plugin. These assets, like images, need no manifest/directive files and it simply works.

    Summary

    Grails moved away from Resources plugin in favor of Asset-Pipeline plugin starting from 2.4. Upgrading prior versions of Grails 2.4 apps to 3.x certainly requires considerable development and testing efforts with respect to static assets. So, just be prepared for this BIG move.

    References

    Resources Plugin Docs
    Asset-Pipeline Plugin Docs
    Asset-Pipeline Plugin - GitHub source code
    Grails Team Blog Post on Migrate from Resources Plugin to Asset-Pipeline Plugin
    Very nice Introduction to Asset Pipeline Plugin

    My previous posts on Upgrading Grails application from 2.2.1 to 3.2.1

    Sunday, November 20, 2016

    Upgrading Grails-2 application to Grails-3: Spring Security Core Plugin differences . . .

    I recently upgraded one of our Grails 2.2.1 with Spring Security core plugin 1.2.7.3 on Java 1.6 application to Grails 3.2.1 with Spring Security core plugin 3.1.1 on Java 1.8. By following the recommended path detailed out well enough in Grails 3 documentation, I got the following done before I got to the point of successfully running the application:
    • Upgraded one of our in-house plugins: ZipCityState
    • Reorganized Grails artifacts and other files as per Grails-3 app directory structure
    • Rewrote build and other configurations
    • Fixed several code compilation errors and issues resulted due to changed package names of several Grails frame-work classes and some classes that are deprecated and removed
    • Upgraded static resources like images, javascript and stylesheets from resources plugin to asset-pipeline plugin by re-organizing those files and creating appropriate asset-pipeline directives to mimic resource plugin's modules defined in AppResources.groovy
    Once all the above are done, I had to make the following changes from the Security aspect for the application to successfully run, display and login:

    Static Rules

    Static rules are now List of Maps and not just a Map. I covered this in my previous post. Check it out.

    Authentication

    Change username and password form fields in login page (auth.gsp) from j_username and j_password to username and password.

    If you have used UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY somewhere in your code, you need to change that to SpringSecurityUtils.SPRING_SECURITY_LAST_USERNAME_KEY

    If you have any pre authentication checks written by extending DefaultPreAuthenticationChecks, the hibernate session seems not created and attached to the current thread at this point.

    If you run into any exception like the following, you may need to use either withTransaction or withSession method on the domain object to come over this.

    org.springframework.dao.DataAccessResourceFailureException: Could not obtain current Hibernate Session; nested exception is org.hibernate.HibernateException: No Session found for current thread.

    Password encryption algorithm differences

    The application has an admin account created in the database only once with exists check from the Bootstrap. The login failed for admin user that was created by Grails 2.2.1 app and after upgrading to Grails 3.2.1 with the following exception:

    ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[grailsDispatcherServlet] - Servlet.service() for servlet [grailsDispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause java.lang.AssertionError: Salt value must be null when used with crypto module PasswordEncoder. Expression: salt. Values: salt = admin at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:404) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:650) at grails.plugin.springsecurity.authentication.encoding.BCryptPasswordEncoder.checkSalt(BCryptPasswordEncoder.groovy:49)

    The error was bit puzzling and made me to comment out the following Spring security core plugin's configuration property set in application.groovy:

    //grails.plugin.springsecurity.dao.reflectionSaltSourceProperty = 'username’

    Commenting out that property revealed the issue with the following error:
    WARN org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder - Encoded password does not look like BCrypt

    After quickly reading through documents of both Grails-2 Spring Security Core Plugin and Grails-3 Spring Security Core Plugin, there was a special mention of Bcrypt algorithm in version 3 documentation. Also, it was specified up front in the Configuration Settings section of the doc that the plugin's default security settings are maintained in DefaultSecurityConfig.groovy file. I checked both plugin 2, plugin 3 and found the following differences:

    Grails-3 plugin
    password { algorithm = ‘bcrypt’ encodeHashAsBase64 = false bcrypt { logrounds = 10 } hash { iterations = 10000 } }

    Grails-2 plugin
    password.algorithm = 'SHA-256' password.encodeHashAsBase64 = false password.bcrypt.logrounds = 10

    Differences are highlighted. The hash.iterations property is set to 10000 in Grails-3 plugin, but is not set explicitly in Grail-2 plugin. I had to add algorithm and hash.iterations explicitly to match Grails-2 plugin and retain the reflectionSaltSourceProperty in application.groovy.The following are the changes:

    grails.plugin.springsecurity.password.algorithm = 'SHA-256' grails.plugin.springsecurity.password.hash.iterations = 1 grails.plugin.springsecurity.dao.reflectionSaltSourceProperty = 'username'

    Summary

    With static rule configuration changes, password encryption properties changes and code changes to auth.gsp and some security related classes, I was able to get the application successfully migrated from Grails 2.2.1 to Grails 3.2.1 along with upgraded Spring Security core plugin.

    References

    Grails 3.2.1 documentation
    Grails Spring Security 2 documentation
    Grails Spring Security 3 documentation


    Sunday, November 13, 2016

    Upgrading Grails-2 application to Grails-3 - Make static assets available . . .

    Recently, I started upgrading a Grails web-application from Grails-2 to Grails-3, particularly from version 2.2.1 to 3.2.1. The perfect minor.patch(2.1) matching of both versions in this process was just a perfect timing-coincidence as Grails was at 2.2.1 when this app was developed and the latest Grails was at 3.2.1 when I started to upgrade.

    The process of upgrading was not bad, but it required quite a bit of careful changes. I simply followed the very well documented Grails3 Documentation and get going this effort after successfully migrating an in-house plugin ZipCityState from Grails-2 to Grails-3. This deserves another blog post.

    Static Assets

    One of the web aspects that needed bit more effort in this upgrade was: Static Assets.

    Grails-3 comes with asset-pipeline plugin. Static assets like images, javascript and stylesheets need an upgrade by moving from Resources plugin to Asset-pipeline plugin. There is a nice blog post on this from Grails Team and according to this, the viable option in Grails-3 is asset-pipeline and resources plugin is not available. I will write a bit more detailed post on this later.

    After upgrading the application and taking care of several compilation and startup issues along the way, I was finally able to get the application started and running. The login page showed up but all the static assets like stylesheets, javascripts and images were totally missing. This made me look into all asset pipeline directives and related changes I made in migrating static assets to asset-pipeline. Everything looked good but the issue was just puzzling. After spending sometime finally I realized that static assets upgrade to asset-pipeline from Grails-2 to Grails-2 need some attention from security settings as well.

    Grails Spring Security Plugin takes a pessimistic locks down approach and locks down all URLs that do not have an applicable URL mapping. 

    Usually, Grails-2 security static rules look like:
    grails.plugins.springsecurity.controllerAnnotations.staticRules = [ '/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'], '/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'], '/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'], '/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'], '/**': ['IS_AUTHENTICATED_FULLY'] ]

    In Grails-3, add pattern 'assets/**' for static assets as they are now moved under grails-app/assets and get served by asset pipeline plugin through requests URLs like- http://my-app-url/assets/.  Make the static rules look like the following (highlighted is the change):
    grails.plugin.springsecurity.controllerAnnotations.staticRules = [ [pattern: '/login/**', access: ['IS_AUTHENTICATED_ANONYMOUSLY']], [pattern: '/assets/**', access: ['IS_AUTHENTICATED_ANONYMOUSLY']], [pattern:'/**', access: ['IS_AUTHENTICATED_FULLY']] ]

    There you go, asset pipeline plugin works and all static assets are available, get served and become visible!

    References


    Sunday, October 16, 2016

    Migrate Grails 2.2.1 application data from Oracle to PostgreSQL (Part-2 of 2) . . .

    Phase-1 Database Migration

    Part -2: Migrate Data from Oracle to PostgreSQL

    In Part-1, I covered migrating Grails Application from Oracle to PostgreSQL.

    In this Part-2, I cover Data Migration to conclude this two-part series. This is not a comprehensive post by any means. I only cover what I experienced in this process.

    After migrating Grails application, my next step was to migrate data. I knew this is where things would become bit tricky. I always feel that Software development is interesting with many competing technologies and products, but is made messy with only few similarities but many differences. Competing technologies start to emerge with little or no standards and deviate so much. Databases are no exception.

    During application development, the debate of application logic getting buried in Stored Procedures as opposed to opening up in the application's business/service layer gets to the question of "What if we change the database?". The answer, "We won't change our database" often wins the debate and takes the roots of business-logic deeper and deeper into the database. Lately, PostgreSQL has been making many to rethink of investing in expensive databases. Change is inevitable. The time to "change your database" has come- to some, at least!

    My first effort

    My first effort was to turn to Google and find any tool(s) that could help migrate data from Oracle to PostgreSQL. After checking several commercial tools, an open-source tool (Ora2Pg), and trying out one tool (Navicat), it was an easy decision for me: decided not to use any tool.

    Most of the tools migrate both schema & data, often resulting with several datatype mismatches between Grails application's domain-model and Database schema, especially with fields like boolean that has a close matching database equivalent boolean type in PostgreSQL. It's not easy for the tools to infer these details through database schema/metadata.

    The Approach

    I took the well-known approach in Software Engineering: ETL - a 3-Step process of migrating data.
    1. Extraction- I leveraged SQL Developer tool to extract data out into delimited files.
    2. Transformation- I wrote Groovy scripts to transform data by transforming data of only those data types that needed special care.
    3. Load- I wrote groovy scripts to load transformed data files into PostgreSQL using its COPY command which is very cool for this task.

    Data Migration Step-by-Step

    Step-0: Create PostgreSQL database and generate schema

    Create PostgreSQL database and generate schema by leveraging Grails and dbm-plugin. I covered this in Part-1.

    Step-1: Extract data from Oracle

    There are many possible ways to extract/export data from Oracle. I used SQL Developer tool to extract data into delimited multiple files. Each file contains exported data of a specific table.

    Certain data types can be transformed during data export by setting preferences. Default Date format is one such that differs form database to database. With this ETL approach, dates certainly need to go through transformations due to default-format differences between databases. This transformation can easily be avoided by taking it into this step of Extraction/Export. SQL Developer offers to set data preferences for a session. With this feature, we can set preferences for Date/Timestamp fields and change the format to default PostgreSQL date format.

    Set Data Preferences

    Open SQL Developer and connect to database. To check current preferences, go to Preferences > Database > NLS for formats from Oracle SQL Developer main menu item.

    Execute the following SQL to alter date format for that particular session:

    alter SESSION set NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS';
    alter SESSION set NLS_TIMESTAMP_FORMAT = 'MM/DD/YYYY HH24:MI:SSXFF';

    Export Data

    Go to Tools > Database Export and follow the following steps:
    Select Connection
    Uncheck Export DDL
    Format: delimited
    Delimiter: % (your preferred char, I chose % as many of special characters are found in the actual data in some column or other)
    Left Enclosure: "
    Right Enclosure: "
    Save as: Separate Files
    File: enter dir name (where the exported data to be saved)

    Simply go through next steps to finish. You will have data exported into multiple files in the directory specified with each file name as <TABLE_NAME>_DATA_TABLE.dsv.  For instance if you have a table MY_TABLE, it's exported delimited data file would be: MY_TABLE_DATA_TABLE.dsv.

    Step-2: Transform Data

    This step requires, finding all data types that need data transformation. Once data type Date is taken care during the Extraction process, I was only left to deal with Boolean/boolean data types, transforming 1/0 to true/false. This requires identifying all such data columns in the entire schema. There are two choices for finding those columns: 1) Database metadata, 2) Grails domain model. I chose Grails domain-model and wrote a simple script to find the list of tables and columns that are mapped to Boolean/boolean properties of domain-model. I had covered this in my previous post: The power of Grails Metadata is there when you need it. Once I found those details, I got everything that I needed to transform data.

    I wrote Groovy script for transforming data. Following is the Groovy script:

    import groovy.json.JsonSlurper /** * oracle-to-postgres-data-mIgrator.groovy * This groovy script migrates/transforms delimited data-files exported from Oracle to data-files that are PostgreSQL * compliant. It takes care of special data migration needed for data-column types like boolean, and nullable FK columns * that get outputted as "" for nulls when left enclosure(") and right enclosure (") characters are used during data * export. * * Certain dependencies like: base directory the data-files are located in, data-file extension etc. are externalized * into postgres-migration-config.groovy file for making this script little flexible. * * Prerequisites * 1. Run script: boolean-columns-finder.groovy from the application's home dir. * 2. Export all data from Oracle database into delimited files from a tool like: SqlDeveloper. * 3. Adjust any configurations needed in postgres-data-migration-config.groovy file. * Especially config properties like: delimiter and baseDir * * Dependencies (input) * 1. Requires myAppTablesBooleanColumns.json to exist in the application home directory. This is the file that is * result of executing boolean-columns-finder.groovy that contains a map of table names and corresponding list of * boolean columns. * 2. A set of delimited data files exported using a tool like SqlDeveloper. * * Assumptions for Data Export (based on the tool used SqlDeveloper to export oracle data). * . Each table's data is exported into it's own data file with the filename like: _DATA_TABLE.dsv * . To simplify parsing, used % as the delimiter because it's one unused character found in application's data. * . Each data file contains header as the first line which is nothing but column names in capital letters. * . Used left enclosure(") and right enclosure (") characters during data export which encloses column-data like * long text that contains embedded new lines, and nullable FK's that are null. * . Also, default date format is different between oracle and PostgreSQL. The data migration date columns has been * eliminated by leveraging SqlDeveloper to export data in the format that PostgreSQL can import. * This can be achieved by running the following SQLs for the session in SqlDeveloper before data export. * alter SESSION set NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS'; * alter SESSION set NLS_TIMESTAMP_FORMAT = 'MM/DD/YYYY HH24:MI:SSXFF'; * * Result (output) * 1. Migrated data files. Each data file (_DATA_TABLE.dsv) will have a corresponding migrated data file * (_DATA_TABLE.dsv.migrated) * 2. Only data that needs to be migrated (for boolean data) goes through data migration. * The input json file: myAppTablesBooleanColumns.json contains all tables that need data migration. For every * table that needs data migration, the corresponding input data-file name is derived by the above mentioned * filename assumptions. * e.g. table: my_table, data-file: MY_TABLE_DATA_TABLE.dsv, migrated data-file: MY_TABLE_DATA_TABLE.dsv.migrated * 3. Rest of the tables' data that do not need data migration (do not have boolean columns) is a simple passthrough * with just the header migration. The header migration is simply converting column names to lowercase. * * @see postgres-data-migration-config.groovy * @see boolean-columns-finder.groovy * * @author Giri Pottepalem */ def config = new ConfigSlurper().parse(new File('postgres-data-migration-config.groovy').toURI().toURL()) def dataDelimiter = config.data.delimiter def datafileBaseDir = config.data.file.baseDir def datafileExtension = config.data.file.extension def migratedDatafileExtension = config.data.migrated.file.extension def tablesBooleanColumnsFile = config.tables.boolean.columns.file if(!new File(tablesBooleanColumnsFile).exists()){ println "Error: Missing required file ${tablesBooleanColumnsFile} for data migration." System.exit(1) } Map tablesBooleanColumnsMap = new JsonSlurper().parseText( new FileReader(tablesBooleanColumnsFile).text ) def dataFilesNeedDataMigraton = tablesBooleanColumnsMap.keySet().collect { "${it.toUpperCase()}${datafileExtension}" } def allDataFiles = new File(datafileBaseDir).listFiles().name.findAll { it.endsWith(datafileExtension) } List dataFilesJustNeedHeaderMigration = allDataFiles - dataFilesNeedDataMigraton println "Migrating just header for all data files that don't need data migration..." dataFilesJustNeedHeaderMigration.each { dataFilename -> String absoluteDataFilename = "${datafileBaseDir}/$dataFilename" print "Reading data file: $absoluteDataFilename..." File dataFile = new File(absoluteDataFilename) File migratedDataFile = new File("${absoluteDataFilename}${migratedDatafileExtension}") migratedDataFile.withWriter { fileWriter -> dataFile.eachLine { line, lineNumber -> if (lineNumber == 1) { fileWriter.writeLine(line.toLowerCase()) //migrate header } else { fileWriter.writeLine(line) //take data as is } } } println "Migrated" } println "Migrating data..." tablesBooleanColumnsMap.each { String tableName, List booleanColumns -> String dataFilename = "${datafileBaseDir}/${tableName.toUpperCase()}${datafileExtension}" print "Reading data file: $dataFilename..." File dataFile = new File(dataFilename) File migratedDataFile = new File("${dataFilename}${migratedDatafileExtension}") String header def columnNames migratedDataFile.withWriter { fileWriter -> dataFile.eachLine { String line, int lineNumber -> if (lineNumber == 1) { header = line.toLowerCase() //migrate header columnNames = header.split(dataDelimiter) fileWriter.writeLine(header) } else { //migrate data String[] columnData = line.split(dataDelimiter, -1) def booleanColumnIndexes = booleanColumns.collect { columnName -> columnNames.findIndexOf { it.equalsIgnoreCase(columnName) } } booleanColumnIndexes.each { booleanColumnIndex -> if (booleanColumnIndex < columnData.length) { switch (columnData[booleanColumnIndex]) { case null: columnData[booleanColumnIndex] = ''; break case '1': columnData[booleanColumnIndex] = 't'; break case '0': columnData[booleanColumnIndex] = 'f'; break } } } //update data that ended up as "" due to blank to '' in data exoport columnData.eachWithIndex { data, i -> if (data == '""') { columnData[i] = '' } } fileWriter.writeLine(columnData.join(dataDelimiter)) } } } println "Migrated" } println "Done"

    Following is a sample of externalized configuration properties as Groovy script in order to make the above script bit flexible:
    /** * postgres-data-migration-config.groovy * Configuration properties as groovy script * @author Giri Pottepalem */ data { delimiter = '%' file { baseDir = './export' //dir in which delimited data files exist extension = '_DATA_TABLE.dsv' } migrated { file { extension = '.migrated' } //need to be loaded in specific order due to foreign key constraints files = [ 'MY_TABLE1_DATA_TABLE.dsv.migrated', 'MY_TABLE2_DATA_TABLE.dsv.migrated', ... ] } } appHome = '/gateway' //myApp application home dir //file that contains list of tables and boolean columns for each table. Result of running boolean-columns-finder.groovy tables.boolean.columns.file = "${appHome}/myAppTablesBooleanColumns.json" postgres { dataSource { url = 'jdbc:postgresql://localhost:5432/myApp' user = 'postgres' //superuser for running COPY password = 'password' driverClassName = 'org.postgresql.Driver' } //Adjust sequences with values taken from oracle db by: select sequence_name, last_number from user_sequences; sequences = [ 'hibernate_sequence': 150461, 'table1_sequence': 4041 ] }

    Step-3: Load Data

    Once data is migrated, find out data dependencies order and load data in the required order that satisfies foreign-key constraints. I covered this in my previous post: Groovy Script to load CSV data files into PostgreSQL database onto Amazon RDS.

    References