- Create a new project via Intellij with Grails 3.2.9 and Java 8
grails create-app education |
When executing create-app command, by default the "web" profile is used. |
- Place all files from Grails 2 project to Grails 3 project respecting packages
Old Location | New Location | Description |
---|---|---|
grails-app/conf/BuildConfig.groovy | build.gradle | Build time configuration is now defined in a Gradle build file |
grails-app/conf/Config.groovy | grails-app/conf/application.groovy | Renamed for consistency with Spring Boot |
grails-app/conf/UrlMappings.groovy | grails-app/controllers/UrlMappings.groovy | Moved since grails-app/conf is not a source directory anymore |
grails-app/conf/BootStrap.groovy | grails-app/init/BootStrap.groovy | Moved since grails-app/conf is not a source directory anymore |
scripts | src/main/scripts | Moved for consistency with Gradle |
src/groovy | src/main/groovy | Moved for consistency with Gradle |
src/java | src/main/groovy (yes, groovy!) | Moved for consistency with Gradle |
test/unit | src/test/groovy | Moved for consistency with Gradle |
test/integration | src/integration-test/groovy | Moved for consistency with Gradle |
web-app | src/main/webapp or src/main/resources/ | Moved for consistency with Gradle |
\*GrailsPlugin.groovy | src/main/groovy | The plugin descriptor moved to a source directory |
src/main/resources/public is recommended as src/main/webapp only gets included in WAR packaging but not in JAR packaging.
It is recommended to merge Java source files from
src/java into src/main/groovy . You can create a src/main/java
directory if you want to and it will be used but it is generally better
to combine the folders. (The Groovy and Java sources compile together.) |
- Our BuildConfig.groovy in Grails 2 project will be our build.gradle in Grails 3 project
buildscript { repositories { mavenLocal() maven { url "https://repo.grails.org/grails/core" } } dependencies { classpath "org.grails:grails-gradle-plugin:$grailsVersion" classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.14.1" classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}" classpath "org.grails.plugins:database-migration:3.0.0" } } version "1.1" group "education" apply plugin:"eclipse" apply plugin:"idea" apply plugin:"war" apply plugin:"org.grails.grails-web" apply plugin:"org.grails.grails-gsp" apply plugin:"asset-pipeline" repositories { mavenLocal() maven { url "https://repo.grails.org/grails/core" } } dependencies { compile "org.springframework.boot:spring-boot-starter-logging" compile "org.springframework.boot:spring-boot-autoconfigure" compile "org.grails:grails-core" compile "org.springframework.boot:spring-boot-starter-actuator" compile "org.springframework.boot:spring-boot-starter-tomcat" compile "org.grails:grails-dependencies" compile "org.grails:grails-web-boot" compile "org.grails.plugins:cache" compile "org.grails.plugins:scaffolding" compile "org.grails.plugins:hibernate5" compile "org.hibernate:hibernate-core:5.1.3.Final" compile "org.hibernate:hibernate-ehcache:5.1.3.Final" console "org.grails:grails-console" profile "org.grails.profiles:web" runtime "com.bertramlabs.plugins:asset-pipeline-grails:2.14.1" runtime "com.h2database:h2" testCompile "org.grails:grails-plugin-testing" testCompile "org.grails.plugins:geb" testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1" testRuntime "net.sourceforge.htmlunit:htmlunit:2.18" // MySQL 5 Driver runtime "mysql:mysql-connector-java:5.1.36" // Ajax Tags compile "org.grails.plugins:ajax-tags:1.0.1.BUILD-SNAPSHOT" // Joda Time compile "joda-time:joda-time:2.9.6" // Rest Client Builder compile "org.grails:grails-datastore-rest-client" // Database Migration compile "org.grails.plugins:database-migration:3.0.0" compile "org.liquibase:liquibase-core:3.5.3" // Spring Security Core compile "org.grails.plugins:spring-security-core:3.1.1" // Spring Security ACL compile "org.grails.plugins:spring-security-acl:3.1.0" // Spring Security Rest //compile "org.grails.plugins:spring-security-rest:2.0.0.M2" // Email compile "org.grails.plugins:mail:2.0.0.RC6" // Export compile "org.grails.plugins:export:2.0.0" // Google Visualization compile "org.grails.plugins:grails-google-visualization:2.2" // CKEditor compile "org.grails.plugins:ckeditor:4.5.9.0" } bootRun { jvmArgs('-Dspring.output.ansi.enabled=always') addResources = true } sourceSets { main { resources { srcDir 'grails-app/migrations' } } } assets { minifyJs = true minifyCss = true } |
There will be plugins already created for Grails 3 distribution but others don't. For these ones we must look for a workaround:
|
- Our Datasource.groovy in Grails 2 project will be in our application.yml in Grails 3 project
--- grails: profile: web codegen: defaultPackage: net.rdcstudios.core spring: transactionManagement: proxies: false gorm: # Whether to autowire entities. # Disabled by default for performance reasons. autowire: true # Changed to true to be able to inject services into domain classes reactor: # Whether to translate GORM events into Reactor events # Disabled by default for performance reasons events: false info: app: name: '@info.app.name@' version: '@info.app.version@' grailsVersion: '@info.app.grailsVersion@' spring: main: banner-mode: "off" groovy: template: check-template-location: false # Spring Actuator Endpoints are Disabled by Default endpoints: enabled: false jmx: enabled: true --- grails: mime: disable: accept: header: userAgents: - Gecko - WebKit - Presto - Trident types: all: '*/*' atom: application/atom+xml css: text/css csv: text/csv form: application/x-www-form-urlencoded html: - text/html - application/xhtml+xml js: text/javascript json: - application/json - text/json multipartForm: multipart/form-data pdf: application/pdf rss: application/rss+xml text: text/plain hal: - application/hal+json - application/hal+xml xml: - text/xml - application/xml urlmapping: cache: maxsize: 1000 controllers: defaultScope: singleton converters: encoding: UTF-8 views: default: codec: html gsp: encoding: UTF-8 htmlcodec: xml codecs: expression: html scriptlets: html taglib: none staticparts: none endpoints: jmx: unique-names: true --- hibernate: cache: queries: false use_second_level_cache: true use_query_cache: false region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory dataSource: pooled: true jmxExport: true driverClassName: com.mysql.jdbc.Driver dialect: org.hibernate.dialect.MySQL5InnoDBDialect # dialect: net.rdcstudios.mysql.dialect.MySQLUTF8MB4InnoDBDialect username: core password: core environments: development: dataSource: dbCreate: none internaldemo: dataSource: dbCreate: none demo: dataSource: dbCreate: none client: dataSource: dbCreate: none test: dataSource: dbCreate: create-drop uat: dataSource: dbCreate: none production: dataSource: dbCreate: none properties: jmxEnabled: true initialSize: 5 maxActive: 50 minIdle: 5 maxIdle: 25 maxWait: 10000 maxAge: 600000 timeBetweenEvictionRunsMillis: 5000 minEvictableIdleTimeMillis: 60000 validationQuery: SELECT 1 validationQueryTimeout: 3 validationInterval: 15000 testOnBorrow: true testWhileIdle: true testOnReturn: false jdbcInterceptors: ConnectionState defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED |
- Our log4j which is inside Config.groovy in Grails 2 project will be our logback.groovy in Grails 3 project
import grails.util.BuildSettings import grails.util.Environment import org.springframework.boot.logging.logback.ColorConverter import org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter conversionRule 'clr', ColorConverter conversionRule 'wex', WhitespaceThrowableProxyConverter // See http://logback.qos.ch/manual/groovy.html for details on configuration //statusListener(OnConsoleStatusListener) def appenderList = ["ROLLING"] def LOG_DIR = "." def consoleAppender = true def targetDir = BuildSettings.TARGET_DIR if (targetDir != null) { appenderList.add("CONSOLE") LOG_DIR = "/var/log/education/${Environment.currentEnvironment.name}" } else { LOG_DIR = "/var/log/education/${Environment.currentEnvironment.name}" consoleAppender = false } if (consoleAppender) { appender("CONSOLE", ConsoleAppender) { encoder(PatternLayoutEncoder) { pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" } } } appender("ROLLING", RollingFileAppender) { encoder(PatternLayoutEncoder) { Pattern = "%d %level %thread %mdc %logger - %m%n" } rollingPolicy(SizeAndTimeBasedRollingPolicy) { MaxFileSize = "5MB" FileNamePattern = "${LOG_DIR}/log.%d{yyyy-MM-dd}.%i" } } logger 'grails.app.services', INFO, appenderList logger 'grails.app.controllers', INFO, appenderList logger 'net.rdcstudios.core', INFO, appenderList //root(INFO, appenderList) // Spring Security //logger 'grails.plugin.springsecurity.web.filter.DebugFilter', INFO, ['STDOUT'] // Hibernate Logs //logger 'org.hibernate.SQL', DEBUG, ['STDOUT'] //logger 'org.hibernate.type.descriptor.sql.BasicBinder', TRACE, ['STDOUT'] |
- In Grails 3.x all internal APIs can be found in the
org.grails
package and public facing APIs in thegrails
package. Theorg.codehaus.groovy.grails
package no longer exists. All package declaration in sources should be modified for the new location of the respective classes. Examples
org.codehaus.groovy.grails.commons.GrailsApplication --> grails.core.GrailsApplication org.codehaus.groovy.grails.core.io.ResourceLocator --> org.grails.core.io.ResourceLocator org.codehaus.groovy.grails.web.mapping.LinkGenerator --> grails.web.mapping.LinkGenerator org.codehaus.groovy.grails.web.binding.DataBindingUtils --> grails.web.databinding.DataBindingUtils org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap --> grails.web.servlet.mvc.GrailsParameterMap In unit or integration tests... org.codehaus.groovy.grails.plugins.testing.GrailsMockMultipartFile --> org.grails.plugins.testing.GrailsMockMultipartFile |
- As
of now Grails 3 uses Pipeline Plugin to work with assets instead of
Resource Plugin in Grails 2. Therefore, we have to move all assets:
- web-app/css --> grails-app/assets/stylesheets
- web-app/images --> grails-app/assets/images
- web-app/js --> grails-app/assets/javascripts
- Create application.js as an index of all js files
// This is a manifest file that'll be compiled into application.js. // // Any JavaScript file within this directory can be referenced here using a relative path. // // You're free to add application-wide JavaScript to this file, but it's generally better // to create separate JavaScript files as needed. // //= require jquery-2.2.0.min //= require ie10-viewport-bug-workaround //= require rs-plugin/js/jquery.themepunch.tools.min //= require rs-plugin/js/jquery.themepunch.revolution.min //= require custom //= require menu/jquery.mmenu.min.all //= require bootstrap/bootstrap.min //= require bootstrap/moment //= require bootstrap/bootstrap-datetimepicker //= require bootstrap/bootstrap-switch.min //= require bootstrap/bootstrap-wizard //= require bootstrap/bootstrap-combobox //= require fullcalendar/fullcalendar.min //= require fullcalendar/lang-all //= require_self if (typeof jQuery !== 'undefined') { (function($) { $(document).ajaxStart(function() { $('#spinner').fadeIn(); }).ajaxStop(function() { $('#spinner').fadeOut(); }); })(jQuery); } |
- Create application.css as an index of all css files
/* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS file within this directory can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the top of the * compiled file, but it's generally better to create a new file per style scope. * *= encoding UTF-8 *= require bootstrap/bootstrap-responsive *= require pure-drawer *= require style *= require font-awesome/css/font-awesome.min *= require menu/jquery.mmenu.all *= require rs-plugin/css/extralayers *= require rs-plugin/css/settings *= require bootstrap/bootstrap-datetimepicker *= require bootstrap/bootstrap-switch.min *= require bootstrap/bootstrap-wizard *= require bootstrap/bootstrap-combobox *= require fullcalendar/fullcalendar.min *= require_self */ |
- Add application.js and application.css to main.gsp
<asset:stylesheet src="application.css"/> <asset:javascript src="application.js"/> |
- Change all <img> and <g:img> to <asset:image src="" />
- Change all <script>, <g:script> and <r:script> to <asset:script type="text/javascript">
- I had to add this line just before
</body>
in the outer main template to make javascript code embedded in views or templates work
<asset:deferredScripts/> |
- Change all ${resource()} methods to ${assetPath(src: ''}
- Change all <link> related to css to <asset:link>
- Change all path in templates from ../ to /
<g:render template="../layouts/bottomBar"/> to <g:render template="/layouts/bottomBar"/> |
- Remove all grailsApplication beans from Controllers as Grails 3 imports them automatically
def grailsApplication |
- Install new plugin called ajax-tags as Grails 3 removes all taglibs related to remote and to keep using them we will need to have it installed
// Ajax Tags plugin compile "org.grails.plugins:ajax-tags:1.0.1.BUILD-SNAPSHOT" |
- Change all prefixes about respond method convention
Example | Argument Type | Calculated Model Variable |
---|---|---|
respond Book.list() | java.util.List | bookList |
respond Book.get(1) | example.Book | book |
respond( [1,2] ) | java.util.List | integerList |
respond( [1,2] as Set ) | java.util.Set | integerSet |
respond( [1,2] as Integer[] ) | Integer[] | integerArray |
// course/_grid <g:each in="${courseInstanceList}" status="i" var="courseInstance"> to <g:each in="${courseList}" status="i" var="courseInstance"> |
- Change all references to config
grailsApplication.config.net.rdcstudios.pagination.size to grailsApplication.config.getProperty('net.rdcstudios.pagination.size') |
- Change all references to grailsResourceLocator
grailsResourceLocator.findResourceForURI() to assetResourceLocator.findAssetForURI() |
- Change all references to domainClass
instance.domainClass.propertyName to grails.util.GrailsNameUtils.getPropertyName(instance.class) |
- To make external config properties work, we will need to modify the class Application.groovy
import grails.boot.GrailsApp import grails.boot.config.GrailsAutoConfiguration import org.springframework.context.EnvironmentAware import org.springframework.core.env.Environment import org.springframework.core.env.MapPropertySource class Application extends GrailsAutoConfiguration implements EnvironmentAware { static void main(String[] args) { GrailsApp.run(Application, args) } @Override void setEnvironment(Environment environment) { def file = new File("/etc/core/core-${grails.util.Environment.getCurrent().name}-config.properties") if(file.exists()) { def config = new ConfigSlurper().parse(file.text) environment.propertySources.addFirst(new MapPropertySource(grails.util.Environment.getCurrent().name, config)) } } } |
- Plus to escape all values from external config properties we will need to add quotes "..." as YAML Parser launches :, . and / values as yml values
dataSource.url = jdbc:mysql://localhost/rdcstudios_dev to dataSource.url = "jdbc:mysql://localhost/rdcstudios_dev" |
- Move all global constraints from Config.groovy to application.groovy
// Shared constraints grails.gorm.default.constraints = { codeConstraint(blank: false, nullable: true, size: 1..4, unique: 'country') } |
- Don't forget to change empty constraints from () to [:]
static constraints = { name nullable: true, blank: false mySimpleProperty() // <- A field that has no constraints. This syntax is not supported in Grails 3. anotherProperty unique: true } to static constraints = { name nullable: true, blank: false mySimpleProperty [:] // <- Empty map argument instead of () anotherProperty unique: true } |
- All services injected into domain classes don't work. Why? In Grails 2 projects they were working property. The reason is that in Grails 3 there is a property called grails.gorm.autowired in application.yml set to false by default. This property is disabled by default for performance reasons.
Further info http://stackoverflow.com/questions/43059313/grails-3-2-8-dependency-injection-in-domain-classes https://gist.github.com/erichelgeson/be2f9f62ab63d989f2ec962ae7001f21 |
- Every time app throws an exception is displayed the following:
Caused by: groovy.lang.MissingMethodException: No signature of method: ch.qos.logback.classic.Logger.error() is applicable for argument types: (org.springframework.dao.DataIntegrityViolationException) |
Why? This is because the default logger that is added to Grails artefacts is now an instance of SLF4J, and not JCL. Unlike JCL, none of the SLF4J logging methods take a single
Object
argument. Therefore we need to do some changes:catch (DataIntegrityViolationException dive) { log.error(dive) flash.error = message(code: 'default.not.deleted.message', args: ["User", id], default: "User could not be deleted") redirect(action: 'show', id: userInstance?.id) } to catch (DataIntegrityViolationException dive) { log.error(dive.toString()) flash.error = message(code: 'default.not.deleted.message', args: ["User", id], default: "User could not be deleted") redirect(action: 'show', id: userInstance?.id) } |
Further info https://github.com/grails/grails-core/issues/9971 http://docs.grails.org/3.2.8/guide/single.html#upgrading31x (See Slf4j now default section) |
UNIT TESTS
No changesINTEGRATION TESTS
- I changed extends IntegrationSpec to Specification from all tests to keep rules as per Grails 3 doc.
- I added @Integration and @Rollback annotation at class level.
@Integration @Rollback class ActivityServiceIntegrationSpec extends Specification { ... }
- I had to change some load methods to get method as in Grails 3 data binding is more stricted.
def setupData() { def user = User.load(1) def course = new Course(shortName: 'Test shortname', summary: 'Test summary', owner: user).save() -> LazyInitializationException } to def setupData() { def user = User.get(1) def course = new Course(shortName: 'Test shortname', summary: 'Test summary', owner: user).save() -> Works fine } |
- As per Grails 3 it's not allowed to save any instance in setup method when @Rollback annotation is not added as app will throw HibernateException: No Session found. To solve this it's recommended to added @Rollback annotation and separate setup method (for non transactional operations) from setupData method (for transactional operations). Don't forget calling setupData method from given block
def setup() { alfrescoService.metaClass.uploadFile = { Object resource, MultipartFile resourceFile -> "1" } alfrescoService.metaClass.deleteFileByNodeRef = { String nodeRef -> true } } def setupData() { def user = User.get(1) def course = new Course(shortName: 'Test shortname', summary: 'Test summary', owner: user).save() } ... void "test save method"() { given: setupData() ... } |
You have two possibilities:
|
DEPLOYMENT ON JENKINS
- Below I'll paste changes I had to do in order to migrate deployment to Grails 3 on Jenkins job
Grails 2 job | Grails 3 job | |
---|---|---|
GENERATE WAR FILE | grails war education.war | grails war |
RUN TESTS | grails test-app -coverage -xml | grails test-app |
GENERATE DOC | grails doc | grails docs |
PUBLISH COVERAGE REPORTS | target/test-reports/cobertura/coverage.xml | build/reports/cobertura/coverage.xml |
DEPLOY TO TOMCAT | (no changes) | (no changes) |
(no changes) | (no changes) |
- On Grails 2 war file was generated under project root path but on Grails 3 war file is generated in build / libs path so I had to add some lines to change path in build.gradle
// build.gradle file war { archiveName "${baseName}.war" // This line for not adding version to war file name destinationDir project.projectDir // This line for setting path to project root path and not in build / libs } |
- When deploying to Tomcat Jenkins threw this weird error
java.lang.NoSuchMethodError: org.apache.tomcat.util.res.StringManager.getManager(Ljava/lang/Class;)Lorg/apache/tomcat/util/res/StringManager; |
After googling a little bit, I figured out it was due to use different Tomcat versions among embed Tomcat used by Spring Boot (version 8) and Tomcat used by Jenkins (version 7). As a workaround I changed this
// build.gradle file compile "org.springframework.boot:spring-boot-starter-tomcat" to provided "org.springframework.boot:spring-boot-starter-tomcat" |
Further info -> http://www.tothenew.com/blog/grails-3-and-deployment-to-tomcat-container/ |
- To keep using coverage plugin as of now you can use Gradle plugin. For it the only thing you need to do is to add some lines to build.gradle file
buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath "net.saliman:gradle-cobertura-plugin:2.4.0" } } apply plugin: "net.saliman.cobertura" |
Further info -> https://plugins.gradle.org/plugin/net.saliman.cobertura |
- Also you will need to add cobertura after running tests
// Cobertura configuration cobertura { coverageFormats = ['xml'] coverageExcludes = ['.*Application.*', '.*BootStrap.*', '.*UrlMappings.*'] } test.finalizedBy(project.tasks.cobertura) |
INTELLIJ IDEA 2017
- Execute all tests under JUnit not under Grails environment, otherwise Intellij IDEA will execute all tests.
- Sometimes
Intellij IDEA executes the test under production or development env
instead of test env. To amend it you just have to add
-Dgrails.env=production to command line from Run / Debug configuration
// Test log trace The following profiles are active: production |
- Sometimes test throws an error related to Cannot add Domain Class [class ...] It is not a Domain!. To fix it,
grails clean |
SPRING SECURITY CORE
- Our Config.groovy in Grails 2 project contained all configuration properties related to plugin. As of now we will have to create a new file called application.groovy to put them in
// Added by the Spring Security Core plugin: grails.plugin.springsecurity.debug.useFilter = true grails.plugin.springsecurity.securityConfigType = "Annotation" grails.plugin.springsecurity.logout.postOnly = false grails.plugin.springsecurity.userLookup.userDomainClassName = "net.rdcstudios.core.security.User" grails.plugin.springsecurity.userLookup.authorityJoinClassName = "net.rdcstudios.core.security.UserRole" grails.plugin.springsecurity.authority.className = "net.rdcstudios.core.security.Role" grails.plugin.springsecurity.adh.errorPage = null grails.plugin.springsecurity.rejectIfNoRule = false grails.plugin.springsecurity.fii.rejectPublicInvocations = false grails.plugin.springsecurity.controllerAnnotations.staticRules = [ [pattern: '/', access: ['permitAll']], [pattern: '/index', access: ['permitAll']], [pattern: '/index.gsp', access: ['permitAll']], [pattern: '/assets/**', access: ['permitAll']] ] grails.plugin.springsecurity.useSecurityEventListener = true |
- Plus as we need to load an event related to plugin at runtime, we will create other file called runtime.groovy to define it
grails.plugin.springsecurity.onInteractiveAuthenticationSuccessEvent = { e, appCtx -> // Setting language by country and language fields from user domain class net.rdcstudios.core.security.User.withSession { net.rdcstudios.core.security.User user = net.rdcstudios.core.security.User.get(e.source.principal.id) def languageCode = user?.language?.code def countryCode = user?.country?.code def request = grails.plugin.springsecurity.web.SecurityRequestHolder.getRequest() if (languageCode && countryCode && request) { def locale = new Locale(languageCode, countryCode) if (locale) { org.grails.web.util.WebUtils.setSessionAttribute(request, org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale) } } } } |
DATABASE MIGRATION
- We could not install the new plugin for Grails 3 as we were working with Java 7 and app threw this error,
Unsupported major.minor version 52.0 INFO J2SE 8 = 52 J2SE 7 = 51 J2SE 6.0 = 50 J2SE 5.0 = 49 JDK 1.4 = 48 JDK 1.3 = 47 JDK 1.2 = 46 JDK 1.1 = 45 |
EXPORT
- I found an error to display export buttons. In Grails 2 projects you set it by adding Resource plugin tag
<r:require module="export"/> |
<asset:stylesheet src="application.css"/> |
- Plus I had to add the following line to build.gradle
assets { minifyJs = true minifyCss = true packagePlugin = true } |
ASSET PIPELINE DOCUMENTATION
Plugins also can have the same "grails-app/assets" folder and their URL mapping is also the same. This means it can be more important to ensure unique naming / path mapping between plugins. This is also powerful in the sense that a plugin can add helper manifests to be used within your apps like jquery, bootstrap, font-awesome, and more.
Plugins should make sure that the
assets { packagePlugin } property is set to true in their build.gradle file otherwise assets will not properly be packaged into the plugin for use by the application. |
JASPER
- I found no plugin compatible for Grails 3 so I had to do the following:
- Import JasperController to my controllers folder
- Import JasperService to my services folder
- Import JasperTagLib to my taglib folder (changing all g.resource references to asset.assetPath)
- Import all Jasper images to my assets/images folder
- Import JasperExportFormat and JasperReportDef to src/main/groovy
- Plus I had to add the following lines to build.gradle
// Jasper compile "com.lowagie:itext:2.1.7" compile("net.sf.jasperreports:jasperreports:5.6.1") { exclude group: 'antlr', module: 'antlr' exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'org.apache.ant', module: 'ant' exclude group: 'mondrian', module: 'mondrian' exclude group: 'commons-javaflow', module: 'commons-javaflow' exclude group: 'net.sourceforge.barbecue', module: 'barbecue' exclude group: 'xml-apis', module: 'xml-apis' exclude group: 'xalan', module: 'xalan' exclude group: 'org.codehaus.groovy', module: 'groovy-all' exclude group: 'org.hibernate ', module: 'hibernate' exclude group: 'javax.xml.soap', module: 'saaj-api' exclude group: 'javax.servlet', module: 'servlet-api' exclude group: 'org.springframework', module: 'spring-core' exclude group: 'org.beanshell', module: 'bsh' exclude group: 'org.springframework', module: 'spring-beans' exclude group: 'jaxen', module: 'jaxen' exclude group: 'net.sf.barcode4j ', module: 'barcode4j' exclude group: 'org.apache.xmlgraphics', module: 'batik-svg-dom' exclude group: 'org.apache.xmlgraphics', module: 'batik-xml' exclude group: 'org.apache.xmlgraphics', module: 'batik-awt-util' exclude group: 'org.apache.xmlgraphics', module: 'batik-dom' exclude group: 'org.apache.xmlgraphics', module: 'batik-css' exclude group: 'org.apache.xmlgraphics', module: 'batik-gvt' exclude group: 'org.apache.xmlgraphics', module: 'batik-script' exclude group: 'org.apache.xmlgraphics', module: 'batik-svggen' exclude group: 'org.apache.xmlgraphics', module: 'batik-util' exclude group: 'org.apache.xmlgraphics', module: 'batik-bridge' exclude group: 'javax.persistence', module: 'persistence-api' exclude group: 'eclipse', module: 'jdtcore' exclude group: 'org.olap4j', module: 'olap4j' } compile "org.apache.poi:poi:3.10-FINAL" compile "commons-io:commons-io:2.5" |
BULK DATA IMPORTS
- I found no plugin compatible for Grails 3 so I had to do the following:
- Import ImportsController to my controllers folder
- Import ImportsService to my services folder
- Import DefaultImporter and ImportsException to src/main/groovy. Plus logging folder (with all Logguers) to src/main/groovy too.
- Plus I had to copy all code from doWithSpring closure of BulkDataImportsGrailsPlugin in Grails 2 to doWithSpring closure of Application in Grails 3
class Application extends GrailsAutoConfiguration implements EnvironmentAware { static void main(String[] args) { GrailsApp.run(Application, args) } @Override void setEnvironment(Environment environment) { def file = new File("/etc/core/core-${grails.util.Environment.getCurrent().name}-config.properties") if(file.exists()) { def config = new ConfigSlurper().parse(file.text) environment.propertySources.addFirst(new MapPropertySource(grails.util.Environment.getCurrent().name, config)) } } @Override Closure doWithSpring() { def loggingProvider = Holders.config.grails.plugins.imports.containsKey('loggingProvider') ? Holders.config.grails.plugins.imports.loggingProvider : 'default' def loggingProviderClass = null if (loggingProvider == 'mongo') { loggingProviderClass = MongoLogger } else if (loggingProvider == 'mem') { loggingProviderClass = InMemoryLogger } else if (loggingProvider == 'default') { loggingProviderClass = DefaultLogger } else { Class clazz = Class.forName(loggingProvider, true, Thread.currentThread().contextClassLoader) loggingProviderClass = clazz } def beans = { importsLogger(loggingProviderClass) } for(service in Holders.grailsApplication.serviceClasses) { if (service.hasProperty('imports')) { def entityName, imports = service.getPropertyValue('imports') if (imports instanceof Class || imports instanceof String) { entityName = GrailsNameUtils.getPropertyName(imports) } if (entityName) { def found = Holders.grailsApplication.domainClasses?.find { GrailsNameUtils.getPropertyName(it.name) == entityName} != null if (!found && !service.hasMetaMethod('processRow', getArgs(5)) ) { log.warn('\n BulkDataImports: could not configure importer '+service.shortName+'... no domain class found and missing processRow method') } else { DefaultImporter.SERVICE_METHODS.each { k, v-> if (!service.hasMetaMethod(k, getArgs(v))) service.metaClass."${k}" = DefaultImporter."${k}" } def props = DefaultImporter.SERVICE_PROPERTIES.clone() props.entityName = entityName props.each {k, v-> if (!service.hasMetaMethod(k)) service.metaClass."${k}" = { -> v } } ImporterService.IMPORT_CONFIGURATIONS[entityName] = GrailsNameUtils.getPropertyNameRepresentation(service.shortName) } } else { log.warn('\n BulkDataImports: invalid imports configuration for '+service.shortName+' :'+imports) } } } ImporterService.IMPORT_CONFIGURATIONS.each { k, v-> log.info('\n BulkDataImports:'+ k + ' imported by '+v) } return beans } private getArgs(ct) { (1..ct).collect { Object.class }.toArray() } } |
Hope this helps.