File Download Component – Java

Problem Definition

Many a time we need to provide file download feature in our application, and it perfectly makes sense to create a component that can be reused.

Solution

This component is created for spring boot web application, although you can customize the code or get the idea for the reusable component.

The Component

FileDownloadHelper this is the main helper class that downloads the file or more technically writes file content on http response.

public class FileDownloadHelper {

    public static ResponseEntity downloadFile(FileResource file, WritableStream stream) {
        Assert.notNull("file and stream parameters are not optional.", file, stream);

        if (!file.exists())
            return ResponseEntity.notFound().build();

        byte[] content = file.getContent();

        if (!FileUtil.verifyChecksum(content, file.getChecksum())) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Verify checksum fail");
        }

        stream.setContentType(file.getContentType());
        stream.write(content);

        return ResponseEntity.ok().build();
    }

}

Supporting Implementaion

FileResource
This allows to wrap underlying complexity of file resource and expose common access pattern.

public interface FileResource {

    String getPath();

    String getChecksum();

    String getContentType();

    boolean exists();

    byte[] getContent();

    InputStream getStream();

}

WritableStream
This allows to wrap writable resource (ex: servlet response) to hide complexity and expose common access pattern.

public interface WritableStream {

    void setContentType(String type);

    void write(byte[] content);
}

In Action

Sample code to download file from google cloud storage.

GoogleFileResource

public final class GoogleFileResource implements FileResource {
    private Blob blob;
    private FileMeta meta;

    public GoogleFileResource(Blob blob, FileMeta meta) {
        this.blob = blob;
        this.meta = meta;
    }

    @Override
    public String getPath() {
        return blob.getName();
    }

    @Override
    public String getChecksum() {
        return meta.getChecksum();
    }

    @Override
    public String getContentType() {
        return meta.getType();
    }

    @Override
    public boolean exists() {
        return blob.exists();
    }

    @Override
    public byte[] getContent() {
        return blob.getContent();
    }

    @Override
    public InputStream getStream() {
        return null;
    }

}

ServletWritableStream

public class ServletWritableStream implements WritableStream {
    private HttpServletResponse response;

    public ServletWritableStream(HttpServletResponse response) {
        Assert.notNull(response);
        this.response = response;
    }

    @Override
    public void setContentType(String type) {
        response.setContentType(type);
    }

    @Override
    public void write(byte[] content) {
        try {
            response.getOutputStream().write(content);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

Final Piece

@GetMapping
public ResponseEntity getFile(@PathVariable String consultationReference,
            HttpServletResponse response) {
        return service.getFile(consultationReference)
                .map(file -> FileDownloadHelper.downloadFile(file, new ServletWritableStream(response)))
                .orElse(ResponseEntity.notFound().build());
    }
Advertisements

Artifactory with Multi-Module Gradle Project

Prerequisites

1. Oracle JDK8

Environment Details

1. OS – Ubuntu 16.04 x64 Server
2. Artifactory – oss-5.3.0

Problem

Publish your jar from Multi-module gradle project and use in other projects.

Steps

Install Artifactory

Start Artifactory

  • Goto artifactory home. (to extracted location)
  • run – bin/artifactory.sh

Setup repository

Your framework project parent/root build.gradle


buildscript {
  repositories {
    jcenter {
      url 'http://localhost:8081/artifactory/jcenter/'
    }
  }

  dependencies {
    //Check for the latest version here: http://plugins.gradle.org/plugin/com.jfrog.artifactory
    classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+"
  }
}

allprojects {
  apply plugin: 'maven'

  group = 'com.example'
  version = '1.0.0-SNAPSHOT'

  ext {
    springBootVersion = '1.5.2.RELEASE'
  }
}

subprojects {
  apply plugin: 'java'
  apply plugin: 'maven-publish'
  apply plugin: "com.jfrog.artifactory"

  sourceCompatibility = 1.8
  targetCompatibility = 1.8
  tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
  }

  repositories {
    jcenter {
      url 'http://localhost:8081/artifactory/jcenter/'
    }
  }

  dependencies {
    testCompile group: 'junit', name: 'junit', version:'4.12'
  }

  artifactory {
    contextUrl = "${artifactory_contextUrl}" //The base Artifactory URL if not overridden by the publisher/resolver
    publish {
      repository {
        repoKey = 'example-framework-snapshot-local'
        username = "${artifactory_user}"
        password = "${artifactory_password}"
        maven = true
      }
      defaults {
        publications ('mavenJava')
      }
    }

    resolve {
      repository {
        repoKey = 'example-framework-snapshot'
        username = "${artifactory_user}"
        password = "${artifactory_password}"
        maven = true
      }
    }
  }

  publishing {
    publications {
      mavenJava(MavenPublication) {
        from components.java
      }
    }
  }
}

build.gradle for user of your Framework Code

Assuming your virtual repository is ‘example-framework-snapshot’

buildscript {
  ext {
    springBootVersion = '1.5.2.RELEASE'
  }
  repositories {
    maven {
      // Your virtual repository
      url 'http://localhost:8081/artifactory/example-framework-snapshot'
    }
  }
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  }
}

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'war'
apply plugin: 'org.springframework.boot'

group = 'com.security.sample'
version = '0.0.1-SNAPSHOT'

description = """sample-security Webapp"""

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
  maven {
    url 'http://localhost:8081/artifactory/example-framework-snapshot'
  }
}

dependencies {
  compile ('com.example:example-security-extras:0.0.1-SNAPSHOT')
  compile ('org.springframework.boot:spring-boot-starter-web')
  compile ('redis.clients:jedis')
  testCompile ('junit:junit')
}

Sorting and Cross Join Problem – Spring Data JPA

Affected Version

Spring boot – 1.2.2
Spring data JPA version – 1.7.2
Spring data common – 1.9.2

Problem Statement

When you use custom query using @Query in your spring data JPA repository and pass sort parameter, where sort parameter is a child attribute; the query generated uses cross join instead of left outer join. This gives unexpected result.

Problem Sample


@Query("SELECT e FROM EventApprovalRequest e WHERE e.approvalRequestType = (:approvalRequestType) "
+ "AND e.status = (:status)")
Page getEventsForApproval(
@Param("approvalRequestType") int approvalRequestType, @Param("status") int status, Pageable pageable);

Solution

Pre-define left outer join in your query.

@Query("SELECT e FROM EventApprovalRequest e LEFT OUTER JOIN e.event LEFT OUTER JOIN e.event.director WHERE e.approvalRequestType = (:approvalRequestType) "
+ "AND e.status = (:status)")
Page getEventsForApproval(
@Param("approvalRequestType") int approvalRequestType, @Param("status") int status, Pageable pageable);

Get Enum type with id

Here is a simple technique to retrieve Enum type using custom ‘id’ property instead of name.

Define Contract

/**
 * Contract that will allow Types with id to have generic implementation.
 */
public interface IdentifierType<T> {
  T getId();
}

Apply Contract

public enum EntityType implements IdentifierType<Integer> {
  ENTITY1(1, "ONE), ENTITY2(2, "TWO");

  private Integer id;
  private String name;

  private EntityType(int id, String name) {
    this.id = id;
    this.name = name;
  }

  public static EntityType valueOf(Integer id) {
    return EnumHelper.INSTANCE.valueOf(id, EntityType.values());
  }

  @Override
  public Integer getId() {
    return id;
  }
}

Make use of contract (helper)

public enum EnumHelper {
  INSTANCE;

  /**
   * This will return {@link Enum} constant out of provided {@link Enum} values with the specified id.
   * @param id the id of the constant to return.
   * @param values the {@link Enum} constants of specified type.
   * @return the {@link Enum} constant.
   */
  public <T extends IdentifierType<S>, S> T valueOf(S id, T[] values) {
    if (!values[0].getClass().isEnum()) {
        throw new IllegalArgumentException("Values provided to scan is not an Enum");
    }

    T type = null;

    for (int i = 0; i < values.length && type == null; i++) {
        if (values[i].getId().equals(id)) {
            type = values[i];
        }
    }

    return type;
  }
}

Search Framework Idea – Java

Introduction

Writing code to implement search in application is a repetitive task, and need understanding of query language provided by underlying search engine. Search framework reduces the development effort and hides implementation complexity by leveraging good Object Oriented principles. It helps to make your design extensible and maintainable.

The solution built using following design patterns

  • Builder
  • Factory
  • Template
  • Strategy

 

Architecture

SearchFramework

Core Components

It consist of interfaces defining contract for searcher, and beans defining search request object format.

Code Sample

public interface Searcher {
   T search(SearchCriteria criteria);
}

public class SearchCriteria {
   private int groupCondition;
   private List searchFilters;
}

public class SearchFilter {
   private String fieldName;
   private String fieldValue;
   private int filterCondition;
}

Provider Integration

In this layer we do provider specific implementation, such that it is implemented once and reused.

Sample for JPA Searcher

public abstract class JPASearcher implements Searcher {
   @Autowired
   private PredicateBuilder predicateBuilder;

   protected Specification getSpecification(final SearchCriteria searchCriteria) {
     return new Specification() {

      @Override
       public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery,
 CriteriaBuilder criteriaBuilder) {
         criteriaQuery.distinct(true);
         return predicateBuilder.build(searchCriteria, criteriaBuilder, root);
       }

   };
}

JPA Predicate builder

@Component
public class PredicateBuilder {
   @Autowired
   private PredicateConditionBuilderFactory conditionBuilderFactory;
   
   public Predicate build(SearchCriteria criteria, CriteriaBuilder cb, Root root) {
     …
     for (SearchFilter searchFilter : criteria.getSearchFilters()) {
       …
     }
     if (criteria.getGroupCondition() == GroupCondition.AND) {
       predicate = cb.and(predicates.toArray(new Predicate[predicates.size()]));
     } else {
       predicate = cb.or(predicates.toArray(new Predicate[predicates.size()]));
     }
   return predicate;
   }
}
 

JPA Condition Builder

public interface PredicateConditionBuilder {

 Predicate build(SearchFilter filter, CriteriaBuilder criteriaBuilder, Root root,
 final DistinctJoinHolder joinHolder);

}

Sample Implementation

public class IsNotNullBuilder implements PredicateConditionBuilder {

private final ConditionBuilderHelper builderHelper = ConditionBuilderHelper.INSTANCE;

@Override
public  Predicate build(SearchFilter filter, CriteriaBuilder criteriaBuilder, Root root,
 final DistinctJoinHolder joinHolder) {
    String fieldName = filter.getField().getSearchFieldName();
    GroupCondition groupCondition = filter.getSearchCriteria().getGroupCondition();
    Path path = builderHelper.getPath(fieldName, root, groupCondition, joinHolder);
    return criteriaBuilder.isNotNull(path);
  }
}

JPA Condition Builder Factory

@Component
public class PredicateConditionBuilderFactory {

  private Map builders;

  @PostConstruct
  protected void init() {
     builders = new HashMap();
     builders.put(FilterCondition.EQUAL, new EqualBuilder());
     builders.put(FilterCondition.NOT_EQUAL, new NotEqualBuilder());
     builders.put(FilterCondition.GT, new GreaterThanBuilder());
     builders.put(FilterCondition.LT, new LessThanBuilder());
     builders.put(FilterCondition.EQ_GT, new GreaterThanEqualBuilder());
     builders.put(FilterCondition.EQ_LT, new LessThanEqualBuilder());
     builders.put(FilterCondition.BETWEEN, new BetweenBuilder());
     builders.put(FilterCondition.LIKE, new LikeBuilder());
     builders.put(FilterCondition.IN, new InBuilder());
     builders.put(FilterCondition.IS_NULL, new IsNullBuilder());
     builders.put(FilterCondition.IS_NOT_NULL, new IsNotNullBuilder());
   }

  public PredicateConditionBuilder getBuilder(FilterCondition filterCondition) {
     return builders.get(filterCondition);
  }

}

Search Configuration

It is not used directly by core framework, but needed by various search utilities – for example search criteria builder utility may want to get list of fields supported in application’s basic search, or may be for validation configuration.

Search Utilities

This layer consist of various search supporting utilities –

  • Search criteria builder & validator.
  • Search filter builder & validator.

Simple JPA Searcher implementation (Search Services)

@Component
public class EventSearcher extends JPASearcher {
   @Autowired
   private EventRepository repository;

   @Override
   public List search(SearchCriteria criteria) {
     return repository.findAll(getSpecification(criteria));
   }
}

All you have to do is

  • Extend JPASearcher and provide entity to search.
  • Provide jpa repository for entity.
  • Call search with search criteria.

Simple Lambda – Java8

public class LambdaSample {
  protected interface SimpleCalc {
    int operation(int a, int b);
  }
  public static void main(String[] args) {
    /*
     * Old style
     */
    SimpleCalc add = new SimpleCalc() {
    @Override
    public int operation(int a, int b) {
      return a + b;
    }
  };
  System.out.println(add.operation(2, 5));
  /*
   * New style - Lambda Expression
   */
  SimpleCalc multiply = (a, b) -> a * b;
  System.out.println(multiply.operation(2, 5));
  }
}

In the above code you can see the old vs new style of anonymous function creation. Lambda expression makes it short, concise and readable. [its look readable to me :)]

Building Cloudera cdh5.1.3 – Hadoop Native – [Ubuntu 14.04 Server x64]

WARN: Unable to load native-hadoop library for your platform… using builtin-java classes where app

On starting Cloudera cdh5 hadoop if you get above warning – then either your hadoop native is not on path or you don’t have native library available in your distribution.

For case 1: solution is simple.

export HADOOP_OPTS="$HADOOP_OPTS -Djava.library.path=$HADOOP_HOME/lib/native"

For case 2: follow below steps

  1. Install jdk7 and maven [skip if already installed]
  2. sudo apt-get install build-essential
  3. apt-get install cmake
  4. apt-get install pkg-config
  5. apt-get install libcurl4-openssl-dev
  6. install protobuf-2.5.0.tar.gz
  7. Goto “hadoop-2.3.0-cdh5.1.3/src” folder and run
    mvn package -Pdist,native -DskipTests -Dtar

check “hadoop-2.3.0-cdh5.1.3/src/hadoop-dist/target/hadoop-2.3.0-cdh5.1.3/lib/native”
or you can use – “hadoop-2.3.0-cdh5.1.3/src/hadoop-dist/target/hadoop-2.3.0-cdh5.1.3.tar.gz”