Problem

Need to generate QRCode and as many as possible BarCodes based on specific code.

Solution

After looking to several plugins like qrcode and barcode4j and after unsuccessful usage of them I’ve decided to do it myself and found out that it is easy enough.

Step 1 – Add necessary libs

After looking to several frameworks which allow generate BarCode for free I stopped on zxing as it support QRCode and several most used in the world BarCodes.
For the moment I used it version was 1.7 and supported BarCodes:

  • EAN_8
  • EAN_13
  • QR_CODE
  • UPC_A
  • CODE_39
  • CODE_128
  • ITF

Currently zxing 2.0 is already available, so probably that list of supported BarCodes extended.
Libs available in SonaType repository. I used two modules: core and javase.

Step 2 – Add Service to generate BarCodes

After libs are in place just create very simple Service like following:

Now you need to specify barcode format, data (number or text) and image size (width and height) and can easily render barcode from any taglib or controller like:

Add some AJAX if you want to reload BarCode on the fly…

 

Have multiple domain which connected with each other. Need to provide deep search functionality, so that need to search in Master domain’s 3-d and 4-th level children. For example we have structure like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
  static hasMany = [clothes:Cloth]
...
}
 
class Cloth {
  Color color
...
}
 
class Color {
  String name
  ...
}

And we have several Persons with different sets of Clothes in different Colors.
We want to find all people who has red Cloth. It can be often the case that Same Person has several Clothes that are red.
So query like:

1
2
3
4
5
6
7
8
9
10
11
def color = params.color
Person.createCriteria().list{
  clothes {
    color {
      ilike('name', "%${color}%")
    }
  }
  maxResults(params.max as Integer)
  firstResult(params.offset as Integer)
  cache true  
}

Will return paged results but with several same people. So we need to use distict list to have correct results.
BUT!
Grails docs states that

The listDistinct() method does not work well with the pagination options maxResult and firstResult. If you need distinct results with pagination, we currently recommend that you use HQL.

And honestly I do not like HQL very much.
So after some time of playing wit Google, Hibernate and grails DSL, solution was found:

1
2
3
4
5
6
7
8
9
10
11
12
13
Person.createCriteria().list {
  projections {
    groupProperty('id')
  }
  clothes {
    color {
      ilike('name', "%${color}%")
    }
  }
  maxResults(params.max as Integer)
  firstResult(params.offset as Integer)
  cache true
}.collect {Person.get(it[0])}

Maybe a little ugly but resolve problem with pagination on distinct elems 100%.

 

For Ajax file uploading I’m using Andrew Valums AJAX file uploader.

On UI I added jQuery script like:

jQuery(function() {
     var uploader = new qq.FileUploaderBasic({
       button: document.getElementById('addButton'),
       action: "${createLink(controller: 'upload', action: 'uploadImage')}",
       multiple: true,
       allowedExtensions:['PNG', 'JPG', 'JPEG', 'GIF'],
       sizeLimit:4194304, // max 4GB file
       onSubmit:function(){jQuery('#spinner').show()},
       onComplete:function(id, fileName, responseJSON){
         reloadImage("urlHolder", responseJSON[0])
       }
     });
  });

Where reloadImage is just another AJAX call which should show loaded image based on location where it was uploaded.
And uploadImage action of UploadController looks like

import org.springframework.web.multipart.commons.CommonsMultipartFile
 
def uploadImage = {
        def fileName
        def inputStream
        if (params.qqfile instanceof CommonsMultipartFile) {
            fileName = params.qqfile?.originalFilename
            inputStream = params.qqfile.getInputStream()
        } else {
            fileName = params.qqfile
            inputStream = request.getInputStream()
        }
        //To avoid problems with spaces
        fileName = fileName.replaceAll(" ", "_")
 
        File storedFile = new File(_some_specfic_path_)
 
        storedFile.append(inputStream)
 
        def result = [fileName] as JSON
        render text: result.toString(), contentType: 'text/html'
    }

Main points here are

  • you should not use request.getFile('qqfile') as it is not working under Safari – need to work with inputStream.
  • contentType: ‘text/html’. If using correct ‘text/json’ not working under IE
 

Problem

We have grails project which is already running in PROD without using database migration/liquibase plugins.
Need to do changes in database structure without destroying of existing data.

Solution

Let’s call database states:

  • state A is current state of database
  • state B new state of database, which we want to get in result

First of all need to have locally old version of project which is working on databases A and also new version of project which is working on database B.
It can be same one just without changes in domain model.

First need to generate changelog for database A.

Going to old project structure and install plugin first of all

grails install-plugin database-migration

After need generate changelog (I prefer groovy file)

grails dbm-generate-changelog changelog.groovy

and change Config.groovy to load data with changelog always

grails.plugin.databasemigration.updateOnStart = true
grails.plugin.databasemigration.updateOnStartFileNames = ['changelog.groovy']

But database is already exists and we do not want to re-create it from scratch neither on local install nor in PROD. So we use

grails dbm-changelog-sync-sql

After this we get SQL commands to execute on local and PROD db.

Migrate database from state A to state B

Now we can make all changes we need on our domain structure. After this run command

grails dbm-gorm-diff addition.groovy

Then put data from addition.groovy to changelog.groovy (I prefer just copy/paste to the end of file and remove addition.groovy after this)
And simple grails run-app your local app and create/deploy war for PROD (do not forget to execute SQL from previous step first!)

Hope this helps someone…

© 2012 REID Consulting Suffusion theme by Sayontan Sinha