Saturday, October 3, 2015

Grails 3 : add a bootstrap plugin to your application


I started to learn Grails 3 by using Netbeans 8.Today, I wanted to add bootstrap to my Grails 3 project.I decided to use a bootstrap plugin.The ways to do it is a little different than in Grails 2 ...

First, you have to search your plugin.There are located on Bintray.Here is the web page I used to configure the bootstrap plugin for my web application : https://bintray.com/kensiprell/gradle-plugins/bootstrap-framework/view#read


Configure build.gradle


You have to put dependencies in build.gradle :

buildscript {
    ext {
         //bootstrapFramework = [
         //    version             : "3.3.5",
         //    cssPath             : "grails-app/assets/stylesheets",
         //    jsPath              : "grails-app/assets/javascripts",
         //    useIndividualJs     : false,
         //    useLess             : false,
         //    invalidVersionFails : false,
         //    fontAwesome : [
         //       install             : false
         //       version             : "4.3.0",
         //       useLess             : false
         //       invalidVersionFails : false
         //    ]
         //]
    }
    repositories {
        jcenter()
    }
    dependencies {
        classpath "com.siprell.plugins:bootstrap-framework:1.0.3"
    }
}


apply plugin: "com.siprell.plugins.bootstrap-framework"


main.gsp


Then just add the require dependencies jquery, bootstrap in your GSP.
Here is an example with a classic navbar :


<html lang="en" class="no-js">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title><g:layoutTitle default="Grails"/></title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <asset:javascript src="jquery-2.1.3.js"/>
        <asset:javascript src="bootstrap-all.js"/>
        <asset:stylesheet src="bootstrap-all.css"/>
        <asset:stylesheet src="font-awesome-all.css"/>
        <g:layoutHead/>
    </head>
    <body>
    <nav class="navbar navbar-default">
                <div class="container-fluid">
                  <!-- Brand and toggle get grouped for better mobile display -->
                  <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                      <span class="sr-only">Toggle navigation</span>
                      <span class="icon-bar"></span>
                      <span class="icon-bar"></span>
                      <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="#">Brand</a>
                  </div>

                  <!-- Collect the nav links, forms, and other content for toggling -->
                  <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                      <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
                      <li><a href="#">Link</a></li>
                      <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                          <li><a href="#">Action</a></li>
                          <li><a href="#">Another action</a></li>
                          <li><a href="#">Something else here</a></li>
                          <li role="separator" class="divider"></li>
                          <li><a href="#">Separated link</a></li>
                          <li role="separator" class="divider"></li>
                          <li><a href="#">One more separated link</a></li>
                        </ul>
                      </li>
                    </ul>
                    <form class="navbar-form navbar-left" role="search">
                      <div class="form-group">
                        <input type="text" class="form-control" placeholder="Search">
                      </div>
                      <button type="submit" class="btn btn-default">Submit</button>
                    </form>
                    <ul class="nav navbar-nav navbar-right">
                      <li><a href="#">Link</a></li>
                      <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                          <li><a href="#">Action</a></li>
                          <li><a href="#">Another action</a></li>
                          <li><a href="#">Something else here</a></li>
                          <li role="separator" class="divider"></li>
                          <li><a href="#">Separated link</a></li>
                        </ul>
                      </li>
                    </ul>
                  </div><!-- /.navbar-collapse -->
                </div><!-- /.container-fluid -->
            </nav>
        <g:layoutBody/>
    </body>

   

Running Gradle task


In NetBeans, I run the clean and assemble Gradle task to download and install your plugin.You have to go in project tab and made right clic then choose task ( clean or assemble ).It will download and install the plugin.Then restart your application.


   
   
   
   

Grails 3 and Netbeans 8.0.2 : warning about Gradle 2.3 compatibility


I had a warning in Netbeans IDE 8.0.2 about Gradle 2.3 compatibility.I followed the following steps to clear this warning. In files tab, I did a right clik on my project.I choose properties and Gradle project Categories.Then I changed the Gradle home to my Gradle 2.7 directory like the following :






And the annoying warning disappeared !

Saturday, September 26, 2015

Eclipse plugin :no menu in Extension when right clicks


I'm looking at eclipse plugin and I met a problem in extension view : I can't access to the extension menu when I did right click.

I have also the following warning in Extensions View : No schema found for the “org.eclipse.ui.menus” extension point

To solves it,  I add Eclipse RCP Target Components ( see http://stackoverflow.com/questions/8366303/no-schema-found-for-the-org-eclipse-ui-menus-extension-point )

  1. In Eclipse, go to Help > Install New Software... 
  2. Update site: http://download.eclipse.org/eclipse/updates/4.5
  3. Choose "Eclipse RCP Target Components" and press Next to install them

Now the menu appears :




Eclipse IDE for Java Developers
Version: Mars Release (4.5.0)














Sunday, September 20, 2015

Grails 3 and IDE support


I'm looking at support of grails 3 by the IDE.
To now, here is an overview :
  • Eclipse : none support, plug-in disappeared from STS.
  • Intellij : appears to be support but you to pay licensing ...
  • Netbeans : begins of support
The good point is Netbeans 8.0.2 recognizes Grails 3 project.So, i was able to create a new Grails 3 project.You can launch you application and there is also syntax highlights in GSP.
However, it's just the beginning and it needs some works.But, you can begin to practice with Grails 3.

https://stackoverflow.com/questions/29441503/grails-3-0-support-in-netbeans/32425071#32425071

One comment, under Eclipse, you can import Grails project as a Gradle project like said in documentation.But, I didn't find how to have syntax highlights in GSP.

Friday, September 18, 2015

Upgrade Grails 2.5.0 to 2.5.1

I successfully upgrade Grails to the version 2.5.1It's quite simple.You only have to follow the release note about plugins update.
I met only one problem.I had this warning : Script 'Upgrade' not found.

To solve that issue, you have to select 3 (SetGrailsVersion) ant it will make the update.

|Running pre-compiled script
|Script 'Upgrade' not found, did you mean:
   1) MigrateDocs
   2) IntegrateWith
   3) SetGrailsVersion
   4) InstallDependency
   5) DependencyReport
Please make a selection or enter Q to quit: 3


With this update, I solved the following error : http://stackoverflow.com/questions/32562678/grails-plugin-error-class-path-resource-cannot-be-resolved

Wednesday, September 16, 2015

How to import files in GRAILS ?

In business web site, we often used to import config files.There are many way to achieve this in GRAILS.In this paper, we will talk about two possibilities but not about batch because it doesn't work for me in GRAILS 2.5.0.

An IHM to import file

You can import files with a classical html form like the following :








A form example : form.gsp

Below, here is an example of Grails upload form by using g:uploadForm tags.This form, when submit, calls the init action from the admin controller.It passed files in parameter with Input tag.The input file with the parameter multiple will permit to upload severeal files.

<g:uploadForm action="init" controller="admin">
    <form role="form">
        <div class="form-group">
            <label for="files">Choix du fichier à parser</label>
            <input type="file" id="files" name="files[]" multiple>
            <p class="help-block">Vous devez choisir un fichier httpd.conf</p>
        </div>
        <div class="form-group">
            <label for="machinename">Nom de la machine</label>
            <input type="text" class="form-control" id="machinename" name="machinename"      placeholder="....ac-limoges.fr" value="">
        </div>
        <button type="submit" class="btn btn-info">Submit</button>
    </form>
</g:uploadForm>





Controller


For example, to parse a file, you could do the following :
      
class AdminController {
  
   def init() {
        if (request instanceof MultipartHttpServletRequest) {
            ...
            request.getFiles("files[]").each { file ->
                log.info("init() file to parse:" + file.originalFilename)
                       InputStream inputStream = file.inpustream
                       BufferedReader = new BufferedReader(new InputStreamReader(inputStream))

                        try {
                            String ldapUser = EMPTY
                            String ldapPwd = EMPTY
                            String ldapHost = EMPTY
                            String ldapPort = EMPTY

                            while ((line = br.readLine()) != null) {
                            ...
                            }
                        }
            }
            ...
        }
    }

...

The arry files[] will contain all import files.You have to notice that file is type of org.springframework.web.multipart.MultiPartFile and not java.io.File


Import files automaticaly


An other way, is to import files automaticaly. You just have to do a method in the controller like the following :

class AdminController {
    def reloadData() {
        def path = "E:\\projet\\toolprod\\import\\"

        log.info("reloadData() Initializing application from httpd.conf files in machine directory ...")
        new File(path).listFiles().findAll{
            if (it.isDirectory()) {
                log.info("reloadData() Machine name ( Directory name ) :" + it.name)
                String machineName = it.name
                log.debug("reloadData() files list:" + it.listFiles())
                for (File f : it.listFiles()) {
                ...

            }

        }

...

Then you need to access at the following url : http://localhost:8080/appli/admin/reloadData
After that, it's easy to make a crontab with a curl to make automatisation ...

Friday, April 24, 2015

Grails and Pdf creation

In my businness application, I needed to print report in a pdf file.Before Grails 2.5.0, I used export-plugin with success.I tried to use with Grails 2.5.0 but I obtained compilation errors.That's why I looked for other solutions.
I found rendering plugin but I had an error when rendering ( see http://stackoverflow.com/questions/29694796/null-pointer-exception-with-grails-rendering-plugin ).
Finaly, I decided to do it myself with apache pdfbox

In my BuildConfig.groovy, I added the dependency :

   dependency {  
     ...  
     compile "org.apache.pdfbox:pdfbox:1.8.9"  
     ...  
   }  

And here is the code in my controller :

 class AppRetailController {  
   ...  
   def renderFormPDF(){  
     List<App> apps = new ArrayList<>()  
     String title = ""  
     String param = params.get("serverSelect")  
     ...  
     Fill the list apps  
     ...  
           title= "Liste de toutes les applications sur " + param  
           title += " (" + apps.size() + ")"  
           // Create all needed for Pdf document  
     PDDocument document = new PDDocument();  
     PDPage page = new PDPage();  
     PDPageContentStream contentStream;  
           // Here I want 30 rows by page  
     int pageNumber = apps.size()/30 + 1  
     log.info("Page number :" + pageNumber)  
     int countApp = 0  
           // for each page I draw table with datas  
     for (int i=1; i<=pageNumber; i++) {  
       log.info("countApp :" + countApp)  
       page = new PDPage();  
       contentStream = new PDPageContentStream(document, page);  
       int max = countApp + 30  
       if (max > apps.size()) {  
         max = apps.size() - 1  
       }  
       drawTable(page, contentStream, 700, 100, apps[countApp..max], title);  
       countApp = countApp + 30 + 1  
       contentStream.close();  
       document.addPage(page);  
     }  
     document.save("report.pdf");  
     document.close();  
     render( file:new File("report.pdf"), fileName: "report.pdf")  
   }  
   /**  
    * @param page  
    * @param contentStream  
    * @param y the y-coordinate of the first row  
    * @param margin the padding on left and right of table  
    * @param content a 2d array containing the table data  
    * @throws IOException  
    */  
   def drawTable(PDPage page, PDPageContentStream contentStream,  
                  float y, float margin,  
                  List<App> apps, String title) throws IOException {  
     final int rows = apps.size() + 1;  
     final int cols = 3;  
     final float rowHeight = 20f;  
     final float tableWidth = page.findMediaBox().getWidth()-(2*margin);  
     final float tableHeight = rowHeight * rows;  
     final float colWidth = tableWidth/(float)cols;  
     final float cellMargin=5f;  
     //draw the rows  
     float nexty = y ;  
     for (int i = 0; i <= rows; i++) {  
       contentStream.drawLine(margin,nexty,(float)(margin+tableWidth),nexty);  
       nexty-= rowHeight;  
     }  
     //draw the columns  
     float nextx = margin;  
     for (int i = 0; i <= cols; i++) {  
       contentStream.drawLine(nextx,y,nextx,(float)(y-tableHeight));  
       nextx += colWidth;  
     }  
     //now add the text  
     contentStream.setFont(PDType1Font.HELVETICA_BOLD,14);  
     contentStream.beginText();  
     contentStream.moveTextPositionByAmount((float)margin+cellMargin+10,(float)(y+20));  
     contentStream.drawString(title);  
     contentStream.endText();  
     contentStream.setFont(PDType1Font.HELVETICA_BOLD,12);  
     float textx = margin+cellMargin;  
     float texty = y-15;  
     //Define colunm title  
     contentStream.beginText();  
     contentStream.moveTextPositionByAmount(textx,texty);  
     contentStream.drawString("Nom");  
     contentStream.endText();  
     textx += colWidth;  
     contentStream.beginText();  
     contentStream.moveTextPositionByAmount(textx,texty);  
     contentStream.drawString("Description");  
     contentStream.endText();  
     textx += colWidth;  
     contentStream.beginText();  
     contentStream.moveTextPositionByAmount(textx,texty);  
     contentStream.drawString("Chemin dans ARENA");  
     contentStream.endText();  
     textx += colWidth;  
     texty-=rowHeight;  
     textx = margin+cellMargin;  
     contentStream.setFont(PDType1Font.HELVETICA,11);  
     apps.each {  
       contentStream.beginText();  
       contentStream.moveTextPositionByAmount(textx,texty);  
       contentStream.drawString(it.name);  
       contentStream.endText();  
       textx += colWidth;  
       contentStream.beginText();  
       contentStream.moveTextPositionByAmount(textx,texty);  
       String desc = it.description  
       if (desc.equals("EMPTY")) {  
         desc = ""  
       }  
       contentStream.drawString(desc);  
       contentStream.endText();  
       textx += colWidth;  
       contentStream.beginText();  
       contentStream.moveTextPositionByAmount(textx,texty);  
       String path = it.arenaPath  
       if (path == null) {  
         path = ""  
       }  
       contentStream.drawString(path);  
       contentStream.endText();  
       textx += colWidth;  
       texty-=rowHeight;  
       textx = margin+cellMargin;  
     }  
   }  
 }  


So, this is not the best solution.I would have preferred something like rendering plugin but it's works and I needed it immediately.A big thanks to this blog for the use of pdf library : http://fahdshariff.blogspot.fr/2010/10/creating-tables-with-pdfbox.html 

Hope this help,

See also : http://grails.github.io/grails-doc/2.5.x/ref/Controllers/render.html

Friday, April 3, 2015

Grails : an example of remoteFunction with two parameters


In my businness web application, I wanted to use a selectbox which call a controller method to check mail address stores in ldap. On change of selectbox, it will automaticaly check if the mail exists in ldap.In this paper, we will talk about the use of remoteFunction with 2 parameters.I wrote it because I had difficulties with this ( especially the syntax ) and I hope it could help someone !

The gsp


In my gsp, I had an input text and a select box.The remoteFunction calls the action of the controller ajaxCheckMailInLDAP. Parameters are passed in params. ( Be carefull at the syntax ).Once achieved, the javascript method checkLdapMail(data) called.

example

// Show the result
 <div id="create_status_ok" class="alert alert-success" role="alert" style="display: none">
        <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
        <span class="sr-only">Info:</span>
        Vérification dans le LDAP OK ! vous pouvez créer cette adresse.
 </div> 
 ...
 
// form that use remoteFunction 
<form>
   <input type="text" class="form-control" id="rne" name="rne" placeholder="....087XXX" value="">  
   ...  
   <g:select noSelection="['0':'Choisir un type de boîte']" optionKey="id" optionValue="fullNameType" name="shortNameType" id="selecttype" from="${toolprod.MailType.list()}"
    onchange="
    cleanMsg();
    ${remoteFunction(
       controller:'tools',
       action:'ajaxCheckMailInLDAP',
       params:'\'id=\' + this.value + \'&rne=\' + document.getElementById(\'rne\').value',
       onSuccess:'checkLdapMail(data)')}"
    ></g:select> 
 </form>  


The controller's action


Here is my controller wich render an array as JSON.Note that the name of data variable is important !

 class ToolsController {  
 ...  
   def ajaxCheckMailInLDAP = {  
        log.info("ajaxCheckMailInLDAP()")
 
       String mail = params.rne + "@.."

        // Init JSON data
        def data = [:]
        data.put("id", "1")
        if (checkMail(mail)) {// just return a boolean
            data.put("text", "KO")
        } else {
            data.put("text", "OK")
        } 
        data.put("mail", mail)
        log.info("ajaxCheckMailInLDAP() END" + mail)
        render data as JSON 
   }  
 ....  
 }  

To finish, below is the javascript method :

    /**
     * Show result stored in data on html page
     * @param data JSON data send by ajaxCheckMailInLDAP in controller.
     */
    function checkLdapMail(data) {
        console.log("checkLdapMail()");

        document.getElementById('selecttype').value=0;
        document.getElementById('create_status_ko').style.display='none';
        document.getElementById('create_status_ok').style.display='none';
        document.getElementById('create_status_info').style.display='block';

        if (data.text == "KO") {
            document.getElementById('create_status_info').style.display='none';
            document.getElementById('create_status_ko').style.display='block';
            document.getElementById('create_status_ko').textContent= data.mail.toString() + " existe deja dans le LDAP !";
        }
        if (data.text == "OK") {
            document.getElementById('create_status_info').style.display='none';
            document.getElementById('create_status_ok').style.display='block';
            document.getElementById('btnSubmit').disabled = false;
        }

        if (data.text == "EMPTY") {
            document.getElementById('create_status_info').style.display='none';
            document.getElementById('create_status_ko').style.display='block';
            document.getElementById('create_status_ko').textContent= "le rne est vide !";

        }
        console.log("checkLdapMail() : end");
    }

To make it works, I use the parameter name data.An other important things, I used Grails 2.4.4 to make this example.
I hope it will help you in your developpement.



Tuesday, March 31, 2015

Upgrade Grails 2.4.4 to 2.5.0

I successfully upgraded Grails 2.4.4 to 2.5.0 by following the Release Version text files ( https://github.com/grails/grails-core/releases/tag/v2.5.0 ).

I only upgraded plugins version in BuildConfig.groovy:

  • build ':tomcat:7.0.55.2'
  • compile ':cache:1.1.8'
  • compile ':scaffolding:2.1.2'
  • compile ':asset-pipeline:2.1.5'

I also met an exception because I forget to change cache.region.factory_class in Datasource.groovy ( cf Release Version ).
Here is the exception :

2015-03-31 16:28:39,590 [localhost-startStop-1] ERROR context.GrailsContextLoaderListener  - Error initializing the application: Error creating bean with name 'transactionManagerPostProcessor': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'transactionManager_lookup' while setting constructor argument with key [1]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager_lookup': Cannot resolve reference to bean 'sessionFactory_lookup' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory_lookup': Invocation of init method failed; nested exception is org.hibernate.cache.CacheException: net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: DefaultConfigurationSource [ ehcache.xml or ehcache-failsafe.xml ]
...

To solves it, I only have to do the following change in Datasource.groovy :

hibernate {
    ...
    cache.region.factory_class = 'org.hibernate.cache.SingletonEhCacheRegionFactory'
}

Tuesday, January 13, 2015

Grails update successfully from 2.3.11 to 2.4.4

I successfully update Grails from 2.3.11 to 2.4.4 by following https://grails.org/2.4.4+Release+Notes and http://grails.org/doc/latest/guide/upgradingFrom23.html. It was not a simple update due to a substancial existing web site.I had error at startup and many things to do to make it works.
So, here are some advices to help to complete this task :

Upgrade plugins

Be carefull, if you don't upgrade plugins like cache, hibernate, jquery, you will have stranges errors.

Plugin asset-pipeline replace resource plugin

You will have to move directory and organize them in assets directory and also change all include in GSP.Possibles surprises will perhaps appear : see http://stackoverflow.com/questions/27906139/grails-assets-directory-management

Plugin export:1.6

To now, it doesn't work for me

Templates

I delete templates directory end recreate it by command : grails install templates.
It fixes some errors.

Other 

There is a new annotation : @Validateable(nullable = true)