2009-11-17

Creating a Sonar Plugin for software development metrics

As you know, at Zauber we follow high quality standards, that's why we have obtained an ISO 9001:2008 Quality Certification. Part of following these standards implies measuring the performance of our company, getting metrics. Obtaining metrics from our development process allow us to understand our current situation, compare it with previous ones and set new objectives for the future.

Until now, we had three main applications which allowed us to collect information about our projects:

However, there was a problem, these applications didn't show us the metrics in a way that they could be studied through time, and Mantis was too dependent on developers' input, not always enforcing us to provide detailed information about the issues we were working on.(e.g.: sometimes people forget to fill in the 'worked hours' field for a certain issue).

Where to start?

We decided to start using Sonar, a quality management platform that allows analyzing and measuring our projects in a continuous way, displaying our metrics in a very fancy way. To cope with the flaws we mentioned of Mantis, we also developed an application that queries our issue tracker database finding incomplete issues that make references to bugs, reporting them to who may be responsible of them, as well as projects managers.

This way, Sonar became our software metrics dashboard, but we also wanted to include custom metrics collected by our own tool Crono and from Mantis. To achiveve this, we implemented a Sonar plug-in using the Sonar API. But many obstacles flourished on the road while we were implementing this plug-in, the main one was that the Sonar API is vaguely documented or not documented at all, so we had to check out how other plug-ins where implemented, so after studying other plugin’s implementations here is what we've learned

As you can see sonar is somehow similar to the MVC pattern, except that we don't have much access to the logic beneath it. There are some classes you must use in order to make the plugin work

  • The 'Plugin' class which tells the server all the classes this plugin will use.
  • The 'Metric' class which is a list with all the new ways to measure each project, also it references all the information that the view may show
  • The 'Widget' class in this case 'RubyRailsWidget' just gives the location of the Ruby template made on eRuby to render in the sonar page
  • The 'Sensor' and the 'Decorator' are the classes used to gather and process all the previous metrics, the difference between the Sensor and the Decorator is that the Sensor mainly collects information but has no access to what other plugins may collect. On the other side, the Decorator should be responsible for analyzing the information obtained and make cross reference to other information from other plugins.
  • The data that the sensor and the decorator gather is sent to the server through the 'context', which is similar to a "command" class in Spring MVC.

Except for the Sensor and the Decorator, the processes run on the Sonar server. The workflow is the following: First, the Plugin class tells Sonar which classes it uses. Then, when you access the project page, the server calls the widgets classes to obtain the template to render the data on the database. When Hudson runs maven, the latter runs the Sonar plugins, which analyze the project with all the sonar plugins currently installed. Then, the Sensor and the Decorator do their job, saving the metrics information through the Context (the Context will hold a measure for each metric in the class Metric).

What we did

We developed a plugin that uses the previously explained API and Crono REST API. When running it against a project it calculates the total amount of worked hours obtained from Crono, the total amount of rework hours taken from Mantis, and some other information (quantity and category of issues grouped by status). But it is necessary to configure certain settings before obtaining that information. There are two kinds of settings: global settings such as Mantis database URL, Mantis username, Crono URL, username, etc. These can be configured from the plugin settings in the sonar server, and project related settings, such as the project id in the Mantis database or the Crono project unixname in Crono, which are configured in each project maven's pom like these:

52
zauber
zauber-crono
2009-10-5

Where the start date means when the project started in ISO format. Once configured, when sonar analyzes a project, this plugin connects to Mantis and Crono, gets all the necessary information and stores it on the sonar server. After that, when we access a project page, the widget from our plugin is rendered and "voilá": we have our plugin results being displayed like this:

How will this help in the future?

We find this application very useful, because it gives us a complete software metrics dashboard for every project at a glance.

Main Dashboard, a quick view of every project (red column provided by our plugin). Tree-map: a tool that works perfectly when comparing projects on a specific subject. Timeline: a tool that lets you graphically see how the project evolves through time.

We hope that you found this article as useful as developing this plugin was for us. Feel free to contact us if you have any questions.

2009-07-14

Stay green: Javascript Unit Testing

With the improvement of our source code quality as a constant goal, we decided to find a way to write Unit Tests for our Javascript code. Our implementation of Apache Commons Lang Validate for Javascript was our first step to build more solid applications, but an important part was missing until now: unit testing. We found a way to integrate JS unit tests with Maven, Eclipse, and the continuous integration server Hudson.

Zauber Javascript Validation Utils
Is an open source library inspired on org.apache.commons.lang.Validate written by Zauber and published under the Apache License v2.0. You can download it from
  • https://code.zauber.com.ar/repos/sandbox/components/javascript/validate/code/releases/0.2/
  • How we test javascript code?
    We use an open source framework called JSUnit1.3, a port of JUnit. There are two frameworks with the same name (JSUnit), the one we are using, JSUnit1.3, uses Rhino to run the tests. The second one, JSUnit2, uses a HTML file called testRunner.html to run the tests on multiple browsers from client-side. This is great to do cross-browser compatibility tests, but for the moment we are just validating Javascript syntax and logic using the first toolkit.
    Project structure
    The main idea is to respect Maven's conventions, so our project structure looks like this:
       /app
          /src
           * /main
               o /java
               o /javascript
                   + validate.js (the library)
               o /resources
           * /test
               o /java
               o /javascript
                   + validateTest.js (tests for validate.js written in JavaScript)
               o /resources
           * /target
               o /surefire-reports
                   + TEST-JS-Validate.xml
           * pom.xml
     
    Configuration - pom.xml
    This file is the key for a successful configuration, you should declare the junit plugin for maven2, something like this:
       <plugin>
           <groupId>de.berlios.jsunit</groupId>
           <artifactId>jsunit-maven2-plugin</artifactId>
           <version>1.3</version>
           <extensions>true</extensions>
       </plugin>
       
    and then configure your tests, for example:
       <executions>
            <execution>
              <phase>test</phase>
              <goals>
                <goal>jsunit-test</goal>
              </goals>
              <configuration>
             
                 <!-- src -->
                 <sourceDirectory>src/main/javascript</sourceDirectory>
                 <sources>
                   <source>validate.js</source>
                 </sources>
                 
                 <!-- test -->
                 <testSourceDirectory>src/test/javascript</testSourceDirectory>
                 <testSuites>
                   <testSuite>
                     <name>JS-Validate</name>
                   </testSuite>
                 </testSuites>
                
                 <!-- report -->
                 <reportsDirectory>target/surefire-reports</reportsDirectory>
               </configuration>
             </execution>
       </executions>
       
    That's all, you can now run your JS test just calling maven as usual (ie. mvn test).
    $ mvn test
    [INFO] Scanning for projects...
    [INFO] ------------------------------------------------------------------------
    [INFO] Building Validation Utils
    [INFO]    task-segment: [test]
    [INFO] ------------------------------------------------------------------------
    [INFO] [resources:resources]
    [INFO] Copying 0 resource
    [INFO] [compiler:compile]
    [INFO] Nothing to compile - all classes are up to date
    [INFO] [resources:testResources]
    [INFO] Copying 0 resource
    [INFO] [compiler:testCompile]
    [INFO] Compiling 1 source file to: target/test-classes
    [INFO] [surefire:test]
    [INFO] Surefire report directory: target/surefire-reports
    ------------------------------------------------------
    T E S T S
    -------------------------------------------------------
    There are no tests to run.
    
    Results :
    Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
    
    [INFO] [jsunit2:jsunit-test {execution: default}]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 8 seconds
    [INFO] Finished at: Tue Jul 14 10:43:00 ART 2009
    [INFO] Final Memory: 18M/148M
    [INFO] ------------------------------------------------------------------------
    
    Note that maven says There are no tests to run but jsunit-test is being called.
    Configuration - Eclipse
    There is also another way to run your tests: calling them from Eclipse just like you do with regular Java tests. To do that, you could write your own implementation or include a module from Zauber Commons 3.16 called commons-javascript-test. We use the class ar.com.zauber.commons.test.javascript.JsTestCase. This class extends from JUnit TestCase, so it can be used just like a regular test case.
    So, how can we run it from Eclipse? A very simple example, step by step:
    1. write some Javascript code, like we did in validate.js
    2. write some tests for that code, you should follow this prototype
            function ValidateTest(name) {
                TestCase.call( this, name );
            }
            
            ValidateTest.prototype = new TestCase();
            
            //"import" validate class
            Validate = validate.Validate;
            
            /** 
             * this function tests Validate.notNull with many valid values and a null value 
             */
            ValidateTest.prototype.testNotNull = function(){
               var valids = new Array(0, 1, true, false, 'text', new Object(), 
                       new Array(1,2), function(){});
               var invalid = null;
            
               for(i in valids){ // tests all valid values
                   try{ 
                       Validate.notNull(valids[i]); // testing a not null value, shoudn't fail.
                   }catch(e){
                       fail("notNull shouldn't raise an exception with value:" + valids[i] + " (type: " + typeof(valids[i]) + ").");    
                   }
               }
            
               try{
                   Validate.notNull(invalid); // testing a null value, should fail.
                   fail("notNull should raise an exception with a null value.");
               }catch(e){
                   //notNull is working correctly
               }
            }
         

      Here you can use the code written in step one by "importing" it, just like we do in this example.

    3. Write a Java Class extending from JsTestCase, it will force you to implement a method called getIncludes(). That method returns a String array with the path for all files to be included in the test context, and all files that actually contain the tests. There is an important point here, and it is the name of this Java class. Normally you don't want this class to be executed by maven, so the class name should not end with Test, any other name will work, for example, we like to use Driver:
          public class ValidateDriver extends JsTestCase {
         
           /** @see JsTestCase#getIncludes() */
           @Override
           protected final String[] getIncludes() {
               return new String[] {
                   "src/main/javascript/validate.js",
                   "src/test/javascript/ValidateTest.js"
               };
           }
         
    4. Now, just click on Run As JUnit Test and you will see your tests running.
    5. Optionally, you can check the test reports on standard output. This can be achieved over all your tests by passing the following argument to the JVM: -Djavascript.test.showdetails=true or over an individual test by overriding the method showDetails() to return true.
    A working example
    You can checkout this project from our svn repository at https://code.zauber.com.ar/repos/sandbox/components/javascript/validate/code/releases/0.2/

    We hope you like it and use it ! Any feedback is always welcome.

    2009-04-14

    ControlFreak: Python Inversion of Control

    Back in 2007 I started programming in python. At first, the goal was to learn how to write programs the pythonic way, being my background a Java Enterprise programmer, I wanted to avoid programming java-like in python.

    We started building a web application, using Pylons, SQLAlchemy andp Mako as the base frameworks. At first we struggled with poor documentation (we were used to have lots of books about each framework in java), but as the time passed, we started feeling comfortable with python and its ways.

    The project became bigger and bigger, and it was at that time that we started missing Java and its "bureaucratic" way of coding. Don't get me wrong, bureaucracy is not a bad thing. The issue is unnecessary bureaucracy. We started missing static types, lack of module variables, long package names, and inversion of control to rule them all.

    At that time we started using "the java way of coding" in python, in order to make the project more maintainable. For example, we quit using the sqlAlchemy session as a module variable, and started injecting it. We erased every static reference to a module variable, and we also wanted to get good package names (at first they seem to be the same as java, but that's misguided).

    So, we needed an inversion of control (IoC) library and started to look for one.

    Why a new one?

    When we started looking for an IoC library, we didn't find one that filled ours needs. The exception was Spring-Python, but from our point of view, it was a port one on one of the Java Spring framework; and we didn't want something that complex.

    What we were looking for, was something that with much of the Spring IoC capabilities, but taking into consideration that this was python and not Java: There are things that are simpler to do in python, and other that are simpler to do in Java. Above all, we wanted to say "inject this instance on that other instance" in the simplest way possible. That was the reason we decided to do our own. I named it "ControlFreak"

    How and When?

    This is kind of a disclaimer, because ControlFreak was built on spare hours, spare and sparse hours. And it's something I would like to refactor, probably as a whole, when I found the time for it.

    Enough of introducction, let's me show what is controlfreak!

    ControlFreak: The library

    Controlfreak is a IoC library with capabilities similar to what spring does in Java. But there are some changes on it's design that are worth to mention. The source code can be found in a SVN repository

    . Also, controlfreak depends on pycommons, that can be found in this svn repo.

    Components

    When you code, you tend to think about interfaces and the different implementations of them. It's like you have a contract, and several providers of that contract.

    Discussing with a colleague of mine, we thought that it would be interesting to take that concept into the IoC configurations. So, that you won't be working just with beans, but with components. A component is a group of beans, that, one can think, provides (or exports) some beans. For example, a database component, will provide a session bean. And one could have different implementations of this database component. In order to take advantage of this metaphor is that components where created. A component, may require other component to work properly. For example, the DAO components will need the database component to function properly, no matter who or how is this other component "implemented"

    So, in controlfreak, the application is a bundle of components, wired together and configuration files for everything that is not beans.

    Python Static Beans

    When considering different alternatives to inject model objects, we arrived to a different approach to the one we would took in java.

    To inject model objects with services and others beans, one would need to intercept the model object creation, so as to let the IoC library to create the object. The ORM is the one usually creating model objects, so one would need to intercept the model creation there.

    When making queries that return hundreds of objects, objects that won't probably use any of their injected properties, one starts to wonder if the overhead of creating those objects with IoC makes sense. But that's a question I don't plan to answer here.

    Our solution, doesn't involve injecting model objects, because we end up injecting model classes. This is what we call static beans. Classes in python are the same as instances, just objects. So it's pretty easy to inject them with properties, not on creation, but afterwards.

    There's a catch on using class injection, instead of instance injection. The injected properties must be stateless, not dependant of the current instance that is calling them. But that's usually the case with services.

    ControlFreak ApplicationContext

    The ApplicationContext class is the same as Spring's one. That is, it is given an application's configuration; and we can use it as a Bean's Locator using the get() method.

    The static beans are initialized at the same time as the ApplicationContext, cause they inject already created objects such as class o modules.

    All other beans are created on demand, and they are cached for later use.

    Right now the library supports the following kind of beans:

    • bean: Constructs and inject a class
    • factory: Creates a bean by calling a function (callable)
    • alias: An alias of another bean
    • map: A map of beans
    • list: a list of beans
    • static: Inject on already created objects (on ApplicationContext initialization), such as class or module objects.

    Configuration

    At this moment the only supported configuration format is yaml files. There are 3 kinds of yaml files to define:

    • components definition
    • configuration variables for components
    • application definition
    Component's File

    A component is defined by a name, required config keys, required components and beans definitions. Several components can be defined in the same file. That is, a component file is something like:

    component-name:
     config-keys:
       - a-config-key
       - another-config-key
     required-components:
       - other-comp-name
     definitions:
       # beans definitions
    
    other-component-name:
      #same as before
      # ...
    

    Inside 'definitions' goes all components beans, next I'll show how to configure each of the bean's types.

    A bean of type 'bean' can have setter or construction injection, so it goes:

       renderer:
         type: bean
         path: renderers.html.impl:HtmlRenderer
         constructor-args:
           debug_mode: config:debug_mode
    
       renderer:
         type: bean
         path: renderers.rest.impl:ReStructuredTextRenderer
         properties:
           syntax: bean:syntax_rules
    

    The bean's name in each case is 'renderer', one uses constructor injection, and the other setter injection. The first has a constructor's keyword argument named 'debug_mode' and the value to set is the config-key of the component 'debug_mode'. The second bean, has a property named 'syntax' set with another bean, named 'syntax_rules'. This bean, must be in the same component. If a bean from another component is needed, first you need to declare the other component in 'required-components' of the component, and then, when referring to the bean, you should use it's absolute name, that is: 'comp_name::bean_name'.

    A bean of type 'factory', has only constructor-args, so it goes:

       syntax_rules:
         type: factory
         path: renderers.rest.rules:create_rules
         constructor-args:
           keywords: config:accepted_keywords
    

    If instead of a keyword argument, you want to use positional arguments, you would place a keyword the position of the argument, beginning with 0.

    A bean of type 'alias' is just a link to another bean, so it goes:

       documents_generator:
         type: alias
         target: simple_generator
    

    A bean of type 'map' or 'list' needs you to define 'properties' as a dictionary or list depending the case. Examples of this are:

       rules_map:
         type: map
         properties:
           rule1: bean:some_rule
           rule2: bean:some_other_rule
       exceptions_list:
         type: list
         properties:
           - bean:some_exception
           - my-string-exception
    

    A bean fo type 'static' has only setter injection, because controlfreak is not involved in the object creation. For example:

       api-accounts:
         type: static
         path: popserver.controllers.api.accounts:AccountsController
         properties:
           service_dao: bean:dao::serviceDAO
           account_dao: bean:dao::accountDAO
           user_dao: bean:dao::userDAO
           account_service: bean:pop-services::accountService
    
    Configuration Variables File

    A configuration variables file, defines a values for component's required configuration values. So if we have a component named 'database' that requires a config value 'database_url' the config file would look like

    database:
     database_url: postgres://user:password@mydbserver:5432/dbname
    

    In the same configuration file one can place configuration values for several components.

    Application File

    An application file, is where we bundle several components files, with their configuration files. The reason to separate the config values from the component's bean is because the config values will probably change more often than the bean's wiring structure. Also, I believe that is clearer to see configuration values on a separate place than seeing them within the beans that are, in a way, more coupled with the programming task itself.

    Another thing one can do in the application file is to redefine the components wiring. That is, to decide witch components will fulfill the required components of other component. For example, suppose that we have two components that "implements" the same contract, one it's called 'dummy-cache' and the other 'standard-cache'. Now, we also have a component, let's say 'services' that requires a 'cache' component. The application file would be the place to wire one of the cache components to the services components: dummy for development, and standard for production environment.

    Another feature of the application file is to have a same component, configured with two different config values at the same time. Think on the component as a class definition, that we would want to instantiate with different values each time. For example, we have a database component, and we are using two databases, and we need to use the same component with two different config values. To do this, we define two aliases for 'database' in application context: 'database1' and 'database2'. In the config file, we will then set the values for 'database1' and 'database2'; and not for 'database'.

    An example of an application file is:

    includes: #component files to include
     - "%(here)s/../../components/general/amazon-mock.yaml"
     - "%(here)s/../../components/general/cache.yaml"
     - "%(here)s/../../components/general/database.yaml"
     - "%(here)s/../../components/general/eye-services.yaml"
     - "%(here)s/../../components/general/pop-services.yaml"
     - "%(here)s/../../components/general/core-model.yaml"
     - "%(here)s/../../components/general/dao.yaml"
     - "%(here)s/../../components/general/domains.yaml"
     - "%(here)s/../../components/general/extauth-services.yaml"
    customization: #aliases and rewiring as explained
     cache:
       required-components:
         cache-customization: dummy-cache-customization
     eye-services:
       required-components:
         item-age-customization: testing-item-age-customization
    config: #config files to read
     - "%(here)s/../base/config.yaml"
     - "%(here)s/config.yaml"
    

    Using the library

    To load a control freak set up, one would to something like:

    from controlfreak.config.yaml import createApplicationContext
    appctx = createApplicationContext('my-app-file.yaml')
    

    To use the Application Context as a bean's locator, one need to give it the absolute path of a bean, that is, the component and the bean name together. For example:

    session = appctx.get('database::session')
    ...
    

    This would result on the 'session' bean from the 'database' component.

    Final Example

    Here's an example of each file:

    component file:
    reSt-renderer:
     config-keys:
       - accepted_keywords
     definitions:
       renderer:
         type: bean
         path: renderers.rest.impl:ReStructuredTextRenderer
         properties:
           syntax: bean:syntax_rules
       syntax_rules:
         type: factory
         path: renderers.rest.rules:create_rules
         constructor-args:
           keywords: config:accepted_keywords
    
    
    html-renderer:
     config-keys:
       - debug_mode
     definitions:
       renderer:
         type: bean
         path: renderers.html.impl:HtmlRenderer
         constructor-args:
           debug_mode: config:debug_mode
    
    documents-generator:
     required-components:
       - renderer
     definitions:
       documents_generator:
         type: alias
         target: simple_generator
       simple_generator:
         type: bean
         path: generators.simple:SimpleGenerator
         propreties:
           renderer: bean:renderer:renderer
       complex_generator:
         type: bean
         path: generators.complex:ComplexGenerator
         constructor-args:
           rules: bean:rules_map
           exceptions: bean:exceptions_list
       rules_map:
         type: map
         properties:
           rule1: bean:some_rule
           rule2: bean:some_other_rule
       exceptions_list:
         type: list
         properties:
           - bean:some_exception
           - my-string-exception
       some_rule:
         type: bean
         path: rules:SomeRule
       some_other_rule:
         type: bean
         path: rules:SomeOtherRule
       some_exception:
         type: bean
         path: excpetions:MyGeneratorException
    

    config file:

    reSt-renderer:
     accepted_keywords:
       - hey
       - jo
    
    html-renderer:
     debug_mode: True
    

    application file:

    includes:
     - "%(here)s/my-components.yaml"
    config:
     - "%(here)s/my-config.yaml"
    

    Well, that's all, I hope you will find this IoC library useful!

    2009-03-25

    [RELEASE] Zauber Commons 3.10

    If you are using Zauber Commons (our java components), you may be interested on the new version: 3.10. This release may not be compatible with the 3.8/3.9 version.

    Global Notes:
    • new modules: commons-web-transformation and commons-wicket
    • The upgrade from older version might require supervision. Some transitive dependencies might be missing. (zauber commons changed some dependency versions)
    • You need to upgrade from log4j 1.2.8 to 1.2.14. We suggest to upgrade your application (this is required if you use commons-repository). Change your pom:
                 
                   log4j
                   log4j
                   1.2.15
                   
                      
                        javax.mail
                        mail
                      
                      
                        javax.jms
                        jms
                      
                      
                        com.sun.jmx
                        jmxri
                      
                      
                        com.sun.jdmk
                        jmxtools
                      
                   
                  
      
    Changelog:
    • commons-doxia:
      • This module aims to provide functionality related to the transformation of html/xml content.
      • So far we've implemented the XmlSanitizer interface, the purpose of which is to parse a dom tree and "sanitize" the dangerous nodes.
      • The only implementation of XmlSanitizer available at this time is DeletingElementNodeSanitizer. It deletes element/attribute nodes considered invalid. In order to do this it requires a TagSecutrityStrategy, implemented by HashMapTagSecurityStrategy.
      • XmlSanitizerTest provides an example of how to use this functionality.
    • commons-web-wicket:
      • we start to detect reusable classes across wicket projects:DynamicDetachableModel, RepositoryDynamicDetachableModel, ConfirmLink
    • commons-doxia:
      • upgrade to doxia 1.1 (http://jira.codehaus.org/secure/ReleaseNote.jspa?version=13617&styleName=Html&projectId=10780)
    • commons-spring:
      • remove unused dependency: org.springframework:spring-webmvc-struts
    • commons-web-utils:
      • new filter (NullFilter) that does nothing. usefull for conditional configuration of filters. for example in this example, depending on a property we activate/deactivate a caching filter depending on a flag:
              <bean name="anunciosServletCache" class="ar.com.zauber.commons.spring.beans.factory.SwitchConditionalFactoryBean">
                    <property name="caseBlocks">
                        <list>
                            <bean class="ar.com.zauber.commons.spring.beans.factory.impl.BooleanPropertyCaseBlock">
                                <constructor-arg index="0" value="${bar.cache.active}"/>
                                <constructor-arg index="1">
                                    <bean class="com.foo.bar.web.filters.AnunciosCacheFilter">
                                        <constructor-arg index="0" ref="anunciosCacheManager"/>
                                        <constructor-arg index="1" value="anuncios"/>
                                        <constructor-arg index="2" ref="seoStrategy"/>
                                        <constructor-arg index="3" ref="pagingHelper"/>
                                        <constructor-arg index="4" ref="commonsWebUtilsVersionProvider"/>
                                        <constructor-arg index="5" ref="lastModificationEntities"/>
                                    </bean>
                                </constructor-arg>
                            </bean>
                            <bean class="ar.com.zauber.commons.spring.beans.factory.impl.DefaultCaseBlock">
                                <constructor-arg index="0">
                                    <bean class="ar.com.zauber.commons.web.filter.NullFilter"/>
                                </constructor-arg>
                            </bean>
                        </list>
                    </property>
                </bean>
            
    • commons-repository:
      • upgrade from hibernate 3.2.x to 3.3.1 (this is the lastest stable version. also it is the version that will be used with spring 3.x). The changes that repository user need to take in consideration:
        • Hibernate migration guide http://www.hibernate.org/250.html#A51
        • If you want to ehcache 2nd level cache you need to add
          <dependency>
                              <groupId>org.hibernate</groupId>
                              <artifactId>hibernate-ehcache</artifactId>
                              <version>3.3.1.GA</version>
                              </dependency>
      • repository now has the ability to group by and apply an agregate function at the same time. This is usefull to lazy load entities and know how many entities are in the query:
                final Query/lt;DireccionDummy> query = 
                    new SimpleQuery<DireccionDummy>(DireccionDummy.class, 
                        new NullFilter(), null,  new Ordering(Collections.emptyList()));
                AggregateFunction function = new CompositeAggregateFunction(
                        Arrays.asList(new AggregateFunction[]{
                                new CountPropertyAggregateFunction("numero"),
                                new GroupPropertyAggregateFilter("direccion"),
                                new GroupPropertyAggregateFilter("numero"),
                        }));
                
                 for(Object row: repository.aggregate(query, function, List.class)) {
                     final Object [] fields = (Object[]) row;
                     System.out.println("frequency: " + fields[0] + " | direccion:  " + fields[1] + "numero:  " + fields[2]);
                 }
        
      • Another implementation of Value called PropertyValue that allows queries that have to compare it's properies. For example select Person where length > age. For example:
                final Query<PersonaDummy> q1 =
                    new SimpleQuery<PersonaDummy>(
                            PersonaDummy.class, new EqualsPropertyFilter("id", 
                                    new PropertyValue("id")), null, null);
        
        
                assertEquals(Integer.valueOf(6), 
                        repository.aggregate(q1, new RowCountAggregateFilter(), Integer.class));
        
      • new Hibernate interceptor used to inject dependencies from a spring context to achieve a Domain Driven Design. It is called =SpringInjectionInterceptor=. The classes wishing being injected with this interceptor, must be annotated with the annotation =@Configurable= And each field that will be injected must be transient and annotated with @Qualifier.The qualifier name can be used to set the bean name (else it uses the field name). For example:
        @Entity
        @Configurable
        public class DomainEntityExample implements Persistible {
            @Id
            private Long id;
        
            @Qualifier(value = "someService")
            private transient SomeService service;
            @Qualifier
            private transient SomeService someService;
            ...
        
            private DomainEntityExample() {
               // hibernate's
            }
        
            public DomainEntityExample(SomeService service, SomeService someService) {
                .....
                this.service = service;
                this.someService = someService;
            }
            public final SomeService getService() {
                return service;
            }
            public final SomeService getSomeService() {
                return someService;
            }
            ....
        }
        
        We also provide a SpringInjectionHibernateAuditLoggerInterceptor that combines the HibernateAuditLoggerInterceptor with SpringInjectionInterceptor (hibernate only let you specify only one interceptor) enhance SpringInjectionInterceptor (the interceptor that inject dependencies to hibernates entities). If the annotated bean implements InitializingBean, the afterPropertiesSet is called for post configuring settings.
    Known Bugs
    None known at this time
    How to use it

    There isn't a site with tutorials or formal documentation for Zauber Commons yet, but we are working on that. For now you can browse the source at the SVN repository https://code.zauber.com.ar/repos/sandbox/components/commons/code/trunk/. Also you can use Zauber Commons with a Maven 2:

      <repositories>
         <repository>
           <id>zauber-code-releases</id>
           <name>public zauber repository</name>
           <url>https://repo1.zauber.com.ar/zauber/code/releases</url>
           <releases><enabled>true</enabled></releases>
           <snapshots><enabled>false</enabled></snapshots>
         </repository>
         <repository>
           <id>zauber-code-snapshots</id>
           <name>public zauber repository</name>
           <url>https://repo1.zauber.com.ar/zauber/code/snapshots</url>
           <releases><enabled>false</enabled></releases>
           <snapshots><enabled>true</enabled></snapshots>
         </repository>
       </repositories>   
     
    And for example add a dependency to your project
      <dependency>
        <groupId>ar.com.zauber.commons.web</groupId>
        <artifactId>commons-web-version</artifactId>
        <version>${commons.version}</version>
      </dependency>

    Diffstat
     auth/password/pom.xml                                                                                                               |    6 
     commons/pom.xml                                                                                                                     |    6 
     dao/src/test/java/ar/com/zauber/commons/dao/OrderTest.java                                                                          |   16 
     exception/pom.xml                                                                                                                   |    7 
     facebook/pom.xml                                                                                                                    |    1 
     jetty-launcher/pom.xml                                                                                                              |    6 
     message/impl/pom.xml                                                                                                                |    7 
     pom.xml                                                                                                                             |   83 +++
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/aggreate/GroupPropertyAggregateFilter.java                      |   30 +
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/BeginsLikePropertyFilter.java                           |    8 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/ContainsLikePropertyFilter.java                         |    8 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/EndsLikePropertyFilter.java                             |    8 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/EqualsPropertyFilter.java                               |    6 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/ExactLikePropertyFilter.java                            |    8 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/GreaterThanEqualsPropertyFilter.java                    |    7 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/GreaterThanPropertyFilter.java                          |    7 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/LessThanEqualsPropertyFilter.java                       |    7 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/LessThanPropertyFilter.java                             |    7 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/filters/LikePropertyFilter.java                                 |    8 
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/values/PropertyValue.java                                       |   42 +
     repository/api/src/main/java/ar/com/zauber/commons/repository/query/values/SimpleValue.java                                         |   17 
     repository/impl/entities/pom.xml                                                                                                    |    1 
     repository/impl/spring-hbm/pom.xml                                                                                                  |   39 +
     repository/impl/spring-hbm/src/main/java/ar/com/zauber/commons/repository/SpringHibernateRepository.java                            |    4 
     repository/impl/spring-hbm/src/main/java/ar/com/zauber/commons/repository/aggregate/ProjectionAggregateFunctionVisitor.java         |    3 
     repository/impl/spring-hbm/src/main/java/ar/com/zauber/commons/repository/query/visitor/CriteriaFilterVisitor.java                  |  102 ++--
     repository/impl/spring-hbm/src/main/java/ar/com/zauber/commons/repository/usertypes/URLUserType.java                                |  163 +++++++
     repository/impl/spring-hbm/src/main/java/ar/com/zauber/commons/repository/utils/SpringInjectionHibernateAuditLoggerInterceptor.java |   52 ++
     repository/impl/spring-hbm/src/main/java/ar/com/zauber/commons/repository/utils/SpringInjectionInterceptor.java                     |  217 ++++++++++
     repository/impl/spring-hbm/src/test/java/ar/com/zauber/commons/repository/test/model/DomainEntityExample.java                       |   82 +++
     repository/impl/spring-hbm/src/test/java/ar/com/zauber/commons/repository/test/model/SomeService.java                               |   27 +
     repository/impl/spring-hbm/src/test/java/ar/com/zauber/commons/repository/utils/SpringInjectionInterceptorTest.java                 |   77 +++
     repository/impl/spring-hbm/src/test/java/ar/com/zauber/commons/test/SpringHibernateRepositoryTest.java                              |   53 ++
     repository/impl/spring-hbm/src/test/resources/ar/com/zauber/commons/repository/utils/injection-hibernate-mapping-spring.xml         |   15 
     repository/impl/spring-hbm/src/test/resources/ar/com/zauber/commons/repository/utils/injection-hibernate-spring.xml                 |   52 ++
     repository/pom.xml                                                                                                                  |   49 --
     spring/pom.xml                                                                                                                      |   16 
     spring/src/main/java/ar/com/zauber/commons/spring/mail/NullMailSender.java                                                          |   16 
     web/pom.xml                                                                                                                         |    1 
     web/transformation/pom.xml                                                                                                          |   44 ++
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/api/AttributeValueValidator.java               |   31 +
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/api/TagSecutrityStrategy.java                  |   48 ++
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/api/XmlSanitizer.java                          |   35 +
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/AbstractElementNodeSanitizer.java         |  125 +++++
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/DeletingElementNodeSanitizer.java         |   73 +++
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/HashMapTagSecurityStrategy.java           |   82 +++
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/HrefUrlOnlyValueValidator.java            |   41 +
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/StyleAlignmentOnlyValueValidator.java     |   42 +
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/StyleTextDecorationValueValidator.java    |   45 ++
     web/transformation/src/main/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/TargetSelfBlankValueValidator.java        |   33 +
     web/transformation/src/test/java/ar/com/zauber/commons/web/transformation/sanitizing/impl/XmlSanitizerTest.java                     |  133 ++++++
     web/utils/src/main/java/ar/com/zauber/commons/web/filter/NullFilter.java                                                            |   51 ++
     wicket/impl/pom.xml                                                                                                                 |   58 ++
     wicket/impl/src/main/java/ar/com/zauber/commons/wicket/components/ConfirmLink.java                                                  |   69 +++
     wicket/impl/src/main/java/ar/com/zauber/commons/wicket/model/DynamicDetachableModel.java                                            |  107 ++++
     wicket/impl/src/main/java/ar/com/zauber/commons/wicket/model/RepositoryDynamicDetachableModel.java                                  |   93 ++++
     wicket/pom.xml                                                                                                                      |   20 
     

    2009-02-11

    [RELEASE] Zauber Commons 3.8

    If you are using Zauber Commons (our java components), you may be interested on the new version: 3.8. This is a small release. Compatible with the 3.7 version.

    Changelog:
    • commons-spring
      • HttpPagingHelper: Now it is posible to use another GET parameter name to track the current page number (the default is "page)". The default value can be changed with the new method setPageField(String).
    Known Bugs
    None known at this time
    How to use it

    There isn't a site with tutorials or formal documentation for Zauber Commons yet, but we are working on that. For now you can browse the source at the SVN repository https://code.zauber.com.ar/repos/sandbox/components/commons/code/trunk/. Also you can use Zauber Commons with a Maven 2:

      <repositories>
        <repository>
          <id>zauber-code-releases</id>
          <name>public zauber repository</name>
          <url>https://repo1.zauber.com.ar/zauber/code/releases</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>false</enabled></snapshots>
        </repository>
        <repository>
          <id>zauber-code-snapshots</id>
          <name>public zauber repository</name>
          <url>https://repo1.zauber.com.ar/zauber/code/snapshots</url>
          <releases><enabled>false</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>   
    
    And for example add a dependency to your project
      <dependency>
       <groupId>ar.com.zauber.commons.web</groupId>
       <artifactId>commons-web-version</artifactId>
       <version>${commons.version}</version>
     </dependency>
    Diffstat
     HttpPagingHelper.java |   12 +++++++++++-
     1 file changed, 11 insertions(+), 1 deletion(-)