Category Archives: Java

MyBatis dynamic “union all”

Take a look at the following union sql fragment. What if includeFoo is false?

    <sql id="dynamic_union">
      

            <if test="includeFoo">
                (select * from foo)
            
            </if>

            <if test="includeBar">
                union all
                (select * from bar)
            </if>
 

    </sql>

You will end up with

union all (select * from bar)

, which is not legal sql. There must be a result set before “union all”

The solution is to use <trim/> tag

    <sql id="dynamic_union">
        <trim  prefixOverrides="union all">


            <if test="includeFoo">
                (select * from foo)
            
            </if>

            <if test="includeBar">
                union all
                (select * from bar)
            </if>
 

        </trim>


    </sql>

It means: if what’s inside <trim/> starts with “union all”, then “union all” will be removed

Code snippet: subListSafely()

    /**
     * @param list      can be null
     * @param fromIndex inclusive
     * @param endIndex  exclusive
     * @param <T>
     * @return
     */
    public static <T> List<T> subListSafely(List<T> list, int fromIndex, int endIndex) {
        if(list == null){
            return new ArrayList<>();
        }

        if(fromIndex < 0 || endIndex <= 0){
            return new ArrayList<>();
        }

        if (list.size() - 1 < fromIndex) {
            return new ArrayList<>();
        }
        return list.subList(fromIndex, Math.min(endIndex, list.size()));
    }
}

And the unit test:

    @Test
    public void subListSafely(){
        // null list
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(null, 0, 3).toArray());

        //empty list
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(new ArrayList<String>(), 0, 3).toArray());
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(new ArrayList<String>(), 1, 3).toArray());

        //non-empty list, increasing fromIndex one by one
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), -1, 3).toArray());
        assertArrayEquals(new Object[]{"a", "b", "c"}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 0, 3).toArray());
        assertArrayEquals(new Object[]{"b", "c"}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 1, 3).toArray());
        assertArrayEquals(new Object[]{"c"}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 2, 3).toArray());
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 3, 3).toArray());
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 4, 3).toArray());

        //non-empty list, decrease endIndex one by one
        assertArrayEquals(new Object[]{"a", "b", "c"}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 0, 4).toArray());
        assertArrayEquals(new Object[]{"a", "b", "c"}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 0, 3).toArray());
        assertArrayEquals(new Object[]{"a", "b"}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 0, 2).toArray());
        assertArrayEquals(new Object[]{"a"}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 0, 1).toArray());
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 0, 0).toArray());
        assertArrayEquals(new Object[]{}, MyCollectionUtils.subListSafely(Arrays.asList("a", "b", "c"), 0, -1).toArray());
    }

Use Flyway in a multi-module gradle project

Requirements

  • Some of the sub-projects need to run flyway as a gradle task
  • We want a single command line to do the flyway migrations for all the sub-projects mentioned above

Solution

  • Apply flyway plugin in these sub-projects
  • Config flyway in these sub-projects
  • Create a root-project task to call the flyway tasks in the sub-projects

Code

buildscript {
    dependencies {
        classpath 'org.postgresql:postgresql:42.2.10' //or any other jdbc driver
}

plugins {
    id "org.flywaydb.flyway" version "6.2.4" apply false //get it in but donot apply it for root project
}

task dbMigrate(){ //root project task
    dependsOn "foo:flywayMigrate"
}

...
project(':foo') {  //a sub project
    apply plugin: "org.flywaydb.flyway" 
    def dbHost = findProperty('fooDbHost')

    flyway{
        url = "jdbc:postgresql://${dbHost}:5432/xxx"
        user = findProperty('fooDbUsername')
        password = findProperty('fooDbPassword')
    }
...
}

...
project(':bar') {  //a sub project
    apply plugin: "org.flywaydb.flyway" 
    def dbHost = findProperty('barDbHost')

    flyway{
        url = "jdbc:postgresql://${barDbHost}:5432/xxx"
        user = findProperty('barDbUsername')
        password = findProperty('barDbPassword')
    }
...
}

Now run it on the root level of your project:

./gradlew dbMigrate -PfooDbHost=xxx -PfooDbUsername=xxx -PfooDbPassword=xxx  -PbarDbHost=xxx -PbarDbUsername=xxx -PbarDbPassword=xxx





Generate assertEquals(xxx, bean.getXxx()) for all the properties of a javabean

In intellij, create such a live template:

    public static void main(String args[]) {
        Class<?> clazz = $Bean$.class;
        String object = "$bean$";
        java.lang.reflect.Method[] methods = clazz.getMethods();
        for (java.lang.reflect.Method m : methods) {
            if ((m.getName().startsWith("get") || m.getName().startsWith("is") )&&  (!m.getName().equals("getClass")) &&  m.getParameterTypes().length == 0) {
                System.out.println(
                    "assertEquals(\"xxx\"," + object + "." + m.getName() + "()); "
                );
            }
        }

    }

Make sure chromedriver can run with Java selenium code, both in a real machine and in docker

Install chromedriver

You want to make the following line of code run successfully.

ChromeDriver driver = new ChromeDriver();

In windows, you need to download chrome driver from https://chromedriver.chromium.org/downloads and put it in system’s path (An alternative is to set the executable file’s path as a system property “webdriver.chrome.driver”, not beautiful)

In *nix or linux-based Docker, you can

apt update
apt install chromedriver -y   #for debian-based systems 

Options needed for ChromeDriver

        ChromeDriverService chromeDriverService = new ChromeDriverService.Builder().withWhitelistedIps("").build();//without this you may see " bind() returned an error, errno=99: Cannot assign requested address (99) "

...
        // without the following you may see "DevToolsActivePort file doesn't exist" 
        ChromeOptions chromeOptions = new ChromeOptions();
        if(isUnixBased()) {
            chromeOptions.addArguments("--headless");
            chromeOptions.addArguments("--no-sandbox");
            chromeOptions.addArguments("--disable-dev-shm-usage");
        }

...
        ChromeDriver driver = new ChromeDriver(chromeDriverService, chromeOptions);

					

Use spring +aspectj in Spring boot

compile('org.springframework.boot:spring-boot-starter-aop')
compile('org.aspectj:aspectjrt')
compile('org.aspectj:aspectjweaver')
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class YourApplication ...{
...
}
@Component
@Aspect
public class YourAspect {

	@Before("execution(public * com..*Manager.*(..))")
	public void doAdvice(JoinPoint joinPoint) {...}
	 
}

Springfox for 204 response

What you need to,

  • Return ResponseEntity<Void>
  • Set the @ResponseStatus for your rest controller’s method

If you don’t do that, you will get ResponseEntity as the response class in your swagger json, and you will get both 200 and 204 as your successful response code, which is not correct because your method only returns 204, no 200.

    @RequestMapping(value = "...", method = RequestMethod.POST)
    @ApiOperation(value = "...")
    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "success")
    })
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public ResponseEntity<Void> someMethod(){}

Jetty’s start.jar forks a new JVM to run the app if logback module is enabled

Jetty is too smart. If there is any JVM option provided, Jetty will spawn a new JVM instance to do the real work, even if you don’t use “–exec” option in start.ini

And jetty’s logback module introduces a JVM option. See here

You will see:

-Dorg.eclipse.jetty.util.log.class?=org.eclipse.jetty.util.log.Slf4jLog 

So does it matter?

It matters a lot because all the JVM options you passed to start.jar will stay on the process of start.jar. They are not passed to the forked JVM process.

What’s the solution ?

Since you have to let it fork a new JVM because of logback, you can just put all the JVM options in start.ini. Like,


--exec
-server
-Xms2g
-Xmx2g
-XX:+UseZGC


Code snippet: Swagger + Spring Boot

compile 'io.springfox:springfox-swagger2:2.9.2'
compile 'io.springfox:springfox-swagger-ui:2.9.2'
/**
 See https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
 */
 @Configuration
 @EnableSwagger2
 public class SwaggerConfig {
 @Value("${enable.swagger}")  //You may want to disabled it in PROD
 boolean enableSwagger;
 @Bean
 public Docket api() {
     Docket api = new Docket(DocumentationType.SWAGGER_2)
             .select()
             .apis(RequestHandlerSelectors.basePackage("your.package"))
             .paths(PathSelectors.any())
             .build();
        ApiInfo apiInfo = new ApiInfoBuilder()		
                .version("xxx")
                .description("xxx")
                .termsOfServiceUrl("xxx")
                .title("Xxx Doc")
                .build();

        api.useDefaultResponseMessages(false).apiInfo(apiInfo);

        api.enable(enableSwagger);

        return api;
    }
}


Generate PDF from server side using Java

You can use OpenPDF to draw a PDF (the open source fork of iText). However, the learning curve of creating PDF with layouts and stuff is very high.

A more cost-effective solution is create an HTML version of it and convert it using Flying Saucer with its OpenPDF component

. As a Java developer you are already familiar with html + css, so doing that is quick.

There are some limitations and side effects of this approach

  • Your html must be XML/XHTML
  • Styles’ version must be no more than CSS 2.1. (So you can’t use flexbox layout)
  • No javascript
  • More: https://flyingsaucerproject.github.io/flyingsaucer/r8/guide/users-guide-R8.html#xil_6
  • The image quality seems worse than what you see on the html page, if there is any

And to create a PDF, your html can’t be too wide, otherwise content may be lost.

Typically, say you are creating an A4 PDF with margin of 0.1 inch on both left and right, the width of your html should not be more than (A4’s paper width – margin) * pixe density = (8.27inch – 0.1 inch * 2) * 96 DPI = 774 pixels

And here is how to set up the size and margin of the PDF in your html:

<head>
    <style>
        @page {
            margin-left: 9px;   /* about 0.1 inch */ 
            margin-right: 9px; 
            size: A4;
        }
    </style>
   ...