Tuesday 24 October 2017

Perfection is a swear word.

There is something fundamentally wrong with a lot people's perspective of how we should attempt a new project. Every time I have been involved with a new project business people like to throw around terms like:
  1. Make sure the software is perfect.
  2. I want a perfect system.
  3. If this systems/application/car is not perfect there will be consequences.
Here is the fundamental problem with all those statements. Perfection implies no mistakes, no bugs, it works everytime for everyone even if they work in a different way. A perfect car would win the best fuel consumption and fastest car ever award. It implies completeness.

To achieve perfection is not realistic, it is not feasible and it is actually plain stupid to make any  statements like that. To create the perfect project you would need to understand everything first.

Or in simple words
  1. Perfection would require you to know what you don't know
  2. You don't know what you don't know.
In short perfection does not exist. People who demand perfection might sound intelligent, have high standards but actually they are just hard to please. Personally to me they sound stupid and unrealistic. They sound like the type of person that needs to get out a little bit more and go see what the real world is about.

So if we cannot strive for perfection, what do you strive for? Excellence. Perfection is 100%, excellence is 90% however you can hit 90% easy. Its easier to move from 90% complete to 95% complete than it is to try and get 100% completeness from the start.

Simple mind shift which will bring you big results. Also as team lead saying to team members I except excellence from you empowers them to do their job to the best of their ability as they can attend to things they now. Demanding perfection means they run around stressed and probably getting burnt out without delivering , its got to be perfect you see.

Go do something excellent!

Wednesday 7 September 2016

Adding Hibernate Search Capabilities To A WildFly 10 Project Not Using Maven

A very quick note for people having to add hibernate search functionality to a legacy project that does not use maven.

Maven makes it so easy when I work on projects where I don't have it I feel like slitting my wrists on a fine spring morning like today. Jokes aside it is not too difficult.

Download the latest Hibernate Search Full distribution zip file from Sourceforge.    

Once downloaded and extracted add only what you need. A minimal at least would be the following dependencies.


  1. hibernate-search-engine-5.5.4.Final.jar
  2. hibernate-search-orm-5.5.4.Final.jar
  3. lucene-core-5.3.1.jar
  4. tika-core-jar-1.4.jar
  5. tika-parsers-1.4.jar
  6. lucene-analyzers-common-5.3.1.jar
This should get you running but you might need some other jars in the archives, just don't add everything in the extracted archive as you might just run into conflicts with existing libraries. So if you are having problems deploying you probably got some jar conflict. Someone reported this problem yesterday. So slow and easy one step at time to avoid frustration here.

Note that these are found under the dist\lib folder of the extracted archives.

Saturday 8 November 2014

A Fun Camel Ride With Apache Camel Part Six Reading Tags, Inserting Values And Setting Headers

This is probably the last sizeable entry in this series of the tutorial. In this part of the tutorial there are some design decisions and a lot more code. I will try to be brief but some of the parts are going to need a little more than a couple of words to justify why I took a certain decisions. The next step is to analyse the tags and insert them into the database. Once this step is complete we want to be able to route the files to the final location.

In the previous step I also showed you how to declare a bean and use that bean as a processor. Lets expand the functionality of this class to actually attempt to read the tags and then set some headers we will use later on to write the file to the right location. To read the tags associated with audio file the JAudioTagger library needs a File object to read.

When Camel reads a file via the file component it does not actually send the file on the exchange this might sound contra-intuitive but unless you access the body of the file there is no need to send the entire file along the exchange. This keeps the message small and thus improves throughput.I discussed this concept in a answer on StackOverflow if you want more information

However like I mentioned the JAudioTagger library requires a file object to read. I had two choices on how to deal with the JAudioTagger library requiring a file to object to read.

My first choice was to convert the message body to a binary file and then point the JAudioTagger library at the temporary file I just created. However that would require me to read the source file and then write it to some temporary location and then point JAudioTagger to that temporary location to read a temporary file. Since I accessed the message body I would send a  much larger message across the exchange.

My second option was to use the information Camel passed in the header and use that information to read the tags from the original file.

 Lets review these two options and the steps(operations) involved to see which one is more efficient:

  1. Access the Camel message body and then save the body to a location for tag analysis:
    • Access the message body forcing Camel to read the source file as a Byte[]
    • Write this Byte[] to some temporary location
    • Load this file in the temporary location into JAudioTagger library
    • Analyse the tags
    • Delete the temporary file.
    • Send the message onto the next step and as the message now has the file data is much bigger.
  2. Use the file path information passed to me by Camel in the header to do tag analysis:
    • Use the filepath info passed in the header of the message to read the original source file
    • Analyse the tags
    • Send the original small message onto the next step. 

Option two has three steps and Option one has six steps. Right so we will go with option two as it involves less operations. Remember when deciding on a course of action to deal with something look for the path of least resistance. There is also another reason I decided on this option.

Later on I want to be able to process audio files in parallel. If I used option one I would be sending the entire audio file as a Byte[] along the exchange. Some audio files can be rather large(more than 1 megabyte is normal) and I don't want Camel to send such large message in parallel as this is a recipe for reducing my scalability right away.

Remember as a developer you must try to solve problem efficiently not just hack your way through it. Keep scalability, performance, security and ease of use as a mantra and you will be ok. Always think before you code the results are much more professional than someone that hacks at it for hours. Enough justification of my design decisions lets see how we will get the tag data from the file and insert this into the database.

In the previous part of this tutorial I showed you how to declare a connection pool and we also declared a couple of stored procedures for the MySQL database. Lets review how we will use these in our route.

The connection pool is actually very easy to use as CDI injection handles for us. However how do you get a connection from a declare connection pool. The code for this is too easy(as the Australians say). The tagAnalyser bean has a property called dataSource which CDI uses to inject the connection pool into. To get a connection from this pool we simply need to access the property datasource and ask it to send give us a connection.

This can be achieved with the following lines of code:
    Connection conn = null; //declare connection object
    conn= dataSource.getConnection(); //get a connection from the pool

Pretty simple right? The next step will be to declare a file object that points to the source file to use in tag analysis. Here I ran into some problems as some audio files in my collection are from other countries such as Japan, China and other places with interesting alphabets and characters. This had me stumped for a while and then I realised I forgot to tell Java which charset encoding to use for my file system. Some JVM's will run under the default charset encoding which may differ from your actual file system encoding. This can be a very difficult bug to spot. Add the following option to your VM arguments -Dfile.encoding=UTF-8 to rectify this. Well that is assuming your file system is running UTF-8.

The code for pointing JAudioTagger at the source file and reading the tags will generally follow this pattern:

    File audioFile = new File(filepath);
    AudioFile f = AudioFileIO.read(audioFile);
    Tag tag = f.getTag();

Right so lets look at the complete code for the processor to implement the needed functionality.
package com.tutorial.namphibiansoftware.camelmusic;

import java.io.File;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;

import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.apache.camel.Headers;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.lang.StringEscapeUtils;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.TagField;

public class tagAnalyser {
 
private static final String SQL_INSERT_ARTIST="CALL setArtist(?);";    //This is the call to the stored procedure which will insert or update an artist
private static final String SQL_INSERT_GENRE="CALL setGenre(?)";
private static final String SQL_INSERT_SONG_ARTIST ="CALL setSongArtist(?,?);";
private static final String SQL_INSERT_GENRE_ARTIST ="CALL setGenreArtist(?,?);";


//We will use dependency injection to inject the connection pool into this property.
private BasicDataSource dataSource;
//Another example of using dependency injection to inject a logger into the class
private static final Log LOG = LogFactory.getLog(tagAnalyser.class);
public BasicDataSource getDataSource() {
 return dataSource;
}

public void setDataSource(BasicDataSource dataSource) {
 this.dataSource = dataSource;
}
 @Handler
 public void readTag(@Headers Map header,Exchange exchange) throws Exception{
        
    
        Connection conn   = null;
        CallableStatement cstmt = null;
        String artist   = null;
        String genre   = null;
        String album   = null;
        String title   = null;
        String camelFilePath  = null; 
        String filepath   = null;
        File   audioFile        = null;
        try
        {
            //get the header information once so we dont have to look it up again
            camelFilePath = (String)header.get("CamelFilePath");   
            LOG.info("We just got a connection from the connection pool we can use this to insert data for the file: "+camelFilePath);
 
            //create the file object used by JAudioTagger to read tags
            audioFile = new File(filepath);        
            //create the audiofile object based on the file
            AudioFile f = AudioFileIO.read(audioFile);      
            Tag tag = f.getTag();
            LOG.info("Read Tag Information For The File:"+camelFilePath);
            /*
             * This line(s) of code might need some explaining. 
             * The tag value can be a lot of different combinations of empty, null or zero length strings.
             * Thus I check for the following conditions
             * 1) NULL Strings
             * 2) Empty Strings
             * 3) Length of zero
             * If any of these conditions are true I set the artist name to UNKNOWN
             * If none of these conditions are met I just use the first artist name.
             * I know there can be multiple artists per song
             * However I am keeping this simple you can expand on this if needed
            */
            title=(tag.getFirst(FieldKey.TITLE)==null||tag.getFirst(FieldKey.TITLE).trim().equalsIgnoreCase("")||tag.getFirst(FieldKey.TITLE).length()==0?"Unknown":tag.getFirst(FieldKey.TITLE));
            LOG.info("The Title Of This File Is: "+title);
            artist = ((tag.getFirst(FieldKey.ARTIST)==null||tag.getFirst(FieldKey.ARTIST).equalsIgnoreCase("")||tag.getFirst(FieldKey.ARTIST).length()==0)?"Unknown":tag.getFirst(FieldKey.ARTIST));
            LOG.info("The First Artist Tag Name is: "+artist);
            if(tag.getFields(FieldKey.ARTIST).size()>1)
            {
             
             LOG.info("Multiple artists listed for "+title+" other artist names are as follows:");
             for (Iterator iterator = tag.getFields(FieldKey.ARTIST).iterator(); iterator
      .hasNext();) 
             {
     TagField artistField = (TagField) iterator.next();
     LOG.info("Other Artist Name: "+artistField);
     
    }
             
            }
            genre=(tag.getFirst(FieldKey.GENRE)==null||tag.getFirst(FieldKey.GENRE).trim().equalsIgnoreCase("")||tag.getFirst(FieldKey.GENRE).length()==0?"Unknown":tag.getFirst(FieldKey.GENRE));
            LOG.info("Song "+title +" is of genre: "+genre);
            album=(tag.getFirst(FieldKey.ALBUM)==null||tag.getFirst(FieldKey.ALBUM).trim().equalsIgnoreCase("")||tag.getFirst(FieldKey.ALBUM).length()==0?"Unknown":tag.getFirst(FieldKey.ALBUM));
            LOG.info("Song "+title +" was release on the album: "+album);
        }
        catch (Exception tagException)
        {
         LOG.info("Error in reading tag information please check error log .");
            LOG.error(tagException.getMessage());
            // Here we throw a cannot read tag exception.
        }
        LOG.info("Read Tag Information Setting Header Values For Exchange.");
        header.put("Artist", artist);
        header.put("Album" ,album);
        header.put("Title",title);
        header.put("Genre",genre);
        try
        {
         int numOfRows =0;
         conn  = dataSource.getConnection(); //get a connection from the pool
         cstmt = conn.prepareCall(SQL_INSERT_ARTIST);
         cstmt.setString(1, artist);
         numOfRows=cstmt.executeUpdate();
         LOG.info("Inserted: "+numOfRows+" Artist");
         cstmt = conn.prepareCall(SQL_INSERT_GENRE);
         cstmt.setString(1, genre);
         numOfRows=cstmt.executeUpdate();
         LOG.info("Inserted: "+numOfRows+" Genre");
         cstmt = conn.prepareCall(SQL_INSERT_SONG_ARTIST);
         cstmt.setString(1, title);
         cstmt.setString(2, artist);
         numOfRows=cstmt.executeUpdate();
         LOG.info("Inserted: "+numOfRows+" Songs/Tracks");
         cstmt = conn.prepareCall(SQL_INSERT_GENRE_ARTIST);
         cstmt.setString(1, genre);
         cstmt.setString(2, artist);
         numOfRows=cstmt.executeUpdate();
         LOG.info("Inserted: "+numOfRows+" Genre And Artist Combinations");

             
            }
            catch(Exception dbException)
            {
             LOG.error(dbException.getMessage());
            }
         finally
         {
             try
             {
                     if (cstmt!=null)
                     {
                      cstmt.close();
                     }
                     if (conn!=null)
                     {
                         conn.close();
                     }
             }
             catch(SQLException e)
             {
                 System.out.println(e.getMessage());
             }
         }
  }
 
}


The code is a a little bit more than previous listing of the processor. The process is pretty easy to follow though first we try to read the tags of the file and then we set the headers and finally we do the database inserts. I have broken up the reading of the tags and the inserting of the data into the database into two separate try catch blocks so that I can raise different types of exceptions for the exchange and have the route respond differently to the two types of exceptions. At this point the exceptions just gets logged to the logging system. Go ahead and run the route and watch the log output.

Thats it for this part which I hope is the last of the long parts. In the next part we will look at dealing with the exceptions and also how to write the files to their final destination. After that we will look at some techniques that scale file processing in a Apache Camel route.

Finally just to make sure that you can apply these techniques into a real life deployment we will also cover how to deploy this route to a Apache Karaf installation.

Tuesday 28 October 2014

A Fun Camel Ride With Apache Camel Part Five Databases, Beans And Connection Pools

In the last part of the tutorial I got the basic flow working. We are now getting to probably the longest part in the tutorial. This section will cover a lot of ground such as the database design, beans, dependency injection and connection pooling.

Lets cover the database first. I want to keep a record of all the artist, song and genres I have music for. So I am going to keep this design very simple you can expand on the ideas here and do your own thing later on. I also want to focus on the Camel part more than the database so I am going really going for barebones.

So lets look at all the primary(entity) information I want to store the list is as follows:

  • Artist: Artist Name
  • Song: Song Name, Artist Name(if available).
  • Genre: Genre Name
I also want to keep a track of which artist's and their genres. This will require me to create some relationships in the database. Thus I will need the following relationship table as well.
  • Artist Genre: Artist Name, Genre Name
Right the following is the scripts I used to create this database.
    
delimiter $$
CREATE SCHEMA `mycamelmusic` DEFAULT CHARACTER SET utf8$$
USE `mycamelmusic`$$
CREATE TABLE `artist` 
(
  `artistName` varchar(127) NOT NULL,
  PRIMARY KEY (`artistName`)
  
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$
CREATE TABLE `genre` 
(
  `genreName` varchar(127) NOT NULL,
  PRIMARY KEY (`genreName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$ 
CREATE TABLE `genreartist` 
(
  `genreName` varchar(127) NOT NULL,
  `artistName` varchar(127)NOT NULL,
  PRIMARY KEY (`genreName`,`artistName`),
  KEY `fk_genreartist_artist_artistName` (`artistName`),
  CONSTRAINT `fk_genreartist_artist_artistName` FOREIGN KEY (`artistName`) REFERENCES `artist` (`artistName`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_genreartist_genre_genrename` FOREIGN KEY (`genreName`) REFERENCES `genre` (`genreName`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$

CREATE  TABLE `mycamelmusic`.`song` 
(
  `songName` VARCHAR(127) NOT NULL ,
  `artistName` VARCHAR(127) NOT NULL ,
  PRIMARY KEY (`songName`, `artistName`) ,
  INDEX `fk_artist_artist_name` (`artistName` ASC) ,
  CONSTRAINT `fk_artist_artist_name`
    FOREIGN KEY (`artistName` )
    REFERENCES `mycamelmusic`.`artist` (`artistName` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)$$
delimiter ;
     
Note that I went for natural keys and not auto numbers. The reason is simple a primary key should not change values thus I can use the genre names such as classical, dance, rock and pop for key values as genre names never change. The same goes for artists. Bach will always be Bach, Depeche Mode will always be Depeche Mode. This eliminates the need to have auto numbers as primary keys and enforce uniqueness on the genre and artist names. Remember autonumbers/identities is but one technique in developing databases not the ONLY technique.

One last part of this is to create the stored procedures that the Camel route will use to insert data into the database. These are simple insert stored procedures for each table. Create these once the database scripts has been run.

USE `mycamelmusic`;
DROP procedure IF EXISTS `setArtist`;
DROP procedure IF EXISTS `setGenre`;
DROP procedure IF EXISTS `setGenreArtist`;
DROP procedure IF EXISTS `setSongArtist`;

DELIMITER $$

USE `mycamelmusic`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `setArtist`(artName VARCHAR(127))
BEGIN
  INSERT INTO `mycamelmusic`.`artist`
  (
    `artistName`
  )
  VALUES
  (
    artName
  )
  ON DUPLICATE KEY UPDATE 
    `artistName`=artName;
END;$$

CREATE PROCEDURE `mycamelmusic`.`setGenre` (genName VARCHAR(127))
BEGIN
  INSERT INTO `mycamelmusic`.`genre`
  (
      `genreName`
  )
  VALUES
  (
       genName
  )
  ON DUPLICATE KEY UPDATE
     `genreName`=genName;
END;$$

CREATE PROCEDURE `mycamelmusic`.`setGenreArtist` (genName VARCHAR(127),artName VARCHAR(127))
BEGIN
  INSERT INTO `mycamelmusic`.`genreartist`
  (
       `genreName`,
       `artistName`
  )
  VALUES
  (
       genName,
       artName
  )
  ON DUPLICATE KEY UPDATE 
    `genreName` =genName,
    `artistName`=artName;
  END;$$

CREATE PROCEDURE `mycamelmusic`.`setSongArtist` (sngName VARCHAR(127), artName VARCHAR(127))
BEGIN
  INSERT INTO `mycamelmusic`.`song`
  (
       `songName`,
       `artistName`
  )
  VALUES
  (
       sngName,
       artName
  )
  ON DUPLICATE KEY UPDATE 
    `songName` =sngName,
    `artistName`=artName;

END;$$
DELIMITER ;


Lets proceed to the next step which is declaring a connection pool to the database. For those of you are not familiar with database connection pooling it is a must have technique in your toolbox. Database connection pooling is one of the most efficient ways to ensure you database handles connections efficiently.

We first need to do is find a Java database pooling manager. If you google for this you will find several such as BoneCP, DBCP and BoneCP. For this example we will be using DBCP so lets add  DBCP as a maven dependency. If you followed the tutorial thus far you will know how to do it. If not then read some of the previous tutorial parts. When you complete this you will have added the following code to your pom.xml file:
 
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.4</version>
</dependency>

Right on to the next step which is actually declaring a connection pool in our Camel route. One of the great features of Apache Camel is being able to declare and configure a POJO as a bean in the XML. This means you don't have to write code to get the POJO functional in your route. The question is how will a connection pool manager be used to create a basic data-source where we can request connections from?

Well DBCP has a class we can use just for this named org.apache.commons.dbcp.BasicDataSource. So we have to declare a bean in our Camel route that will be of type org.apache.commons.dbcp.BasicDataSource. This bean can then be injected into any other bean. Sounds complicated? Not really.

To do this add the following to the Camel route:

<bean id="dataSourcePool" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="url" value="jdbc:mysql://yourservernamehere:3306/mycamelmusic?noAccessToProcedureBodies=true"/>
   <property name="username" value="someuser"/>
   <property name="password" value="somepassword"/>
   <property name="initialSize" value="4"/>
   <property name="maxActive" value="16"/>
</bean>

This adds a bean with a name of dataSource to the camel route which is a DBCP basic data-source and handles the connection pooling. You will notice that the connection pool is looking for the com.mysql.jdbc.Driver class. As we will be using this to connect we need to add the com.mysql.jdbc.Driver as a maven dependency. At this point you should be able to handle this task without code from me. If you are struggling to get this dependency resolved here is a tip search for mysql-connector-java.

Now that the mysql driver dependency has been resolved you are finished in creating a connection pool. Can you believe that is all you need to do to create and configure a database connection pool in a Camel route? Simple right? Well if you think it was simple to create a connection pool wait till you see how it easy it is to use the connection pool. Since we are going to be doing the music file tag analysis and database operations in the same class we now need to create the class that will use the JAudioTagger library and the DBCP connection pooling class.

First create a class called tagAnalyser. This class will have a basic data-source property which we will set by using dependency injection in the Camel context. Since this bean will also be handling messages we will mark the function that handles the exchange with the @Handler annotation this clearly marks which function in the POJO/Bean will handle the exchange.One last thing I want to log some actions inside this POJO/Bean to keep track of what's happening. So we will use dependency injection to achieve this, the same way we did with the data-source. Sounds complicated and difficult to code right?

Wrong it is dead easy let me show you the code.
package com.tutorial.namphibiansoftware.camelmusic;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;

import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.apache.camel.Headers;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class tagAnalyser {
 //We will use dependency injection to inject the connection pool into this property
 private BasicDataSource dataSource;
        //Another example of using dependency injection to inject a logger into the class
 private static final Log LOG = LogFactory.getLog(tagAnalyser.class);
 public BasicDataSource getDataSource() {
  return dataSource;
 }

 public void setDataSource(BasicDataSource dataSource) {
  this.dataSource = dataSource;
 }
  @Handler
  public void readTag(@Headers Map<String ,Object> header,Exchange exchange) throws Exception{
         
         
         Connection conn = null;
         CallableStatement cstmt=null;
         try
         {
          conn= dataSource.getConnection();
          LOG.info("We just got a connection from the connection pool we can use this to insert data for the file: "+(String)header.get("CamelFilePath"));
         
         }
         catch (Exception e)
         {
             System.out.println(e.getMessage());
         }
        finally
        {
             try
             {
                     if (cstmt!=null)
                     {
                      cstmt.close();
                     }
                     if (conn!=null)
                     {
                         conn.close();
                     }
             }
             catch(SQLException e)
             {
                 System.out.println(e.getMessage());
             }
         }
  }
 
}

You can see the two injection point in the bean they are the dataSource and the log fields of the tag Analyser class. Lets review quickly, we have a class called dataSourcePool which is of type basic data-source, we also have a class tagAnalyser that has a property of basic data-source. We will need to inject the dataSourcePool bean into the dataSource property of the tagAnalyser bean. What a mouth full.

Once again this very simple and all you need to do is first declare your tagAnalyser as a bean in the Camel context by placing  the following XML snippet into the Camel context XML file.

<bean id="tagAnalyser" class="com.tutorial.namphibiansoftware.camelmusic.tagAnalyser">
  <property name="dataSource" ref="dataSourcePool"/>
</bean>

You can see the dependency injection here clearly. The dataSourcePool is injected into the dataSource property of the tagAnalyser class. Pretty simple and straight forward. Once last thing to do before we wrap up this part of the tutorial. We need to make sure our newly created tagAnalyser class is used in the Camel route.

Open the camel context file and then proceed to modify the routeby adding a bean to the route. Connect the log component to the bean component. You should end up with a route definition in XML that looks like the following:

<camelContext trace="false" id="blueprintContext" xmlns="http://camel.apache.org/schema/blueprint">
  <route shutdownRoute="Default" id="CamelMusicRoute" customId="true">
    <from uri="file://c:/mymusic?noop=true&amp;recursive=true&amp;delay=3000&amp;exclude=.*.(jpg|JPG|gif|GIF|doc|DOC|pdf|PDF|avi|AVI|Mpg|mpg|db|DB|ini|INI|txt|TXT|m3u|M3U|mpeg|MPEG)&amp;charset=utf-16" id="camelMusicFileSource" customId="true">
    <description>This endpoint will read the source folder for music files.</description>
    </from>
    <log loggingLevel="INFO" message="File name: ${file:absolute.path} was read at ${date:now:yyyy-MM-dd HH-mm-ss} and placed onto the route." id="LogSourceFileRead" customId="true">
    <description>This log component logs the filename and the time the file endpoint read a source file.</description>
    </log>
    <bean ref="tagAnalyser" id="tagAnalyser" customId="true">
    <description>This bean uses dependency injection to use a database connection pool and analyse and insert data around the music collection into the database for further analysis.</description>
    </bean>
  </route>
</camelContext>


In the visual designer you should see the following:
Tag Analyser Added 

Time to test all of this and see that our foundations are solid. You can run the camel route and watch the output you should see something like this logged:


[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building My Camel Music Route 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> camel-maven-plugin:2.12.0.redhat-610379:run (default-cli) @ camelmusic >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ camelmusic ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ camelmusic ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ camelmusic ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ camelmusic ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< camel-maven-plugin:2.12.0.redhat-610379:run (default-cli) @ camelmusic <<<
[INFO] 
[INFO] --- camel-maven-plugin:2.12.0.redhat-610379:run (default-cli) @ camelmusic ---
[INFO] Using org.apache.camel.test.blueprint.Main to initiate a CamelContext
[INFO] Starting Camel ...
[mel.test.blueprint.Main.main()] MainSupport                    INFO  Apache Camel 2.12.0.redhat-610379 starting
[mel.test.blueprint.Main.main()] Activator                      INFO  Camel activator starting
[mel.test.blueprint.Main.main()] Activator                      INFO  Camel activator started
[mel.test.blueprint.Main.main()] BlueprintExtender              INFO  No quiesce support is available, so blueprint components will not participate in quiesce operations
[         Blueprint Extender: 1] BlueprintContainerImpl         INFO  Bundle camelmusic is waiting for namespace handlers [http://camel.apache.org/schema/blueprint]
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  Apache Camel 2.12.0.redhat-610379 (CamelContext: blueprintContext) is starting
[         Blueprint Extender: 1] ManagedManagementStrategy      INFO  JMX is enabled
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  AllowUseOriginalMessage is enabled. If access to the original message is not needed, then its recommended to turn this option off as it may improve performance.
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
[         Blueprint Extender: 1] FileEndpoint                   INFO  Endpoint is configured with noop=true so forcing endpoint to be idempotent as well
[         Blueprint Extender: 1] FileEndpoint                   INFO  Using default memory based idempotent repository with cache max size: 1000
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  Route: CamelMusicRoute started and consuming from: Endpoint[file://c:/mymusic?charset=utf-16&delay=3000&exclude=.*.%28jpg%7CJPG%7Cgif%7CGIF%7Cdoc%7CDOC%7Cpdf%7CPDF%7Cavi%7CAVI%7CMpg%7Cmpg%7Cdb%7CDB%7Cini%7CINI%7Ctxt%7CTXT%7Cm3u%7CM3U%7Cmpeg%7CMPEG%29&noop=true&recursive=true]
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  Total 1 routes, of which 1 is started.
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  Apache Camel 2.12.0.redhat-610379 (CamelContext: blueprintContext) started in 0.401 seconds
[ thread #0 - file://c:/mymusic] CamelMusicRoute                INFO  File name: c:\mymusic\9 Lazy 9\9 Lazy 9 - Electric Lazyland.mp3 was read at 2014-10-28 19-50-09 and placed onto the route.
[ thread #0 - file://c:/mymusic] tagAnalyser                    INFO  We just got a connection from the connection pool we can use this to insert data for the file: c:\mymusic\9 Lazy 9\9 Lazy 9 - Electric Lazyland.mp3
Ok time to wrap it up. This part was a bit heavy going with a lot of work to be done. However we have covered almost all of the brunt work and things will start to flow faster in the next few parts.

Tuesday 21 October 2014

A Fun Camel Ride With Apache Camel Part Four Exploring The Basic Route Lets Process Those Files

Finally we are getting into the meat of the tutorial. We are going to start off with a basic implementation of the route then measure its performance and then start applying some more advanced techniques to increase performance.

The first thing we need to do is create a route which reads from a folder and then sends that information to a tag analyser. Apache Camel does not come with a digital music tag analyser so we will need to create a custom processor to handle this task. Now we could learn about all the different file formats and how to read the tags associated with them which is a rather lengthy task. Rather than going this way I have settled for the JAudioTagger library to read tags.  Since this is not a part of  camel we need to configure this as a maven dependency.

Double click the pom.xml file in your project explorer switch to the dependency tab and click on add a screen will popup and you can do a search on the Maven repositories from here. In the Enter groupId, artifactId, or sha1 prefix or pattern(*) text box type in jaudiotagger. Maven will do a search of the repository and you should get some results. Choose the org jaudiotagger results and expand it. Then select version 2.0.3.  This will add the dependency to the project.
Adding JAudioTagger Dependency

Now we can use the JAudioTagger library instead of the hard way of coding everything ourselves. We also need to start defining the pieces of the route. First things is to clear out the current route as it as it a example and we don't need the contents. Go to the blueprint.xml file and delete all the components in the current route. Also go the the src/main/java folder and delete the two source files there which are Hello.Java and HelloBean. Java. One last thing is to remove the  helloBean from the Camel route blueprint.xml file. Open the blueprint.xml file and go to the source tab and find the lines that read:

<bean id="helloBean" class="com.tutorial.namphibiansoftware.camelmusic.HelloBean">
<property name="say" value="Hi from Camel"/>
</bean>

Delete the lines as indicated above. At this point you will have an empty route ready to start with. It should look more or less like this:
<?xml version="1.0" encoding="UTF-8"?>
    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/blueprint"
       xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

    <camelContext trace="false" id="blueprintContext" xmlns="http://camel.apache.org/schema/blueprint">
         <route id="timerToLog" customId="true">

         </route>
</camelContext>

</blueprint>
The name of the route is still not correct it says timerToLog so change that to CamelMusicRoute and we are ready to start with the rest of the route. To get the basics working we will need the following components:
  • File endpoint to read the source directory
  • A custom processor that uses the JAudioTagger Library and inserts information into the database.
  • A processor to write the files to their destination folders
Lets add a file endpoint to the route. Go to the Design tab of the blueprint.xml and find the endpoints folder on the palette. Click and drag a endpoint component onto the design page. Make sure the file endpoint component is selected and the properties page is showing. You will a text box labeled URI and this is where we are going to configure the file endpoint.
My configuration URI looks like follows:

file://c:/mymusic?noop=true&recursive=true&delay=3000&exclude=.*.(jpg|JPG|gif|GIF|doc|DOC|pdf|PDF|avi|AVI|Mpg|mpg|db|DB|ini|INI|txt|TXT|m3u|M3U|mpeg|MPEG)&charset=utf-16

You should up with a view that looks more or less like this:

Adding The File Endpoint

This essentially says for each file in the c:\mymusic folder and subfolder, that does not have a extension listed in the exclude filter, read the files and send them on the Camel route. The end point is also configured to run every 3000 milliseconds or roughly five seconds. Notice that I set the noop to true. This is just to make sure that we dont have to reset the folder everytime we debug/run the route. In a real route you would probably move the files to a .done folder or some similar action. Also note that I like to add nice descriptions to my routes, endpoint etc. This makes things a lot easier when you start debugging, tracing and monitoring routes and the various steps in them.  A little bit of detail now will save you a lot of brain power later.

Some notes on the file endpoint that you have to keep in mind. One of the important concepts that you have to understand that the body of the message will contain an org.apache.camel.component.file.GenericFile object it will not contain the actual file data until you touch the body and convert it.This might seem counter intuitive at first however there is a very solid reason behind this. Scalability the file endpoint must be able to scale and if it reads the contents of the file into the body it is going to hamper the ability to quickly spool a lot of files into the route. Once you access the body you will be able to access the data of the file. I answered a StackOverflow question which contains more details so I wont repeat it here.

The file component adds some information to the header of the message. This list is primarily file meta-data such as file size, path, extensions and so on. You can use this information to route files to endpoint based on extension for example.

Just to demo some of the headers I will log the full path of each file that is being read. Go to the design view of the blueprint.xml and drag and drop a log component from the palette and drop it on the form. Make sure the log component is selected and add the following line as the message:
File name: ${file:absolute.path} was read at ${date:now:yyyy-MM-dd HH-mm-ss} and placed onto the route.
I also added the ID property and a description. You should end up with a route like this:

Adding A Log Component

At this point you will be able to run the route to do a basic test that all is order. Right click the project and go to Debug As->Debug Configuration menu item. Choose your camel music maven configuration and click debug. Watch your console output window for the results. If all goes well you should see something like the following:

[mel.test.blueprint.Main.main()] Activator                      INFO  Camel activator started
[mel.test.blueprint.Main.main()] BlueprintExtender              INFO  No quiesce support is available, so blueprint components will not participate in quiesce operations
[Blueprint Extender: 1] BlueprintContainerImpl         INFO  Bundle camelmusic is waiting for namespace handlers [http://camel.apache.org/schema/blueprint]
[Blueprint Extender: 1] BlueprintCamelContext          INFO  Apache Camel 2.12.0.redhat-610379 (CamelContext: blueprintContext) is starting
[Blueprint Extender: 1] ManagedManagementStrategy      INFO  JMX is enabled
[Blueprint Extender: 1] BlueprintCamelContext          INFO  AllowUseOriginalMessage is enabled. If access to the original message is not needed, then its recommended to turn this option off as it may improve performance.
[Blueprint Extender: 1] BlueprintCamelContext          INFO  StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
[Blueprint Extender: 1] FileEndpoint                   INFO  Endpoint is configured with noop=true so forcing endpoint to be idempotent as well
[Blueprint Extender: 1] FileEndpoint                   INFO  Using default memory based idempotent repository with cache max size: 1000
[Blueprint Extender: 1] BlueprintCamelContext          INFO  Route: CamelMusicRoute started and consuming from: Endpoint[file://c:/mymusic?charset=utf-16&delay=3000&exclude=.*.%28jpg%7CJPG%7Cgif%7CGIF%7Cdoc%7CDOC%7Cpdf%7CPDF%7Cavi%7CAVI%7CMpg%7Cmpg%7Cdb%7CDB%7Cini%7CINI%7Ctxt%7CTXT%7Cm3u%7CM3U%7Cmpeg%7CMPEG%29&noop=true&recursive=true]
[Blueprint Extender: 1] BlueprintCamelContext          INFO  Total 1 routes, of which 1 is started.
[Blueprint Extender: 1] BlueprintCamelContext          INFO  Apache Camel 2.12.0.redhat-610379 (CamelContext: blueprintContext) started in 0.451 seconds
[ thread #0 - file://c:/mymusic] CamelMusicRoute                INFO  File name: c:\mymusic\Adventures Beyond The Ultraworld\The Orb-Lfc - Cumunolibus Mix.mp3 was read at 2014-10-21 18-25-45 and placed onto the route.
[ thread #0 - file://c:/mymusic] CamelMusicRoute                INFO  File name: c:\mymusic\Adventures Beyond The Ultraworld\The Orb-Lfc - Tsunami One.mp3 was read at 2014-10-21 18-25-45 and placed onto the route.
[ thread #0 - file://c:/mymusic] CamelMusicRoute                INFO  File name: c:\mymusic\Adventures Beyond The Ultraworld\The Orb-Little 

In the next part of the tutorial I will deal with the JAudioTagger processor we will need to complete this route. There will be several techniques I use in creating this processor to make sure it is ready to scale to absurd levels and thus you will have to read the next part as I don't want to bombard you with all information at once. Besides this part is already extremely long and I need a break. Till next time.

Sunday 19 October 2014

A Fun Camel Ride With Apache Camel Part Three Understanding Your Project Layout.

This section is the final piece before we start deep diving into the code and techniques. I found that it helps that when following a tutorial it makes you familiar with navigating the tutorial in the IDE. Some of this might be basic knowledge for you folks out there.

The project explorer is a pretty standard view in eclipse so I am going to show you where the various pieces of the project lie in the in this view. When working with Apache Camel projects you will find you mostly work with source code, a xml route configuration file(unless you are doing it completely in Java) and Maven dependencies.

First lets look at the camel route definition/OSGI blue-print XML file. This can be found under the Camel Contexts folder and also under the src/main/resources/OSGI-INF/blueprint folder  in the project explorer view.

Camel Route Definitions


These two files contains the XML configuration for the route we will be modifying. If you click on either of these two files eclipse will open a graphical representation of this file. Here you can visually see your camel route. Hold on you say I did not define a route yet what is this? The camel archetype by default creates a example route and this is what you will see. We will modify this shortly. The following screenshot is an example of what you will see.

A Visual Representation Of The Camel Route In Eclipse
You can also view the xmlby clicking on the source tab and manually modify the xmlfile by hand. On the right there is a palette where you can find the various camel endpoints, routing, control flow and transformation components used in your camel routes. These can be dragged and dropped and then connected using the visual designer. You can view the properties of the various components by using the properties tab. The properties tab allows you to specify end point url for example.

Properties Of The Endpoint Component

Now onto Maven. The pom.xml can be found by using the project explorer. It also has a visual interface for the xml file or you could edit the xml by hand. The pom file contains a lot of information that can be very useful when you have to manages several camel routes.

The pom file allows you to add some pretty descriptive information which can be used to document and identify what a route does. I added some information this information will later be visible in your runtime container i.e. Apache Karaf and it can really help to manage things if you fill it in correctly

Adding Descriptive Information To Your Project Object Model
As for the source code your files can be found at the usual spot of the src/main/java folder. At this point you should have a good handle on how to edit the project. Now there is one last little hurdle to getting this tutorial into high gear.

Since this is not a normal Java application you cannot just run the route or debug it. You need to configure the debug and run steps. This is very simple all you have to do is create a Maven configuration/profile that can be used for both running and debugging.

In the project explorer right click on the project(any folder/node in the project should also work) then from the popup menu choose Debug As->Debug Configurations. This will bring a screen with various debug/run options. There will be a Maven section which you need to click/select. Then click the new button in the top menu and fill it in as follows:

  • Name: CamelMusic
  • Goals: camel:run
One last thing to do is to set the base directory which is done by clicking the browse workspace button and then choosing the camelmusic project as the base directory.  See the screenshot below for what it should look like.
Creating The Maven Run/Debug Configurations
Lets quickly test that the configuration is working. Click the debug button and Maven will kick in and download some dependencies. If this is your first time with the JBoss Fuse tooling plug-in it might take a while before the route starts running. If all goes well you will see something like the following lines appear in the output window of eclipse.


[INFO] Scanning for projects...
Downloading: http://repo.fusesource.com/nexus/content/groups/ea/org/apache/maven/plugins/maven-install-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/repositories/snapshots/org/apache/maven/plugins/maven-install-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/repositories/releases/org/apache/maven/plugins/maven-install-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/repositories/releases/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/groups/ea/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/repositories/snapshots/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/repositories/releases/org/apache/maven/plugins/maven-deploy-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/repositories/snapshots/org/apache/maven/plugins/maven-deploy-plugin/maven-metadata.xml
Downloading: http://repo.fusesource.com/nexus/content/groups/ea/org/apache/maven/plugins/maven-deploy-plugin/maven-metadata.xml
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building My Camel Music Route 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> camel-maven-plugin:2.12.0.redhat-610379:run (default-cli) @ camelmusic >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ camelmusic ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ camelmusic ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ camelmusic ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ camelmusic ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< camel-maven-plugin:2.12.0.redhat-610379:run (default-cli) @ camelmusic <<<
[INFO] 
[INFO] --- camel-maven-plugin:2.12.0.redhat-610379:run (default-cli) @ camelmusic ---
[INFO] Using org.apache.camel.test.blueprint.Main to initiate a CamelContext
[INFO] Starting Camel ...
[mel.test.blueprint.Main.main()] MainSupport          INFO  Apache Camel 2.12.0.redhat-610379 starting
[mel.test.blueprint.Main.main()] Activator            INFO  Camel activator starting
[mel.test.blueprint.Main.main()] Activator            INFO  Camel activator started
[mel.test.blueprint.Main.main()] BlueprintExtender    INFO  No quiesce support is available, so blueprint components .....
[Blueprint Extender: 1] BlueprintContainerImpl        INFO  Bundle camelmusic is waiting for namespace handlers [http://camel.apache.org/schema/blueprint]
[Blueprint Extender: 1] BlueprintCamelContext         INFO  Apache Camel 2.12.0.redhat-610379 (CamelContext: blueprintContext) is starting
[Blueprint Extender: 1] ManagedManagementStrategy     INFO  JMX is enabled
[Blueprint Extender: 1] BlueprintCamelContext         INFO  AllowUseOriginalMessage is enabled. If access to the original message.....
[Blueprint Extender: 1] BlueprintCamelContext         INFO  StreamCaching is not in use. If using streams then its recommended....
[Blueprint Extender: 1] BlueprintCamelContext         INFO  Route: timerToLog started and consuming from: Endpoint[timer://foo?period=5000]
[Blueprint Extender: 1] BlueprintCamelContext         INFO  Total 1 routes, of which 1 is started.
[Blueprint Extender: 1] BlueprintCamelContext         INFO  Apache Camel 2.12.0.redhat-610379 (CamelContext: blueprintContext) started in 0.521 seconds
[ntext) thread #0 - timer://foo] timerToLog                INFO  The message contains Hi from Camel at 2014-10-19 19:40:07
[ntext) thread #0 - timer://foo] timerToLog                INFO  The message contains Hi from Camel at 2014-10-19 19:40:11

At this point we are ready to start really working with Apache Camel in detail. The next few entries in the tutorial is going to be focussed on getting the basic route up and running. Then quickly move into the advanced techniques required to scale a Camel route to some serious performance.

Friday 17 October 2014

A Fun Camel Ride With Apache Camel Part Two The Required Bits And Pieces To Start

Onto the various bits and pieces you will need to finish this tutorial. Based on the previous post we will need a database server, a tag analyser library and finally an IDE that we can develop the route in. We will also need some additional software once we start  to introduce the more advanced features of Apache Camel, however I will leave those for later.

So keeping with the open-source nature of Apache Camel I will use the following pieces of software to run this demo.

  • MySQL server.
  • The Eclipse IDE with the JBoss Fuse Tooling plug-in (Thank you RedHat).
  • JAudioTagger library(Will be downloaded later as a Maven dependency).
  • Maven
  • Apache Karaf


Since there is enough material on the internet to cover the installation and setup of MySQL, Maven and Apache Camel I wont be touching on those subjects here. For the IDE there are a couple of versions of eclipse you can go for, however I went for the Kepler release and followed the instructions found in this excellent blog by Lars Heinemann.

If you try any other versions your mileage might vary so it might be advisable to stick with these instructions to get this tutorial working. In short you will need to download and install the Kepler release of Eclipse. I would also suggest that you have Maven working before progressing to the next step.

Onto adding the JBoss Fuse tooling plugin via the usual method of installing Eclipse add-ons/plug-ins (hint: Help menu -> install new software). Just out of interest FuseESB or JBossFuse is a ESB product that uses Apache Camel as the backbone of the architecture and is thus one of the easiest ways to work with Apache Camel. 

After installing the JBoss Fuse tooling plug in and restarting eclipse you can now start a new camel project by going to the file menu and selecting the New->Other option(or by pressing CRTL+N). You should now see a fuse tooling folder and by expanding that you will see a option called Fuse Project. See the screen shot below:

Starting a new fuse project.
On the next screen you will be asked which work space to use. I just stuck with the defaults here. On the next screen you will be presented with a New Fuse Project selection screen. There are a lot of various options you can choose here as to what maven archetype you want to use. 

Since this tutorial will be using Apache Karaf for deployment and Apache Karaf is a OSGI runtime we will need to choose a simple OSGI archetype. The archetype that will create the needed OSGI setup is named: camel-archetype-blueprint. Select that and fill out the rest of the details as seen in the screenshot below.

Select the archetype

Now click on the finish button and wait a moment for the project to be setup. The next step is just adding the Fuse Integration perspective to the project. Click on the add perspective button and select the fuse integration perspective as shown in screen shot below:

Select fuse integration perspective

At this point you will have a new project that is ready to start working with. In the next section of the tutorial I will show you how to navigate around the project and where the various important pieces are.