Akka: Spring Integration

PROBLEM

To create Spring-managed prototype-scoped actors.

SOLUTION

The key to this solution is to create a custom implementation of IndirectActorProducer. From the documentation:-

“…This interface defines a class of actor creation strategies deviating from the usual default of just reflectively instantiating the Actor subclass. It can be used to allow a dependency injection framework to determine the actual actor class and how it shall be instantiated…”

First, create an implementation of IndirectActorProducer that creates the actor using Spring’s ApplicationContext.

public final class SpringActorProducer implements IndirectActorProducer {
    private final ApplicationContext applicationContext;
    private final Class<? extends Actor> actorClass;

    public SpringActorProducer(final ApplicationContext applicationContext, 
                               final Class<? extends Actor> actorClass) {
        this.applicationContext = applicationContext;
        this.actorClass = actorClass;
    }

    @Override
    public Actor produce() {
        return applicationContext.getBean(actorClass);
    }

    @Override
    public Class<? extends Actor> actorClass() {
        return actorClass;
    }
}

To prevent tedious auto-wiring ApplicationContext in every actor class to be passed to SpringActorProducer above, create a Spring-managed service that will do just that.

@Component
public final class SpringProps {
    private final ApplicationContext applicationContext;

    @Autowired
    public SpringProps(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public Props create(final Class<? extends Actor> actorClass) {
        return Props.create(SpringActorProducer.class, 
                            applicationContext, 
                            actorClass);
    }
}

Now, instead of creating the actors like this….

ActorRef master = actorSystem.actorOf(
                      Props.create(Master.class), "master");

ActorRef workerRouter = getContext().actorOf(
                            Props.create(Worker.class)
                                 .withRouter(new RoundRobinPool(5)),
                            "workerRouter");

… we will use the auto-wired SpringProps to create the actors…

@Autowire
private SpringProps springProps;

...

ActorRef master = actorSystem.actorOf(
                      springProps.create(Master.class), "master");
												 
ActorRef workerRouter = getContext().actorOf(
                            springProps.create(Worker.class)
                                       .withRouter(new RoundRobinPool(5)),
                            "workerRouter");

Make sure all actor classes are Spring-managed with prototype-scoped by annotating them with @Component and @Scope("prototype").

To see the full example, visit https://github.com/choonchernlim/test-akka-spring

Neo4j: WHERE Clause vs Curly Braces in MATCH Clause

PROBLEM

With Cypher Query Language, you can write similar queries that yield the same result.

For example:-

MATCH (p:Person)-[:loves]->(b:Beverage) 
WHERE b.name = 'Dark Roast' 
RETURN p.name as PERSON

… AND …

MATCH (p:Person)-[:loves]->(b:Beverage{name:'Dark Roast'}) 
RETURN p.name as PERSON

… returns the same result.

So, which one is better in terms of performance?

SOLUTION

Neo4j provides a very helpful command called EXPLAIN that allows us to do some investigation ourselves.

The EXPLAIN command displays the query plan without actually executing the query.

To use it, just add EXPLAIN in front of the query.

In this example, both queries produce exactly the same query plan.

In another word, while both queries look slightly different, they perform exactly the same.

Neo4j: Playing with Cypher Query Language

PROBLEM

Every week, my shitty coworkers struggle to reach consensus on where to go for lunches and what beverages to order in the morning.

SOLUTION

To further understand the phenomenon of this first world problem, here’s a shitty graph database powered by Neo4j to visualize the love/hate relationship between my shitty coworkers and the shitty restaurants/beverages.

Now, let’s flex the power of Cypher Query Language to unclutter this shitty mess.

People who love Whistle Binkies

MATCH (p:Person)-[:loves]->(:Restaurant{name:'Whistle Binkies'}) 
RETURN p.name as PERSON

… OR …

MATCH (p:Person)-[:loves]->(r:Restaurant) 
WHERE r.name = 'Whistle Binkies' 
RETURN p.name as PERSON

Result:-

PERSON
Mike
Corey
Jason

Restaurants with at least 2 people loving it

MATCH (:Person)-[:loves]->(r:Restaurant) 
WITH r, count(*) as tr 
WHERE tr > 2 
RETURN r.name as RESTAURANT, tr as TOTAL

Result:-

RESTAURANT         TOTAL
Whistle Binkies    3
Newts              5
John Hardy's       3
Noodles and Co     4

Restaurants that everyone loves

Winner winner chicken dinner!

MATCH (p:Person) 
WITH distinct count(*) as tp
MATCH (p)-[:loves]->(r:Restaurant) 
WITH tp, r, count(*) as tr 
WHERE tr = tp 
RETURN r.name as RESTAURANT

Result:-

RESTAURANT
Newts

Beverages that only one person loves… and find out who that weirdo is

MATCH (p:Person)-[:loves]->(b1:Beverage)
WITH b1, count(*) as tb
WHERE tb = 1
MATCH (p)-[:loves]->(b2:Beverage)
WHERE b1.name = b2.name 
RETURN p.name as PERSON, b1.name as BEVERAGE

… OR …

MATCH (p:Person)-[:loves]->(b:Beverage)
WITH b, count(*) as tb
WHERE tb = 1
MATCH (p)-[:loves]->(:Beverage{name:b.name})
RETURN p.name as PERSON, b.name as BEVERAGE	

Result:-

PERSON    BEVERAGE
Cory      Fufu Drink
Corey     Vanilla Latte

People who love to eat at John Hardies and drink Cold Press… ie: people with high taste

MATCH (b:Beverage)<-[:loves]-(p:Person)-[:loves]->(r:Restaurant) 
WHERE r.name = "John Hardy's" and b.name = 'Cold Press' 
RETURN p.name as PERSON

… OR …

MATCH (b:Beverage{name:'Cold Press'})<-[:loves]-(p:Person)-[:loves]->(r:Restaurant{name:"John Hardy's"}) 
RETURN p.name as PERSON

… OR …

MATCH (p:Person)-[:loves]->(r:Restaurant{name:"John Hardy's"}),
      (p)-[:loves]->(b:Beverage{name:'Cold Press'}) 
RETURN p.name as PERSON

Result:-

PERSON
Mike
Jason

People who love and hate the same beverage

“When love and hate collide” – Def Leppard… ie: coworkers with identity crisis.

MATCH (b:Beverage)<-[:loves]-(p:Person)-[:hates]->(:Beverage{name:b.name}) 
RETURN p.name as PERSON, b.name as BEVERAGE 

… OR …

MATCH (p:Person)-[:loves]->(b:Beverage),
      (p)-[:hates]->(:Beverage{name:b.name}) 
RETURN p.name as PERSON, b.name as BEVERAGE 

Result:-

PERSON    BEVERAGE
Corey     Dark Roast

LESSON LEARNED

While Cypher Query Language syntax looks much different than SQL syntax, they are pretty easy to learn and understand.

Cypher Query Language doesn’t and will not solve people problem though. We still argue about lunches/beverages every week and those who are willing to drive usually win the arguments.

Spring Data Neo4J: Requested a entity of type ‘X’, but the entity is of type ‘Y’

PROBLEM

Let’s assume we have a node entity like this:-

@NodeEntity
public final class Person {
    @GraphId
    private Long id;

    private String name;

    @Fetch
    @RelatedTo(type = "likes", direction = OUTGOING)
    public Set<Restaurant> likesRestaurants;

    @Fetch
    @RelatedTo(type = "likes", direction = OUTGOING)
    public Set<Beverage> likesBeverages;

    public Person() {
    }

    public Person(final String name,
                  final Set<Restaurant> likesRestaurants,
                  final Set<Beverage> likesBeverages) {
        this.name = name;
        this.likesRestaurants = likesRestaurants;
        this.likesBeverages = likesBeverages;
    }
}

When saving this entity, we get this exception:-

Exception in thread "main" org.springframework.data.neo4j.mapping.PersistentEntityConversionException: 
Requested a entity of type 'class myproject.Restaurant', 
but the entity is of type 'class myproject.Beverage'.

SOLUTION

This problem occurs because relationship likes is being used by both Restaurant entity and Beverage entity.

To fix it, we need to enforce the target type.

@NodeEntity
public final class Person {
    @GraphId
    private Long id;

    private String name;

    @Fetch
    @RelatedTo(type = "likes", direction = OUTGOING, enforceTargetType = true)
    public Set<Restaurant> likesRestaurants;

    @Fetch
    @RelatedTo(type = "likes", direction = OUTGOING, enforceTargetType = true)
    public Set<Beverage> likesBeverages;

    public Person() {
    }

    public Person(final String name,
                  final Set<Restaurant> likesRestaurants,
                  final Set<Beverage> likesBeverages) {
        this.name = name;
        this.likesRestaurants = likesRestaurants;
        this.likesBeverages = likesBeverages;
    }
}

Windows 10 Was NOT My Idea, But It Was Free

So, I decided to make use of the complimentary upgrade from my current Windows 7 running on VirtualBox to Windows 10. Not exactly the smoothest process, but I got it working after 5 days.

I’m listing all the problems I encountered and the solutions I applied to get my free toy that I rarely use.

PROBLEM: “Get Windows 10” icon not showing on taskbar

To fix this, run all Windows updates first. In my case, I had to upgrade my Windows 7 to SP1 for that icon to show up.

PROBLEM: “CPU isn’t supported” error

When clicking on “Get Windows 10”, you may get “CPU isn’t supported” error.

To fix this, shut down guest VM. Then, check “Enable PAE/NX” setting in VirtualBox.

PROBLEM: “Get Windows 10” error report doesn’t refresh

It appears Microsoft configures Windows 10 Compatibility Appraiser to run every once awhile.

To fix this, force it immediately because my time is money.

schtasks.exe /Run /TN "\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser"

Wait for a few minutes before checking the error report.

PROBLEM: “Virtualbox Graphics Adapter was not compatible with Windows 10” error

Okay, this is pure bull.

To fix this, download Media Creation Tool and use that to perform the upgrade instead.

PROBLEM: Progress stuck at 0% for hours

When running media creation tool, the progress may be stuck at 0% or whatever percentage for hours. Well, the Microsoft servers are overloaded.

To fix this, rerun Media Creation Tool, say 5AM in the morning… works for me.

PROBLEM: “Setup has failed to initialize the working directory” error

Basically, the guest VM doesn’t have enough space for Windows 10 upgrade.

To fix this, shut down guest VM.

Then, increase the virtual space by running the following command on the host. In my case, I increased my virtual size to 100GB.

VBoxManage modifyhd "/Users/Shitty Author/VirtualBox VMs\Windows 7\Windows 7.vdi” --resize 102400

Now, increase the actual space by first downloading GParted Live ISO.

In VirtualBox, add this ISO to “IDE Controller”. Make sure CD is booted before hard drive.

Run guest VM.

When GParted Live gets loaded, use the default values by hitting Enter keys several times.

Expand the existing NTFS drive space accordingly.

Restart guest VM.

Rerun Media Creation Tool… and hopefully it works this time.

What Now?

Well, now I have Windows 10 Pro running in VirtualBox, together with Ubuntu, Centos, Fedora and Android OS.

In reality, I will only use Windows 10 to test my software and spend 99.9% of my time on Mac.

Maven Archetype Plugin: Velocity Variable Substitutions Not Resolving

PROBLEM

Let’s assume we have the following package.json in our archetype:-

{
  "name": "${rootArtifactId}",
  "private": true,
  "devDependencies": {
    ...
  }
}

When creating a project from this archetype, the Velocity variable substitution for ${rootArtifactId} doesn’t resolve at all.

SOLUTION

After reading Maven Archetype Plugin’s source code here and here, the Velocity variable substitutions are only performed on the following file extensions:-

List<String> DEFAULT_FILTERED_EXTENSIONS =
    Arrays.asList(
        new String[]
            {
                "java", "xml", "txt", "groovy", "cs", "mdo", "aj", "jsp", "gsp", 
                "vm", "html", "xhtml", "properties", ".classpath", ".project"
            }
    );

In another word, if we have these variables in JSON or JavaScript files, they will not resolved at all.

To fix this, define the needed file extensions in Maven Archetype Plugin configuration:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-archetype-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <archetypeFilteredExtentions>js,json,md,java,xml,txt,groovy,jsp,vm,html,properties</archetypeFilteredExtentions>
    </configuration>
</plugin>

Maven Archetype Plugin: Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.3:create-from-project

PROBLEM

When running mvn archetype:create-from-project with Maven Ear Plugin defined under <pluginManagement>….

<project ...>
    ...
    <build>
        <pluginManagement>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-archetype-plugin</artifactId>
                    <version>2.3</version>
                </plugin>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-ear-plugin</artifactId>
                    <version>2.10.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

… the following exception occurs…

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.3:create-from-project (default-cli) on project myproject-webapp: null: MojoFailureException -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.3:create-from-project (default-cli) on project myproject-webapp: null
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
        at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
        at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
        at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
        at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoFailureException
        at org.apache.maven.archetype.mojos.CreateArchetypeFromProjectMojo.execute(CreateArchetypeFromProjectMojo.java:258)
        at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
        ... 19 more

SOLUTION

It appears this problem happens when using Maven Archetype Plugin 2.3, but works fine when using 2.2.

To fix this, define an empty <modules> under <configuration> to prevent NullPointerException.

<project ...>
    ...
    <build>
        <pluginManagement>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-archetype-plugin</artifactId>
                    <version>2.3</version>
                </plugin>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-ear-plugin</artifactId>
                    <version>2.10.1</version>
                    <configuration>
                        <modules/>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>