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.

Thursday, 1 September 2011

Eclipse indigo minimal install

Since i've found out about installation of customized (minimal) eclipse, I haven't ever used full version of PHP, JEE or PDT packages. Here's a bit updated version:

In order to install eclipse that suits to your needs, you must:

  1. Go to Eclipse downloads page
  2. Select Other Downloads near Eclipse Classic
  3. I couldn't find a minimal installation for Eclipse 4.0 or 4.1, hence you must switch to 3.x downloads page
  4. Next select version of your choice
  5. Scroll down to "Platform Runtime Binary" and download the package depending on your OS and architecture
  6. Uzip and start Eclipse
  7. Select Help > Install New Software ...
  8. And install appropriate packages:
    1. For Java web development I use:
      • Eclipse Java EE Developer Tools
      • Eclipse Java Web Developer Tools
      • Eclipse Web Developer Tools
      • All JST Server Adapters (2 packages), JST Server UI, WST Server Adapters (this is the part where it's hard to guess where Tomcat server adapter is located, if you want to use Tomcat installation from Eclipse)
      • m2e - Maven Integration for Eclipse + integrations for WTP, SCM, etc.
      • Subclipse + Adapters
      • Spring IDE: Core, Web Flow, Autowire Extention, Security Extention
    2. For PHP web development I use:
      • PHP Development Tools (PDT) SDK Feature
      • Subclipse + Adapters
Note that both installations indirectly include support for Javascript, CSS, HTML and other file editing. And this is MY way of doing things - you're not restricted to these packages - feel free to add any additional plugins of your choice.

If you have any suggestions or questions, feel free to comment. Blogging and commenting is all about knowledge exchange (that is fantastic!).

Thursday, 11 August 2011

Eclipse doesn't shut down the process after exit

Another issue that I solved today was regarding closing Eclipse. After migrating to 64bit Ubuntu, my Eclipse (newly installed) didn't want to close. After closing, eclipse continued to run in background. In fact when I noticed it, I had 3 or 4 running eclipse processes since plugin installation requires restarts and each restart added one process eating all swap space. There was no evidence in GUI of running processes, but process manager did show them running. Then i came across this bug in launcpad and it did solve the problem.

Solution: Disable assistive technologies: System > Preferences > Assistive Technology Preferences > "Enable assistive technologies"

Dual monitors in LXDE

Trying out LXDE as my desktop environment on ubuntu box (Gnome is getting easy to use, but too heavy). There were some problems with dual monitor support (couldn't find a config app), but this line solved everyting:

xrandr --output VGA-0 --auto --left-of DVI-0

Inspired by this blog post.

Friday, 15 April 2011

I18n portlets

Supporting multilingual content is very important in my organization. Unfortunately not everything is so easy as it might seem. Today I solved a little problem which was bugging probationer who is under my guidance. As it turned out, there were two major problems with this tutorial:

  1. He was using wrong fmt.tld file. The best thing is to define taglib using this line (notice the difference from line in tutorial):
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
  2. Ssecond pitfall was that uPortal implementation which we use, didn't set locale automagically. If your portal implementation isn't setting this attribute (with key 'javax.servlet.jsp.jstl.fmt.locale'), then use these lines (the first one is needed in order to define renderRequest value):
    <portlet:defineObjects />
    <fmt:setLocale value="${renderRequest.locale}"/>

Ok, now putting all together if you need to translate content in JSP:

  1. Check that your portlet supports locales of your choice
    <portlet>
    ...
    <supported-locale>en</supported-locale>
    <supported-locale>lv</supported-locale>
    ...
    </portlet>
  2. Create files named messages.properties and messages_lv.properties in package com.example with key-value pairs of your choice. For example:
    messages.properties:
    welcome=Welcome to my page!

    messages_lv.properties:
    welcome=Esiet sveicināti manā mājas lapā!
  3. Add standard library to classpath of your web application (i.e. in WEB-INF/lib) and define use of formatting taglib in JSP page, specify locale, and translate:
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <%@ taglib prefix="portlet" uri="http://java.sun.com/portlet" %>
    ...
    <portlet:defineObjects />
    <fmt:setLocale value="${renderRequest.locale}"/>
    <fmt:setBundle basename="com.example.messages"/>
    <h1><fmt:message key="welcome"/></h1>

Hope this helps

Thursday, 7 April 2011

Trusting self signed certificate in java

Simple version of how to trust self-signed certificates.
$ wget https://raw.githubusercontent.com/escline/InstallCert/master/InstallCert.java
$ javac InstallCert.java 
$ java InstallCert host:port
$ cp jssecacerts /usr/java/jdk1.6.0_21/jre/lib/security/

restart JVM and you're done. Of course, you can always choose longer path and follow JSSE documentation

Thursday, 24 March 2011

Trusting self-signed certificates

Yesterday I worked on a task that involved changing user password in Active Directory. Changing users pasword in AD requires secure connection (636 port) and self signed certificate was used (it was a test machine after all). I knew that Java won't accept that certificate, but was surprised that even Luma didn't accept the certificate, hence started looking over google to find a solution. Surprisingly nothing came out. There were tons of materials describing how to create a self signed certificate, but nothing how to accept one. After a couple of hours I remembered that one internal project documentation described the thing i needed. Steps:


  1. openssl s_client -connect ad.example.com:636
  2. Copy the certificate (everything between lines 'begin certificate' and 'end certificate' including both lines)in file (e.g. ad-example-com.cert)
  3. keytool -import -alias ad-example-com -file ad-example-com.cert -keystore ad-example-com.keystore
  4. Add certificate and keystore file to Luma (in server configuration view) and now you should be able connect to AD without problems...

In order to connect to AD using Java and accept SSL connection, use see this blog entry (by Andreas Sterbenz). InstallCert java file attached to his blog post is very easy to use in order to create jssecacerts file which can be copied to $JAVA_HOME/jre/lib/security in order to accept the self signed certificate.