home.

tagged: java

Java and Android: Dagger 2 introduction (including Component.Builder)

Let's work through an example of Dagger 2. I'm doing it this in an Android project but for simplicity we'll be treating the project like a normal Java project for the most part: there will be no Activity or Fragment specific code. First this this in our gradle.build file:

annotationProcessor 'com.google.dagger:dagger-compiler:2.20'
implementation 'com.google.dagger:dagger:2.20'

We have a Drink class. This has a taste method. The taste method prints out the ingredients in the drink. The Ingredients are held as a instance method in the class. Normally we would pass the ingredients in a constructor parameters. But instead we use the @Inject annotation on a member field. Later Dagger will inject an implmentation of Ingredients here. (We must also annotate Drink with @Inject to keep Dagger happy when creating Drink.)

public class Drink {

    public static interface Ingredients {
        List<String> getAll();
    }

    @Inject
    public Drink() {}

    @Inject
    Ingrediants ingredients;

    public void taste() {
        System.out.println("Our drink contains: " + ingredients.getAll());
    }
}

We now use a Dagger Module to define our dependencies. In our case, our dependency is Ingredient. We make a plain class and annotate it with @Module. In that class we have a method that's annotated with @Provides and that method returns our Ingredients. The name of method could actually be anything - the only thing that matters is the return type.
 
(Note: we are explicity detailing our ingredients implementation in the module. Later we'll learn how to do this in a more dynamic way)
 
We must have a Component class. This let's us begin our dependency injection. You need to create an interface. And this is annotated with @Component. And the parameters to that annotation is our Module that we created above. Inside our component we have an inject method. The paramter is the type of class which we will use our dependency injection.

@Component(modules = DrinkModule.class)
interface BarComponent {

  void inject(MainActivity activity);

  @Module
  public class DrinkModule {

    @Provides public Ingredients providesIngredients() {
      return new Ingrediants() {
          @Override
          public List<String> getAll() {
              return Arrays.asList("GIN", "Orange Juice", "Coke");
          }
      };
    }
  }
}

We've made our module with the dependencies that lives inside our component. So we can now use the component. Dagger automatically creates a class DaggerBarComponent that's based on our BarComponent class. We then call the builder() method that returns a builer. We're not currently doing anything special so we call 'build()'. And we've built our component, we call the inject method -- passing in our current class.
 
At the point, any instance methods in the current class that have the @Inject annotation will be initiated by Dagger. And importantly its dependency's will be resolved. For instance, we have @Inject Drink drink and Dagger will create Drink, look at all the instance methods, for example, @Inject Ingredients ingredient, and resolve those using Dagger's component and module.

@Inject Drink drink;
...
DaggerBarComponent
  .builder()
  .inject(this);
...
drink.taste()

We previously hard-coded our Ingredients "GIN", "Orange Juice" and "Coke" in our module. We can instead pass those into our component. We do this using a dagger factory. We do this using an interface annnotated with @Component.Builder. We have a method that returns that interface. It also has an argument of, in our case, List<String> ingredeients. We must have an extra method called build that returns the component. The second thing we do is alter the providesIngredients method in our module. This method now takes in the List<String> ingredients and uses it.

@Component(modules = { BarComponent.DrinkModule.class })
interface BarComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance Builder ingredients(List<String> ingredients);
        BarComponent build();
    }

    void inject(MainActivity activity);

    @Module
    class DrinkModule {

        @Provides
        public Drink.Ingredients providesIngredients(final List<String> ingredients) {
            return new Drink.Ingredients() {
                @Override
                public List<String> getAll() {
                    return ingredients;
                }
            };
        }

    }
}

Now when we build our module we can pass in the List<String> ingredients:

DaggerBarComponent
  .builder()
  .ingredients(Arrays.asList("Vodka", "Rum", "Coke"))
  .build()
  .inject(this);

You can also then use Android's build types to swap in or out the depedencies depending on the build type:

List<String> ingredients = null;
if(BuildConfig.BUILD_TYPE.equals("teeTotal")) {
    ingredients = Arrays.asList("Orange", "Soda", "Pineapple");
} else if(BuildConfig.BUILD_TYPE.equals("alcoholic")) {
    ingredients = Arrays.asList("Vodka", "Rum", "Coke");
}

DaggerBarComponent
  .builder()
  .ingredients(ingredients)
  .build()
  .inject(this);

drink.taste();

By specifying two build types in your app/build.gradle file:

...
android {
  ...
    buildTypes {
      ..
      teeTotal {
        initWith debug
      }
      alcoholic {
        initWith debug
      }
    }
  ...
}
...

And then using adb to either ./gradlew installAlcoholic or ./gradlew installTeeTotal. And then you can see the different output in logcat.

java dagger android


Java: Using autovalue

Google's AutoValue helps us create a Java builder, amongst other things.
 
Let's first include it in our build.gralde:

implementation "com.google.auto.value:auto-value-annotations:1.6.1"
annotationProcessor 'com.google.auto.value:auto-value:1.6.1'

Then use the @AutoValue annotation on our abstract POJO with abstract methods.
 
And @AutoValue.Builder on a static abstract inner class that has abstract setters and a build method.

@AutoValue
public abstract class SomePOJO {
    public abstract String name();
    public abstract int age();

    @AutoValue.Builder
    public abstract static class Builder {
        public abstract SomePOJO build();
        public abstract Builder name(String name);
        public abstract Builder age(int age);
    }

    public static SomePOJO build() {
        return new AutoValue_SomePOJO.Builder()
                .age(100)
                .name("Dave")
                .build();
    }
}

We can then use the generated AutoValue_SomePOJO object to use the Builder class and methods.
 
If we pass null into any of the setters, Auto throws an IllegalStateException and tells us what we're missing.

java java-autovalue


Docker: install Java8 automatically on ubuntu/debian

If you want to create a docker image with Java, then there's a problem: it'll ask you manually confirm that you agree to the terms and conditions.
 
To automatically do this, add some debconf-set-selections to your script.
 
So the steps are now update, install software properties utils, add the webupd8team repo, set the debconf selections, update again, install java.

apt-get update
apt-get -y install software-properties-common
add-apt-repository -y ppa:webupd8team/java
echo debconf shared/accepted-oracle-license-v1-1 select true | debconf-set-selections
echo debconf shared/accepted-oracle-license-v1-1 seen true | debconf-set-selections
apt-get update
apt-get -y install oracle-java8-installer

docker java


Gradle: Hello world Java with a fat jar

Create your build.gradle with your dependencies and a jar section that collects all your libraries into the jar and sets the Main class file.

apply plugin: 'java'

repositories {
   mavenCentral()
}

dependencies {
    compile 'org.mindrot:jbcrypt:0.3m'
}

jar {
    from {
        (configurations.runtime).collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    manifest {
        attributes("Main-Class": "Main" )
    }
}

Now create a basic hello world, using the library we imported:

import org.mindrot.jbcrypt.BCrypt;

public class Main {
        public static void main(String[] args) {
                String password = BCrypt.hashpw("password", BCrypt.gensalt(10));
                System.out.println(password);
        }
}

Now build and run your jar:

$ gradle clean build && java -jar build/libs/THE_NAME_OF_YOUR_JAR.jar
...
$2a$10$R6q8LOed8LqXCOIhBnzhMecyebv/8v1urKjU76JMJGUctnZ8VkyZu

java gradle fat-jar jar bcrypt


A single page Java/Jetty/Jersey REST app with no web.xml descriptor

First create the build.gradle file in your directory:

apply plugin: 'war'
apply plugin: 'eclipse'

repositories {
   mavenCentral()
}

dependencies {
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.16'
    compile 'org.glassfish.jersey.bundles:jaxrs-ri:2.16'
}

compileJava {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

Then, in src/main/java/com/example, create App.java:

package com.example;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("rest") 
public class App extends ResourceConfig {
  public App() { packages("com.example"); }

  @Path("example")
  public static class Hello {
    
      @GET
      @Produces(MediaType.APPLICATION_JSON)
      public List<String> example() {
        List<String> l = new ArrayList<>();
        l.add("one"); l.add("two");
        return l; 
      }

  }  
  
}

Now
  1. Create the war file with gradle
  2. Download the Jetty runner jar
  3. Run the war file with the jetty runner (takes about 15 seconds to init usually)
  4. Use curl to test the app

From the command line:

gradle build war
wget http://central.maven.org/maven2/org/eclipse/jetty/jetty-runner/9.3.0.M1/jetty-runner-9.3.0.M1.jar
java -jar jetty-runner-9.3.0.M1.jar --port 8081 build/libs/your-directory-name.war
curl localhost:8081/rest/example && echo
    

The above curl command should print:

["one","two"]

jetty jersey gradle java

Page 1 of 10
next