spring

5 posts

Spring Async Task Executor with Event Bus

I have a operation which talks to database, so it may be slow. So I was looking for a way to make it asynchronous, then I discovered Spring 4 has async task executor and ListenableFuture and it works well with current Google Guava EventBus.

We created a new AsyncListenableTaskExecutor first. SimpleAsyncTaskExecutor does not reuse any threads, rather it starts up a new thread for each invocation. But it's good enough.

private final AsyncListenableTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("my task");  

Then we submit a task to the executor.

this.taskExecutor.submitListenable(() -> {  
    saveToDB();
    return null;
}).addCallback(
    (result) -> this.eventBus.post(new SaveOKEvent()),
    (ex)     -> this.eventBus.post(new SaveFailedEvent(ex))
);

Then we adds listeners to both SaveOKEvent and SaveFailedEvent to handle the success and failure case. Done!

Tips for using ProGuard with Spring framework

ProGuard is a is a free Java class file shrinker, optimizer, obfuscator, and preverifier. You may want to use ProGuard to obfuscate your Java binary code first before you release it to customers, especially for Android apps, on-premise enterprise apps or libraries. The whole obfuscation process is very painful and you need to run a lot of tests to make sure your code still works properly after obfuscation.

Here are some tips to use ProGuard, especially when Spring framework is used.

Use the Maven plugin

If you use Maven to manage your project, then you should use the Maven plugin for ProGuard. It's easy to set up and use.

<plugin>  
    <groupId>com.github.wvengen</groupId>
    <artifactId>proguard-maven-plugin</artifactId>
    <version>2.0.10</version>
    <executions>
        <execution>
            <id>proguard</id>
            <phase>package</phase>
            <goals>
                <goal>proguard</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <obfuscate>true</obfuscate>
        <injar>${shaded-jar.name}</injar>
        <outjar>${shaded-jar.name}</outjar>
        <libs>
            <lib>${java.bootstrap.classes}</lib>
            <lib>${java.cryptographic.extension.classes}</lib>
            <lib>${java.secure.socket.extension.classes}</lib>
        </libs>
        <injarNotExistsSkip>true</injarNotExistsSkip>
        <options>
        </options>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>net.sf.proguard</groupId>
            <artifactId>proguard-base</artifactId>
            <version>5.2.1</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</plugin>  

In <options> of <configuration>, there should be a list of <option> to configure ProGuard.

Multi-modules project

If your Maven projects have multiple modules, then you should use Maven shade plugin to create a shaded jar for all your modules, then run ProGuard against this single jar. This can make sure ProGuard has the correct mappings for all your application's classes.

<plugin>  
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <outputFile>${shaded-jar.name}</outputFile>
        <artifactSet>
            <includes>
                <include>com.myapp:*</include>
            </includes>
        </artifactSet>
        <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/spring.factories</resource>
            </transformer>
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/spring.handlers</resource>
            </transformer>
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/spring.schemas</resource>
            </transformer>
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/spring.provides</resource>
            </transformer>
        </transformers>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
</plugin>  

If you use Spring, make sure transformers are added to process Spring's various files.

Disable optimization class/marking/final

By default ProGuard marks classes as final when possible even when classes are not declared as final. This causes problems for Spring as Spring doesn't allow classes with @Configuration annotation to be final.Use following <option> to disable it.

<option>-optimizations !class/marking/final</option>  

Adapt Spring resources

If you use configuration files like spring.factories to customise Spring, make sure these configuration files are adapted by ProGuard, otherwise the class names in those files will be wrong. META-INF/spring.* in following code specifies Spring configuration files.

<option>-adaptresourcefilecontents **.properties,META-INF/MANIFEST.MF,META-INF/spring.*</option>  

Keep annotations

Spring uses annotations extensively, so annotations should be kept in the runtime to make sure Spring still works properly. *Annotation* in code below is used to keep annotations.

<option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>  

Keep application launch class

If you use Spring Boot, the Application class should be kept to launch the app. The option in code below keeps any class with main method.

<option>-keepclasseswithmembers public class * { public static void main(java.lang.String[]);}</option>  

Keep your REST resources classes

If your app exposes a REST API, make sure those resources classes are kept. Most likely you rely on Jackson or other libraries to convert your resources objects to JSON or XML. These libraries use reflection to find out the properties in your resources classes, so these resources classes should be kept to make sure the JSON/XML representations are correct.

For example, given a resource class User,

public class User {  
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return this.firstName;
    }

    public String getLastName() {
        return this.lastName;
    }
}

After ProGuard processed this class file, the methods getFirstName and getLastName may be renamed to something like a or b. Then Jackson cannot use reflection to find JavaBean properties in this class file. The output will be just an empty JSON object.

<option>-keep public class com.myapp.**.model.** { *; }</option>  

Process bean classes

You can also following examples in ProGuard website to process bean classes by keeping setter and getter methods.

<option>  
-keep class com.myapp.**.model.** {
    void set*(***);
    boolean is*();
    *** get*();
}
</option>  

Add name to Spring beans

If Spring annotations @Service, @Component and @Configuration are used to declare beans, make sure a name is assigned to each bean, e.g. @Component("userHelper") or @Service("userService"). This is because when no name is assigned, Spring uses the class's name as the bean name, but ProGuard will change class names to something like a, b, or c. This will have name conflicts across different packages. For example, package com.myapp.a.a has a class a, package com.myapp.a.b also has a class a, these two class use the same bean name a, but the type is different. So beans should be explicitly named to avoid name conflicts.

Keep members with Spring annotations

If you use Spring annotations like @Value to inject values into your classes like below:

@Value("${myval}")
private String myVal;  

ProGuard is smart enough to infer that the value of myVal is null as this variable has not been assigned any value, so it replaces all occurrences of myVal with null in the binary code, then a lot of NullPointerExceptions will be thrown at runtime. To avoid this, use following options:

<option>-keepclassmembers class * {  
    @org.springframework.beans.factory.annotation.Autowired *;
    @org.springframework.beans.factory.annotation.Value *;
}
</option>  

FileNotFoundException when using Jersey with Spring Boot

When using Jersey with Spring Boot, you may see an FileNotFoundException error like below when Spring Boot starts:

org.springframework.beans.BeanInstantiationException: Failed to instantiate [myapp.JerseyConfig$$EnhancerBySpringCGLIB$$380920c5]:  
Constructor threw exception; nested exception is org.glassfish.jersey.server.internal.scanning.ResourceFinderException:  
java.io.FileNotFoundException: /Users/myapp.jar!/lib/myapp-ws.jar (No such file or directory)  

This error only happens using java -jar to run executable Spring Boot jar file. This is because Spring Boot packages library jars in the lib directory of the executable stand-alone jar. Jersey's class loader cannot scan those jars embedded in the executable jar. In the above error message, Jersey was trying to find myapp-ws.jar on the local file system, but cannot find it because it's embedded in the lib directory of myapp.jar.

To solve this issue, library jars which contain Jersey related classes need to be unpacked first before Spring Boot runs. This is supported by Spring Boot' Maven plugin using requiresUnpack, see doc.

Below is an example of the Maven pom.xml file.

<build>  
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <requiresUnpack>
                    <dependency>
                        <groupId>com.myapp</groupId>
                        <artifactId>rest-api</artifactId>
                    </dependency>
                </requiresUnpack>
            </configuration>
        </plugin>
    </plugins>
</build>  

Spring RestTemplate Basic Authentication

I'm using Spring RestTemplate to consume REST service with basic authentication, so I need a way to set the username and password. After running some searches, it turns out that it's not that easy to set the username and password directly. So I manually created the Authorization header.

import org.apache.commons.codec.binary.Base64;

HttpHeaders headers = new HttpHeaders();  
headers.set("Authorization", "Basic " + new String(Base64.encodeBase64((username + ":" + password).getBytes(Charset.forName("US-ASCII")))));  
HttpEntity<byte[]> entity = new HttpEntity<byte[]>(headers);  
ResponseEntity<byte[]> response = restTemplate.exchange(uri, HttpMethod.valueOf(httpMethod), entity, byte[].class);  

Spring Testing transaction management

Spring Testing is a good tool for testing application written using Spring framework. It has convenient built-in transaction management for integration tests. By default, Spring starts a transaction for each testing method and rollback the transaction after testing method is executed. Methods annotated with @Before and @After are also executed in the same transaction. By doing this, no change is actually made to the database, so you don't need to clean the database manually after each test.

Although this automatic transaction management is considered harmful for some cases, it's very handy for most cases. I did encounter some cases when you had to find workrounds.

In one test case, some database setup is required for all testing methods. So a method with @Before is created with necessary code to do that. In the acutal testing method, a background service is triggered to run some tasks and then the result is verified. In this case, the background service is running in a separate thread and reads data created in @Before method from database. But because @Before and current testing method is running in the same transaction, before the testing method finishes and the transaction is committed, the data changes are not written to database. So the background service cannot see the data and always fails.

Programmatic transaction management

To workaround this, I changed the @Before to @BeforeTransaction, which makes the method executed before the transaction starts. Then use programmatic transaction management to commit the transaction.

@Autowired
protected PlatformTransactionManager transactionManager;

@BeforeTransaction
public void setup() {  
  DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
  TransactionStatus transaction = transactionManager.getTransaction(definition);

  //database setup

  transactionManager.commit(transaction);
}

As shown in code above, data created in setup method is written to database and visible to following testing methods.

JUnit execution order

Another solution is to leverage test execution order introduced in JUnit 4.11. The idea is to make the @Before method as a testing method, but is executed before other testing methods.

@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MyTest {

  @Test
  @Transactional
  public void testMethod0() {
    //database setup
  }

  @Test
  @Transactional
  public void testMethod1() {
    //actual testing code
  }
}

As shown in the code above, defaultRollback of @TransactionConfiguration should be set to false, then database changes won't be rolled back. Use @FixMethodOrder(MethodSorters.NAME_ASCENDING) to tell JUnit to execute methods in ascending order of method names. testMethod0 is the method for database setup and is executed before the actual testing method testMethod1.

No automatic transaction management

If automatic transaction management introduces more trouble than it solves, you can just disable the automatic transaction management.

To disable automatic transaction management for a test class, use @TestExecutionListeners and exclude TransactionalTestExecutionListener.class from listeners. By default, TransactionalTestExecutionListener is included.

@TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
public class MyTest {

}

For individual test, use @Transactional(propagation = Propagation.NOT_SUPPORTED) to exclude single testing method from transaction.

Note

This actually shows a very common case in daily development. 95% of time, good framework , like Spring, can help you a lot. But the rest 5% of time, you'll need to find the answer youself. In this case, unfortunately 95% of online resources cannot help you. You have to dig into the reference guide and source code to find out the answer.