Showing posts with label maven. Show all posts
Showing posts with label maven. Show all posts

Wednesday, 21 March 2012

Bytecode instrumentation for Hibernate using Maven

Lazy property loading for hibernate is nice when you're using complex formulas for calculating values. But it requires bytecode instrumentation or otherwise hibernate will ignore 'lazy="true"' on property element and load the value eagerly. Here's the snippet of my pom.xml that does the instrumentation:
    <build>
        <plugins>
            ...
            <!-- Bytecode insturmentation in order to enable lazy property loading by Hibernate. -->
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <id>Instrument domain classes</id>
                        <configuration>
                            <tasks>
                                <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
                                    <classpath>
                                        <path refid="maven.dependency.classpath" />
                                        <path refid="maven.plugin.classpath" />
                                    </classpath>
                                </taskdef>
                                <instrument verbose="true">
                                    <fileset dir="${project.build.outputDirectory}">
                                        <include name="com/example/domain/*.class" />
                                    </fileset>
                                </instrument>
                            </tasks>
                        </configuration>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.hibernate</groupId>
                        <artifactId>hibernate-core</artifactId>
                        <version>${hibernate.version}</version>
                    </dependency>
                    <dependency><!-- bytecode instrumentation library to use... note that cglib is deprecated -->
                        <groupId>org.javassist</groupId>
                        <artifactId>javassist</artifactId>
                        <version>${javassist.version}</version>
                    </dependency>
                    <dependency><!-- so that slf4j won't barf about missing binding -->
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-simple</artifactId>
                        <version>${slf4j.version}   </version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
Note that you'll have to define the versions of libraries that you're using (here are my values):
  <properties>
    <hibernate.version>3.6.7.Final</hibernate.version>
    <javassist.version>3.14.0-GA</javassist.version>
    <slf4j.version>1.6.1</slf4j.version>
  </properties>

Friday, 23 September 2011

Reading XML files in java

Example description: I have to create a custom group store so I could add them to the groups retrieved from LDAP. My choice is storing them in XML files. So I thought someone might find my experience interesting.

Prerequisites: Eclipse for java development with m2e plugin.

Create XSD file

Why? This will make creating XML files very easy and will ensure that those files will be readable as expected. I won't go into details, since it's not so hard to create even very complex schema definition files. But here's the result (file /src/main/resources/lv/rtu/itd/vivs/user-groups-1.0.xsd):

<schema elementformdefault="qualified" targetnamespace="http://vivs.rtu.lv/schemas/user-groups" xmlns:tns="http://vivs.rtu.lv/schemas/user-groups" xmlns="http://www.w3.org/2001/XMLSchema">

  <element name="user-groups" type="tns:user-groups-type">

  <complextype name="user-groups-type">
    <sequence>
      <element maxoccurs="unbounded" name="user-groups" type="tns:user-group" />
    </sequence>
  </complextype>

  <complextype name="user-group">
    <sequence>
      <element name="uid" type="string" />
      <element maxoccurs="unbounded" name="group" type="string" />
    </sequence>
  </complextype>

</element>

Configure class generation

Create file src/main/binding/bindings.xjb (this will instruct the generator how to create new classes):

<bindings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns="http://java.sun.com/xml/ns/jaxb"
  xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
  version="2.1">

  <bindings>
    <globalBindings>
      <serializable uid="1" />
      <xjc:simple />
    </globalBindings>
  </bindings>

  <bindings schemaLocation="../resources/lv/rtu/itd/vivs/user-groups-1.0.xsd">
    <schemaBindings>
      <package name="lv.rtu.itd.vivs.xml.user_groups" />
    </schemaBindings>
  </bindings>

</bindings>

Configure pom.xml and add something like this in order to tell Maven that it must now generate classes using jaxb2:

  <build>
    <plugins>
      <plugin>
        <groupId>org.jvnet.jaxb2.maven2</groupId>
        <artifactId>maven-jaxb2-plugin</artifactId>
        <version>0.7.4</version>
        <executions>
          <execution>
            <id>generate-xjc-sources</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <bindingDirectory>src/main/binding</bindingDirectory>
          <schemaDirectory>src/main/resources</schemaDirectory>
          <schemaIncludes>
            <include>lv/rtu/itd/vivs/*.xsd</include>
          </schemaIncludes>
          <extension>true</extension>
          <args>
            <arg>-no-header</arg>
            <arg>-readOnly</arg>
            <arg>-mark-generated</arg>
          </args>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-xjc</artifactId>
            <version>2.2.1</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>

Now, probably, you must click on project and choose Maven > Update Project Configuration

Java code

Create interface for class:

package lv.rtu.itd.vivs.service;

import java.util.Set;

public interface GroupService {
    Set getGroups(String uid);
}

Create class that will unmarshal (read) the XML document:

package lv.rtu.itd.vivs.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import lv.rtu.itd.vivs.service.GroupService;
import lv.rtu.itd.vivs.xml.user_groups.UserGroup;
import lv.rtu.itd.vivs.xml.user_groups.UserGroups;

public class XmlGroupService implements GroupService {
    
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    private UserGroups groups;
    
    public void setFilename(String filename) {
        JAXBContext ctx;
        try {
            ctx = JAXBContext.newInstance("lv.rtu.itd.vivs.xml.user_groups");
            Unmarshaller unmarshaller = ctx.createUnmarshaller();
            InputStream is = new ClassPathResource(filename).getInputStream();
            this.groups = (UserGroups) unmarshaller.unmarshal(is);
        } catch (JAXBException e) {
            logger.warn(e.getMessage(), e);
        } catch (IOException e) {
            logger.warn(e.getMessage(), e);
        }
    }
    
    @Override
    public Set getGroups(String uid) {
        for (UserGroup group : groups.getUserGroups()) {
            if (group.getUid().equals(uid)) {
                return new HashSet(group.getGroups());
            }
        }
        return new HashSet();
    }
}

Create test class (under src/test/java):

package lv.rtu.itd.vivs.service;

import java.util.HashSet;
import java.util.Set;

import lv.rtu.itd.vivs.service.impl.XmlGroupService;

import org.junit.Assert;
import org.junit.Test;

public class XmlGroupsTester {
    
    @Test()
    public void testGetGroups() {
        XmlGroupService service = new XmlGroupService();
        service.setFilename("user-groups.xml");
        
        Set expected = new HashSet();
        Assert.assertEquals(expected, service.getGroups("not-existing-user"));
        
        expected.add("my/demo/group");
        expected.add("my/demo/group2");
        expected.add("my/demo/group3");
        Assert.assertEquals(expected, service.getGroups("user.demo"));
    }
}

Now create XML file (src/test/resources/user-groups.xml:

<?xml version="1.0" encoding="UTF-8"?>
<user-groups xmlns="http://vivs.rtu.lv/schemas/user-groups" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://vivs.rtu.lv/schemas/user-groups http\://vivs.rtu.lv/schemas/user-groups">
  <user-groups>
    <uid>user.demo</uid>
    <group>my/demo/group</group>
    <group>my/demo/group2</group>
    <group>my/demo/group3</group>
  </user-groups>
</user-groups>

Now you can run tests and check the results.

Wednesday, 3 June 2009

Maven remote archetype catalogs through proxy

Some days ago created a new maven archetype. Everything went good till i had to deploy it on our maven (Nexus) repository.
First i had to install an archetype plugin. Since i had older Nexus installation, i had to update it to 1.3.x to get that plugin working (later i came across some e-mail treads where developers agreed to create that plugin 1.2.x-compatible... later... :( ).
Finally i could see archetype-catalog.xml through my browser. But when i added it to eclipse as remote file, it gave me WARN messages saying "Error reading archetype catalog http://my.domain.com/service/local/nexus-archetype-plugin/public/archetype-catalog.xml Error transferring file". Now what?!?
It turns out that there's a bug http://jira.codehaus.org/browse/ARCHETYPE-202 - can't fetch remote archetype catalogs through proxy. And no fix versions. yet... Is this the cause?

Now i'm using workaround:
  1. downloadi XML file and save it as /home/me/.m2/archetype-catalog.xml
  2. add it to m2eclipse archetypes as local catalog
  3. repeat after catalog changes :(
Better ideas?

"mvn archetype:generate -DarchetypeCatalog=http://my.domain.com/service/local/nexus-archetype-plugin/public/archetype-catalog.xml"
But why is it working though command line?

There's still much to learn...