12 October, 2014

Me on GitHub

I'm moving my projects to GitHub.

Most of them are open and free for all.

Me on GitHub
My GitHub pages

Already moved projects:
  • MusicBox (sources and demo) - digital musicbox
  • Harmonica (sources and demo) - set of musical instruments
  • Xameleon (demo) - webcam application
  • XenoHarmonica (demo) - MIDI bayan (russian accordion) emulator
  • Pkmx (sources and demo) - JavaScript library for optimal blocks layout
  • WebTools (sources and demo) - collection of online web tools

28 November, 2013

Spring-boot JPA sample with auto-configured repository

Spring-boot allows to create a completely minimalistic applications.
For example, its auto-configuration ability raises and configures JPA and Hibernate-related stuff:
DataSource, Entity and Transaction Managers, Repositories.
With no manual configuration in your code or in xml files.

And the application code is getting smaller and smaller...

User.java
package sb; 
 
import javax.persistence.*; 
 
@Entity 
@Table(name = "sb_user") 
public class User { 
 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    public long id; 
    public String name; 
    public String alias; 
 
    public User() { 
    } 
 
    public User(long id, String name) {
        this.id = id; 
        this.name = name; 
    } 
} 
UserRepository.java
package sb;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface UserRepository extends JpaRepository<User, Long> {

    @Query("SELECT u FROM User u WHERE u.alias IS NOT NULL")
    List<User> findAliased();
} 
UserController.java
package sb;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("user")
public class UserController {
    protected final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private UserRepository users;

    @RequestMapping("test")
    public String test() {
        log.info("Test");
        return "OK";
    }

    @RequestMapping("user")
    public User getUser(@RequestParam("id") long id) {
        log.info("Get user");
        return users.findOne(id);
    }

    @RequestMapping("users")
    public List<User> getUsers(@RequestParam("ids") List<Long> ids) {
    log.info("Get users");
        return users.findAll(ids);
    }

    @RequestMapping("aliased")
    public List<User> getAliasedUsers() {
    log.info("Get aliased users");
        return users.findAliased();
    }
} 
Application.java
package sb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@EnableAutoConfiguration
@Configuration
@ComponentScan
public class Application {

    public static void main(String[] args) throws Throwable {
        new SpringApplication(Application.class).run(args);
    }
} 
application.properties
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://srv0/test
spring.datasource.username=test
spring.datasource.password=test 
schema.sql
-- DROP TABLE sb_user;

CREATE TABLE sb_user
(
  id   SERIAL PRIMARY KEY    NOT NULL,
  name CHARACTER VARYING(65) NOT NULL,
  alias CHARACTER VARYING(20)
);

INSERT INTO sb_user (name) VALUES ('Ben Linus');
INSERT INTO sb_user (name) VALUES ('Claire Littleton');
INSERT INTO sb_user (name) VALUES ('Desmond Hume');
INSERT INTO sb_user (name) VALUES ('Frank Lapidus');
INSERT INTO sb_user (name, alias) VALUES ('Hugo Reyes', 'Hurley');
INSERT INTO sb_user (name) VALUES ('Jack Shephard');
INSERT INTO sb_user (name, alias) VALUES ('James Ford', 'Sawyer');
INSERT INTO sb_user (name) VALUES ('Jin-Soo Kwon');
INSERT INTO sb_user (name) VALUES ('Miles Straume');
INSERT INTO sb_user (name) VALUES ('Richard Alpert');
INSERT INTO sb_user (name) VALUES ('Sayid Jarrah');
INSERT INTO sb_user (name) VALUES ('Sun-Hwa Kwon');



Build and run command: gradle clean build runJar

Test urls:
http://localhost:8080/user/user?id=2
http://localhost:8080/user/users?ids=1,3,5,7
http://localhost:8080/user/aliased

26 November, 2013

Spring-boot memory usage

Typical spring-boot application - single jar file.
Size of the file is about 10+ megabytes (it contains another jars, classes and resources inside).
What about memory usage for this app?
I created sample with only one web @Controller and two webmethods: first - reports memory usage, second - runs Java Garbage Collector.

Memory usage after start (http://localhost:8080/test/mem):
max: 1,875,378,176 bytes
total: 355,729,408 bytes
free:  178,138,976 bytes
used:  177,590,432 bytes
and after GC (http://localhost:8080/test/rungc):
max: 1,875,378,176 bytes
total: 355,729,408 bytes
free:  331,821,496 bytes
used:   23,907,912 bytes
With spring-boot we have a lot of available memory)

Controller.java
package sb; 
 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RestController; 
 
@RestController 
@RequestMapping("test") 
public class Controller { 
 
    @RequestMapping(value = "mem", produces = "text/plain")
    public String mem() { 
        long max = Runtime.getRuntime().maxMemory(); 
        long total = Runtime.getRuntime().totalMemory(); 
        long free = Runtime.getRuntime().freeMemory(); 
        long used = total - free; 
        return String.format("Memory:\n" +
                "max: %,d bytes\n" +
                "total: %,d bytes\n" +
                "free: %,d bytes\n" +
                "used: %,d bytes\n",
                max, total, free, used); 
    } 
 
    @RequestMapping("rungc") 
    public String rungc() { 
        Runtime.getRuntime().gc(); 
        return "Run GC";
    } 
} 


Application.java
package sb; 
 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
 
@EnableAutoConfiguration 
@Configuration 
@ComponentScan 
public class Application { 
 
    public static void main(String[] args) throws Throwable {
        new SpringApplication(Application.class).run();
    } 
} 

build.gradle
buildscript { 
    repositories { 
        maven { url "http://repo.spring.io/libs-snapshot" }
    } 
    dependencies { 
        classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M6")
    } 
} 
 
apply plugin: "java" 
apply plugin: "spring-boot" 
 
buildDir = "out" 
 
jar { 
    baseName = "sb-mem" 
    version = "0.1" 
} 
 
repositories { 
    mavenCentral() 
    maven { url "http://repo.spring.io/libs-snapshot" }
} 
 
dependencies { 
    def springBootVersion = '0.5.0.M6'
    compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
} 

25 November, 2013

Spring-boot change log-file location

By default spring-boot writes logs to the /tmp/spring.log file.
But if you need another location - just set LOG_FILE variable in logback.xml configuration.

logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 
    <property name="LOG_FILE" value="tmp/application.log"/>
 
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
 
    <logger name="sb" level="DEBUG"/>
 
</configuration> 

Application.java
package sb;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
 
@Configuration 
public class Application {
 
    @Service 
    static class TestBean {
        protected final Logger log = LoggerFactory.getLogger(getClass());
 
        void test() {
            log.debug("TestBean.test");
        } 
    } 
 
 
    public static void main(String[] args) throws Throwable {
        SpringApplication app = new SpringApplication(Application.class);
        ConfigurableApplicationContext ctx = app.run(args); 
 
        TestBean bean = ctx.getBean(TestBean.class);
        bean.test(); 
    } 
} 

build.gradle
buildscript {
    repositories { 
        maven { url "http://repo.spring.io/libs-snapshot" }
    } 
    dependencies { 
        classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M6")
    } 
} 
 
apply plugin: "java"
apply plugin: "spring-boot"
 
buildDir = "out"
 
jar { 
    baseName = "sb-log-file"
    version = "0.1"
} 
 
repositories { 
    mavenCentral() 
    maven { url "http://repo.spring.io/libs-snapshot" }
} 
 
dependencies { 
    def springBootVersion = '0.5.0.M6'
    compile("org.springframework.boot:spring-boot-starter:$springBootVersion")
}

21 November, 2013

Spring-boot JDBC with multiple DataSources sample

Spring-Boot's auto-configurer seems good for simple applications. For example it automatically creates DataSource and JdbcTemplate, when you need to connect to the database. But what about less trivial configuration, when you have several different databases?

I found the way to have multiple DataSources for Spring-Boot-based application.

In the sample below I have two special (db-related) Configurations, one properties file with connections' parameters and two Repositories.

Each @Repository connects with appropriate database through separate DataSource.
application.properties
spring.ds_items.driverClassName=org.postgresql.Driver 
spring.ds_items.url=jdbc:postgresql://srv0/test 
spring.ds_items.username=test0 
spring.ds_items.password=test0 
 
 
spring.ds_users.driverClassName=org.postgresql.Driver 
spring.ds_users.url=jdbc:postgresql://srv1/test 
spring.ds_users.username=test1 
spring.ds_users.password=test1 



DatabaseItemsConfig.java
package sb; 
 
import org.springframework.boot.autoconfigure.jdbc.TomcatDataSourceConfiguration; 
import org.springframework.boot.context.properties.ConfigurationProperties; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.jdbc.core.JdbcTemplate; 
 
import javax.sql.DataSource; 
 
@Configuration 
@ConfigurationProperties(name = "spring.ds_items") 
public class DatabaseItemsConfig extends TomcatDataSourceConfiguration { 
 
    @Bean(name = "dsItems") 
    public DataSource dataSource() { 
        return super.dataSource(); 
    } 
 
    @Bean(name = "jdbcItems") 
    public JdbcTemplate jdbcTemplate(DataSource dsItems) { 
        return new JdbcTemplate(dsItems); 
    } 
} 


DatabaseUsersConfig.java
package sb; 
 
import org.springframework.boot.autoconfigure.jdbc.TomcatDataSourceConfiguration; 
import org.springframework.boot.context.properties.ConfigurationProperties; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.jdbc.core.JdbcTemplate; 
 
import javax.sql.DataSource; 
 
@Configuration 
@ConfigurationProperties(name = "spring.ds_users") 
public class DatabaseUsersConfig extends TomcatDataSourceConfiguration { 
 
    @Bean(name = "dsUsers") 
    public DataSource dataSource() { 
        return super.dataSource(); 
    } 
 
    @Bean(name = "jdbcUsers") 
    public JdbcTemplate jdbcTemplate(DataSource dsUsers) { 
        return new JdbcTemplate(dsUsers); 
    } 
 
} 


ItemRepository.java
package sb; 
 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.jdbc.core.JdbcTemplate; 
import org.springframework.jdbc.core.RowMapper; 
import org.springframework.stereotype.Repository; 
 
import java.sql.ResultSet; 
import java.sql.SQLException; 
 
@Repository 
public class ItemRepository { 
    protected final Logger log = LoggerFactory.getLogger(getClass()); 
 
    @Autowired 
    @Qualifier("jdbcItems") 
    protected JdbcTemplate jdbc; 
 
    public Item getItem(long id) { 
        return jdbc.queryForObject("SELECT * FROM sb_item WHERE id=?", itemMapper, id); 
    } 
 
    private static final RowMapper<Item> itemMapper = new RowMapper<Item>() {
        public Item mapRow(ResultSet rs, int rowNum) throws SQLException { 
            Item item = new Item(rs.getLong("id"), rs.getString("title")); 
            item.price = rs.getDouble("id"); 
            return item; 
        } 
    }; 
} 


UserRepository.java
package sb; 
 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.jdbc.core.JdbcTemplate; 
import org.springframework.jdbc.core.RowMapper; 
import org.springframework.stereotype.Repository; 
 
import java.sql.ResultSet; 
import java.sql.SQLException; 
 
@Repository 
public class UserRepository { 
    protected final Logger log = LoggerFactory.getLogger(getClass()); 
 
    @Autowired 
    @Qualifier("jdbcUsers") 
    protected JdbcTemplate jdbc; 
 
    public User getUser(long id) { 
        return jdbc.queryForObject("SELECT * FROM sb_user WHERE id=?", userMapper, id); 
    } 
 
    private static final RowMapper<User> userMapper = new RowMapper<User>() {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException { 
            User user = new User(rs.getLong("id"), rs.getString("name")); 
            user.alias = rs.getString("alias"); 
            return user; 
        } 
    }; 
} 


Controller.java
package sb; 
 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.bind.annotation.RestController; 
 
@RestController 
public class Controller { 
    protected final Logger log = LoggerFactory.getLogger(getClass()); 
 
    @Autowired 
    private UserRepository users; 
 
    @Autowired 
    private ItemRepository items; 
 
    @RequestMapping("test") 
    public String test() { 
        log.info("Test"); 
        return "OK"; 
    } 
 
    @RequestMapping("user") 
    public User getUser(@RequestParam("id") long id) { 
        log.info("Get user"); 
        return users.getUser(id); 
    } 
 
    @RequestMapping("item") 
    public Item getItem(@RequestParam("id") long id) { 
        log.info("Get item"); 
        return items.getItem(id); 
    } 
 
} 


Application.java
package sb; 
 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
 
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class) 
@Configuration 
@ComponentScan(basePackages = "sb") 
public class Application { 
 
    public static void main(String[] args) throws Throwable { 
        SpringApplication app = new SpringApplication(Application.class); 
        app.run(); 
    } 
}