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

2014-03-16

Guice Tutorial - 01 - The simplest injection

Introduction

Guice is very powerful and simple dependency injection library from Google. There is no need to create xml configuration or have container to use it. Moreover, it gives us objects already casted to desired types. In this post I will show how to start working with guice and make the simplest injection. In this case, domain will be adding persons. All sources are available here.


Main

First, we have to add maven dependency to guice and optionally to javax.inject:
<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>3.0</version>
</dependency>
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
Guice has own annotations to describe injections, but I like to use javax.inject annotation, which could also be used with Guice and are part of JavaEE.


Person is described by given class:
public class Person {
    private String firstName;
    private String lastName;
    private Integer age;

    public Person(final String firstName, final String lastName, 
                  final Integer age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    //...
}
We also need PersonDao and PersonValidator:
public class PersonDao {
    public void persist(final Person user) {
        // ...
    }
}
public class PersonValidator {
    void validate(final Person user) throws PersonValidationException {
        // ...
    }
}
Now we have all dependencies so let's create controller, which will use this classes:
public class PersonController {
    private final PersonValidator userValidator;
    private final PersonDao userDao;

    @Inject
    public PersonController(final PersonValidator userValidator, 
                            final PersonDao userDao){
        this.userValidator = userValidator;
        this.userDao = userDao;
    }

    public void add(final Person user) throws PersonValidationException{
        userValidator.validate(user);
        userDao.persist(user);
    }
}
The magic is in annotation Inject. In Guice, when we want to use class with not default, no argument constructor, we have to add this annotation to one of constructor.


Testing

It is time to test it. I will use JUnit.
@Test
public void shouldGetPersonControllerFromInjectorAndPersistPerson() {
    //given
    Injector injector = Guice.createInjector();
    Person user = new Person("John", "Smith", 25);
    //when
    PersonController userController = injector.getInstance(PersonController.class);
    //then
    userController.add(user);
}
All classes could be obtained from Guice via Injector. Injector collects all classes in classpath and allows to get their instances if they have default constructors or constructors annotated with Inject (from Guice or javax).
Method add of userController pass without exception so Guice has injected PersonDao and Person Validator classes. 


Conclusion

Guice is very simple and in basic cases we need only to use Inject annotation on class with dependecies. We only have to call getInstance and tell Guice which class (or interface) we want.

2014-01-13

Properties from maven

Introduction

Sometimes your code should work in many environments using varaibles defined in many places. There could be default variable in code (when configuration is not defined), variable in configuration file or pom.xml for tests and maybe each developer should have own variable value...

Property reader code

All examples will be tests and will be use PropertyReader class which is defined below.
package com.blogspot.przybyszd.sandbox.propertyplaceholders;

import java.util.Properties;

public class PropertyReader{
    
    public static Properties readProperties(final String configurationFile){
        Properties properties = new Properties();
        try{
            properties.load(PropertyReader.class.getResourceAsStream(configurationFile));
        }catch(Exception e){
            System.err.println("Configuration file was not found");
        }
        return properties;
    }   
}
All properties files will be defined in src/test/resources.

Default properties in code

First, assume that properties file is not provided or property is not given:
package com.blogspot.przybyszd.sandbox.propertyplaceholders;

import static org.junit.Assert.assertEquals;
import java.util.Properties;
import org.junit.Test;

public class PropertyReaderTest{
    
    @Test
    public void shouldUseDefaultPropertyFromCode(){
        // given
        Properties properties = PropertyReader.readProperties("/no-existing-file.properties");
        // when
        String value = properties.getProperty("address", "localhost");
        // then
        assertEquals("localhost", value);
    }
}
But when property file exists and has this property then it will be used:
@Test
public void shouldUsePropertyFromFile(){
    // given
    Properties properties = PropertyReader.readProperties("/existing-file.properties");
    // when
    String value = properties.getProperty("address", "localhost");
    // then
    assertEquals("10.10.10.10", value);
}
Property in file existing-file.properties:
address=10.10.10.10

Default properties in pom.xml

Properties could be also defined in pom.xml and then placeholders could be used in property file. Test is going first:
@Test
public void shouldUsePropertyFromPom(){
    // given
    Properties properties = PropertyReader.readProperties("/file-with-placeholder.properties");
    // when
    String value = properties.getProperty("address", "localhost");
    // then
    assertEquals("trololo.com", value);
}
Instead of value in properties file there is only placeholder (file file-with-placeholder.properties):
address=${address}

Value of property is defined in pom.xml (filtering of test resources must be set to true and, optionally, resource filtering could be set to true):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>property-placeholders</groupId>
 <artifactId>property-placeholders</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>PropertyPlaceholder</name>

 <properties>
  <address>google.com</address>
 </properties>

 <build>
  <resources>
   <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
   </resource>
  </resources>
  <testResources>
   <testResource>
    <directory>src/test/resources</directory>
    <filtering>true</filtering>
   </testResource>
  </testResources>
 </build>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.11</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>
Now variable could be also set from command line:
mvn -Daddress=my-address clean test

Default property in settings.xml

Each developer (or each environment) could define some properties in settings.xml in $HOME/.m2 directory and these properties overwrite properties defined in pom.xml.
Test first:
@Test
public void shouldUsePropertyFromSettingsXMl(){
    // given
    Properties properties = PropertyReader.readProperties("/file-with-placeholder.properties");
    // when
    String value = properties.getProperty("address2", "localhost");
    // then
    assertEquals("yahoo.com", value);
}
I add new property address2 so I have to add property placeholder in properties file and in pom.xml.
Properties file:
address=${address}
address2=${address2}
pom.xml:
<properties>
 <address>google.com</address>
 <address2>bing.com</address2>
</properties>
And now variable address2 will be redefined in settings.xml file :
<settings>
  <profiles>
    <profile>
      <id>property-placeholder</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <address2>yahoo.com</address2>
      </properties>
    </profile>
  </profiles>
</settings>
This profile is active by default so there is not need to use -P parameter.
Not the tests pass.

Conclusion

Use Maven and define properties anywhere.