Categories
development

Another aspect to Spring

Take our Hello, Spring World program from the previous post (hello-spring-world)

Lets says we wanted to count all the characters given to ChatterBox to say. Perhaps ChatterBox is an old fashioned telegraph operator and is paid per character transmitted. We could add some counting code to ChatterBox to keep a count of all characters ‘said’. But ChatterBox is a stubborn so-and-so who believes the administrative work of counting is far beneath him. We therefore need to assign another party the task of tracking everything that ChatterBox needs to say – a supervisor perhaps.

A supervisor is created and assigned to ChatterBox. ChatterBox is meant to notify the supervisor when he is about to say anything – but ChatterBox is often a little forgetful and fails to do so. This means messages are sent (strings are ‘said’) without the telegraph company able to bill customers accordingly. What is needed is a way for the supervisor to just examine any strings before they are given to ChatterBox. AOP (Aspect Oriented Programming) gives us this facility.

We can configure the Spring container to set up our beans in such a way that whenever ChatterBox.saySomething() is called, we can make a call to our supervisor bean, ChatterBoxCounterAdvice, which can then track the number of characters being passed to saySomething(). In general this type of programming is very useful for tasks such as logging and transaction management where you want the classes being advised to concentrate on their business logic without having to carry out functions not related to their business role. Further, this reduces coupling of the business logic classes against Logging or Transaction Management classes.

  1. To make use of Spring Aspects we have a few new dependencies for the project. Add the three new maven dependencies to the project’s pom.xml file as shown in Listing 1.
  2. The helloSpring.xml file needs to me modified to describe the advice to be applied to ChatterBox. The new helloSpring.xml file is provided in Listing 2.
  3. Create the ChatterBoxCounterAdvice class per Listing 3.
  4. Change HelloApp class per Listing 4.

ChatterBoxCounterAdvice (Listing 3) has a single dependency on an OutputStream (same as ChatterBox). We will once again use Spring to configure this dependency for us. The Advice provided by ChatterBoxCounterAdvice is in method countCharacters() whose job is to examine determine the number of characters in a string and add that to a cumulative sum. ChatterBoxCounterAdvice also has a report() method to print the sum of characters to the OutputStream dependency.

ChatterBoxCounterAdvice has no coupling to ChatterBox, and ChatterBox has no coupling to ChatterBoxCounterAdvice, therefore we need to weave ChatterBoxCounterAdvice’s countCharacters() method into the calls to ChatterBox.saySomething(). Spring provides configuration items to do this.

Looking at helloSpring.xml (Listing 2) we can see a <bean> entry with an id of counter describing how to instantiate and inject dependencies into ChatterBoxCounterAdvice. There is also an <aop:config> element which describes how to weave ChatterBoxCounterAdvice into ChatterBox. The <aop:aspect> references the counter bean as the bean that will be weaved into any pointcuts identified in the remainder of the aop:aspect element.

The <aop:before> identifies a method of ChatterBoxCounterAdvice that should be executed before any matched pointcuts. The pointcut in this example is an expression which identifies an execution of the method com.foomoo.example.spring.helloSpring.ChatterBox.saySomething() which takes a single String parameter and returns any type (*). The argument to the matched method should be bound to the aStringToCount argument of ChatterBoxCounterAdvice.countCharacters() – specified by the args(aStringToCount) element of the expression.

That’s everything we need to get spring to weave our supervisor class into the execution of the saySomething() method of ChatterBox. Next we might want to see the results determined by the supervisor (ChatterBoxCounterAdvice). HelloApp has been modified to make three calls to ChatterBox.saySomething(), meaning ChatterBoxCounterAdvice should have been quietly counting the characters said for us. HelloApp then retrieves ChatterBoxCounterAdvice from the spring context and directs it to report its findings. Running HelloApp should similar console output to the following:

02-Mar-2010 16:07:01 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1a05308: startup date [Tue Mar 02 16:07:01 GMT 2010]; root of context hierarchy
02-Mar-2010 16:07:02 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [helloSpring.xml]
02-Mar-2010 16:07:02 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@54a328: defining beans [chatterBox,counter,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.aop.aspectj.AspectJPointcutAdvisor#0]; root of factory hierarchy
ten ten te
twenty twenty twenty
thirty thirty thirty thirty th
Characters Counted: 60

The last line is the output reported from ChatterBoxCounterAdvice. We can see the three strings “ten ten te”, “twenty twenty twenty” and “thirty thirty thirty thirty th” were correctly counted to have a total of 60 characters.

Listing 1 – excerpt from pom.xml

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>3.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.8</version>
</dependency>
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
</dependency>

Listing 2 – helloSpring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="chatterBox"
          class="com.foomoo.example.spring.helloSpring.ChatterBox">
        <property name="outputStream">
            <util:constant static-field="java.lang.System.out" />
        </property>
    </bean>
    <bean id="counter"
          class="com.foomoo.example.spring.helloSpring.advice.ChatterBoxCounterAdvice">
        <property name="outputStream">
            <util:constant static-field="java.lang.System.out" />
        </property>
    </bean>
    <aop:config>
        <aop:aspect id="chatterBoxCounterAspect" ref="counter">
            <aop:before method="countCharacters"
                        pointcut="execution(* com.foomoo.example.spring.helloSpring.ChatterBox.saySomething(String)) and args(aStringToCount)" />
        </aop:aspect>
    </aop:config>
</beans>

Listing 3 – ChatterBoxCounterAdvice.java

package com.foomoo.example.spring.helloSpring.advice;

import java.io.OutputStream;
import java.io.PrintStream;

public class ChatterBoxCounterAdvice {
    private OutputStream outputStream;
    private int characterCount;

    public void setOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public void report() {
        new PrintStream(outputStream).println("Characters Counted: " + characterCount);
    }

    public void countCharacters(String aStringToCount) {
        characterCount += aStringToCount.length();
    }
}

Listing 4 – HelloApp.java

package com.foomoo.example.spring.helloSpring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.foomoo.example.spring.helloSpring.advice.ChatterBoxCounterAdvice;

public class HelloApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "helloSpring.xml");
        ChatterBox bean = context.getBean("chatterBox", ChatterBox.class);
        bean.saySomething("ten ten te");
        bean.saySomething("twenty twenty twenty");
        bean.saySomething("thirty thirty thirty thirty th");

        ChatterBoxCounterAdvice counter = context.getBean("counter", ChatterBoxCounterAdvice.class);
        counter.report();
    }
}
Categories
development

Hello, Spring World

This post shows how to create a very simple Hello, World type program using the Spring framework’s Dependency Injection functionality. The project presented in this post can be downloaded at https://github.com/danwatford/helloSpring

  1. Load the SpringSource Tool Suite, or your Spring enable eclipse installation.
  2. Select File->New->Project…
  3. Choose the Maven Project from the Maven folder.
  4. Enable option (check the checkbox for) Create a simple project. Click Next.
  5. Set a Group Id. (e.g. com.foomoo.example.spring).
  6. Set an Artifact Id. (e.g. helloSpring). Click Next.
  7. Click Finish
  8. Double click on pom.xml, choose the pom.xml tab to view the source ensure the and tags contain the same as that in Listing 1. Save the pom file. Right-click on the project in Package Explorer and select Maven->Update Project Configuration. This step should download the Spring and JUnit dependencies needed for this project.
  9. Create a new package (e.g com.foomoo.example.spring.helloSpring) under the src/main/java folder. [Right-click on the src/main/java folder of the newly created project in the Package Explorer view. Select New->Package. Enter the package name. Click Finish].
  10. Create a new class (e.g. ChatterBox) within the package just created. [Right-click on the package. Select New->Class. Enter the class name. Click Finish]. Set the contents of ChatterBox.java per Listing 2.
  11. Create a new class (e.g. HelloApp) within the same package. Set the contents of HelloApp.java per Listing 3.
  12. Create a new Spring Bean Configuration File in src/main/resources. [Right-click on the src/main/resources folder. Select New->Spring Bean Configuration File.
  13. Enter helloSpring.xml as the file name. Ensure the checkbox for Add Spring project nature if required is selected. Click Finish.
  14. Double-click on the helloSpring.xml file, choose the Source tab and ensure the contents of the file match Listing 4.

Thats the project and code setup. Next we’ll see the project running and producing some output. Right-click on HelloApp.java and choose Run As->Java Application. You should see some console output similar to the following:

01-Mar-2010 21:03:49 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1a05308: startup date [Mon Mar 01 21:03:49 GMT 2010]; root of context hierarchy
01-Mar-2010 21:03:49 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [helloSpring.xml]
01-Mar-2010 21:03:49 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1a786c3: defining beans [chatterBox]; root of factory hierarchy
This should appear once.
This should appear twice.This should appear twice.

The last two lines are the output from the application, the rest is logging output from the Spring Framework.

ChatterBox.java (listing 2) contains a setter method, setOutputStream(), and two business logic methods, saySomething() and saySaySomething(). The business of the ChatterBox is to say something. Method saySomething() will print its String argument to an OutputStream, method saySaySomething will print its String argument to an OutputStream twice.

We can have the spring framework instantiate and configure the ChatterBox as a Bean via an ApplicationContext. When an ApplicationContext is initialised we advise it of a configuration (such as the helloSpring.xml file – listing 4) to describe the Beans. Each bean entry in an XML configuration file includes a class attribute used by spring to identify the class to instantiate for the bean. Top-level beans will also include an id attribute used to search the configuration for a particular bean.

ChatterBox has a dependency on an OutputStream which must be injected into it before it can perform its business logic. Since we rely on spring to instantiate ChatterBox, it is reasonable to have spring handle depedency injection. In the helloSpring.xml file within the tag we have a tag with a name attribute set to outputStream. Spring expects ChatterBox to have a setter matching the name attribute of the property – in this case, setOutputStream(). The contents of this tag describe the argument that should be passed to the setter, here we are using the static field, System.out. tags can also refer to other beans.

Looking at HelloApp.java (listing 3) an ApplicationContext (ClassPathXmlApplicationContext is an ApplicationContext configured by an XML resource found on the classpath) is created and used to retrieve the ChatterBox instance described in the configuration with id, chatterBox. Spring will instantiate this object and inject any dependencies needed.

Once the main method has a reference to the ChatterBox bean it executes the business logic provided by the bean, causing the last two lines of our console output. Usage of the Spring Framework removes the burden of wiring our application’s objects in code where the object structure may not be so easy to read.

Listing 1 – pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.foomoo.example.spring</groupId>
 <artifactId>helloSpring</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.1</version>
    <configuration>
     <source>1.6</source>
     <target>1.6</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.0.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.8.1</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>

Listing 2 – ChatterBox.java

package com.foomoo.example.spring.helloSpring;

import java.io.OutputStream;
import java.io.PrintStream;

public class ChatterBox {
 private OutputStream outputStream;
 public void setOutputStream(OutputStream outputStream) {this.outputStream = outputStream;}
 public void saySomething(String something) {new PrintStream(outputStream).println(something);}
 public void saySaySomething(String something) {new PrintStream(outputStream).println(something + something);}
}

Listing 3 – HelloApp.java

package com.foomoo.example.spring.helloSpring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloApp {
 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "helloSpring.xml");
  ChatterBox bean = context.getBean("chatterBox", ChatterBox.class);
  bean.saySomething("This should appear once.");
  bean.saySaySomething("This should appear twice.");
 }
}

Listing 4 – helloSpring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">


 <bean id="chatterBox"
  class="com.foomoo.example.spring.helloSpring.ChatterBox">
  <property name="outputStream">
   <util:constant static-field="java.lang.System.out" />
  </property>
 </bean>
</beans>
Categories
development

Getting going with Spring development

I’m working on some small projects that I will post here to demonstrate some Spring functionality.

To follow/build the projects I recommend installing the SpringSource Tool Suite. The tool suite is a version of eclipse customised towards Spring development activities. You can carry out Spring development without the Tool Suite, but I’d recommend its use if you’re new to Spring.

It also comes with Maven preconfigured. I use Maven since it provides a nice way of grabbing all the dependencies I need for a project. I’d recommend taking a look at http://maven.apache.org/.
Getting SpringSource Tool Suite Visit http://www.springsource.com/products/sts and click the Download link towards the bottom of the page. The next page requires you enter some information and click the Download Now button to get access to the software.
The next page provides a list of all the different distribution options. I went for the win32 installer option.
Installing SpringSource Tool Suite If you have downloaded an installer option, run the installer.

  • For steps 1 to 4 of the installation accept the license and any defaults presented.
  • Step 5 requires identifying the path to your JDK. For example, C:Program FilesJavajdk1.6.0_17.
  • At step 6 the installer will begin copying and configuring STS.
  • Accept the defaults for steps 7 and 8.
Categories
development

Getting some data and building a decoder

If you’ve followed my recent posts so far we’ve built a little hibernate app in eclipse to write amateur radio (ham) call signs to a mysql database. Next it would be nice to get a source of data.

The APRS-IS (Automatic Packet Reporting System-Internet Service) channels data from APRS networks from around the world and gives us a access to a wealth of location reports from around the world.

From the APRS-IS website, follow the link Services->APRS Service->APRS-IS Servers to get a list of all servers listed by region. Don’t use any of the core servers. After browsing through the list of servers I found that I couldn’t connect to several, and some others required you to have already registered with them.

One server I found was aprsdcsp.kc7zru.net on port 10152. If you use this server, don’t overdo it. To see the type of data returned from the server telnet to it:

telnet aprsdcsp.kc7zru.net 10152

This is some output I captured:

/home/user> ./telnet aprsdcsp.kc7zru.net 10152
Trying 67.40.120.251...
Connected to kc7zru.net.
Escape character is '^]'.
# aprsd 2.2.5-15 Oct 2002 aprsd group
aprsdCSP>APD225,TCPIP*:!4249.72N/10624.04WIPHG7130/A=005156 Casper, WY aprsd server & I-gate
# **PLEASE EDIT THIS TO SUIT YOUR LOCAL REQUIRMENTS. Start each comment line with a "#"   **
Welcome to the KC7ZRU Rocky Mnt Regional APRS server.
Direct comments to 'kc7zru "at" arrl "dot" net'
Type ctrl-D to disconnect.
apdCSP-2>JAVA::javaTITLE:Live, local APRS data from Casper, Wyoming [DN62]
W8FSM-15>APVR30,TCPIP*,qAC,T2NUENGLD:;IRLP-4924*090535z4233.22NI08420.47W0444575+107IDLE     
WX4BB-1>APW285,KR4XN-2*,WIDE2-1,qAR,KE4TTA-2:>061701 Warrenville, SC  WX observations
W5NRU-13>APVR30,TCPIP*,qAC,T2MIDWEST:;IRLP-5980*090535z3131.14NI08907.23W0145.230MHz T136 R40m IDLE   
KA1MZY-15>APVR30,TCPIP*,qAC,T2MCI:;IRLP-3221*090535z2932.95NI09819.71W0147.515MHz T225 R20m IDLE     
KC6VVT>W9MKS-15,qAR,W9MKS-15:;147.120IL*111111z4111.41N/08858.88WrT103 R30m NetW7pm Mtg1M7pm
KA1EKS-8>APNU19,W1PIG*,WIDE4-2,qAS,N1ZRL-1:!4537.12NS06852.97W#PHG2260 W4,MEn SouthTwinLake Maine 145.25-
N4HAJ>APU25N,TCPIP*,qAC,T2MIDWEST:=3517.73N/07739.83WI {UIV32N}
DB0RDT-13>APRS,TCPIP*,qAC,T2NZAA:;IRLP5822 *090535z4849.95NC01117.57E0439.150MHz, -7600 offset : LINK CLEAR
N3KTX-7>APN383,qAR,N3UJJ:!3856.34NS07603.76W#PHG7580 W3.MDn   MasonDixon Group
DL1GKR>BEACON,TCPIP*,qAS,DL1GKR-5:;IRE4222  *090534z4809.36N/00948.81E=000/000/A=001712 IRE4222 km125
K5LLL-13>APU25N,WIDE2-2,qAR,KE5C-15:=3014.77N/09709.61W#W-R-T Digi/WX for McDade, TX {UIV32N}
IW9BGV-9>SX0Y47,IR9AK*,WIDE2-1,qAr,IT9OBK*:`)/Ul >/
HS6VW>APU25N,TCPIP*,qAC,T2THAPR-2:=1841.40N/09856.52E-BanTawai  HangDong  ChiangMai Thailand
VE2SSG>TV4R7Y,WIDE1-1,WIDE2-1,qAO,VE2MGJ:`c<{l!Lu/"43} F6BIG-9>APZ16,WIDE,WIDE,qAS,HB3YNV:!4547.30N/00606.00E#PHG1960/Mt.Semnoz TEST APRS DIGIPEATER (UIDIGI 1.6)
HG3PMF-1>APZ186,WIDE7-5,qAR,HG1DFB-7:!4606.53N/01812.18E#PHG3230/APRS DIGIPEATER PECS-TUBES SysOp:HG3SK
W2LV>APNPXX,qAR,N0IGD-1:!4111.55N/07445.19W#PHG4730/11,22,21,33 Sussex County ARC DIGI
N5SKU-15>APRS,TCPIP*,qAC,T2NZAA:;IRLP3382 *090535z3259.52NO09643.74W&446.125MHz, - offset PL tone 123.0: NODE OFFLINE
K4GIG-7>APZ19,qAR,N8DEU-5:!3424.30NS08648.02W#PHG5670/W2,ALn    Wilson Mtn AL 1360ft
KA8ZGE>APU25N,TCPIP*,qAC,T2APRSFL:=4052.85NS08435.96W#PHG51304/Wn,BCn/Van Wert {UIV32N}
KB9OQF>APAGW,WIDE3-2,qAR,KA8ZGE:=4031.63N/08530.25W-PHG7180Upland, Indiana - kb9oqf@aol.com
DB0XIK>APU25N,TCPIP*,qAC,T2SOLO:=4900.73N/00824.76ErAPRS E-Lnk D-STAR   Projekt
UA5ERC-15>APNU19,EA5RCI-14*,TRACE4-4,qAS,EA5CJA:!3931.13N/00059.27W#PHG2110/RCE Resuena. ea5e�@ea5erc.myvoc.com
WLNK-1>APRS,TCPIP*,qAC,T2MCI:=3853.75N/07702.50W?APRSLink - www.winlink.org/aprslink
G7HEJ>APU25N,WIDE2-2,qAR,G1EUH:=5349.81N/00302.47W-If you hear me email me. g7hej@yahoo.com
KE5GLK-1>APRS,TCPIP*,qAC,FIRST:@090534z2902.60N/09524.97W_125/001g...t070r...p...P000h97b10220.DsVP
VE6TVN>APOT21,EDMNTN*,WIDE2-1,qAR,VE6GPS:!5332.57N/11325.59W>
GB7DXC>APZS05,TCPIP*,qAC,T2IRELAND:/090535z5201.8 N/00206.4 W%Bredon [IO82WA]

Lets convert the existing eclipse project to grab data from this server, decode it and generate call sign records. Alternatively, grab a zipped copy of the eclipse project here.

Create package com.foomoo.aprs.decoder. Add the following 4 files to this package.

DecodedItem.java

package com.foomoo.aprs.decoder;

public abstract class DecodedItem {
 public abstract String toString();
}

Decoder.java

package com.foomoo.aprs.decoder;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;

public abstract class Decoder extends Thread {

 public static int DEFAULT_SLEEP_TIME = 10;

 BufferedReader br;
 int sleepTime = DEFAULT_SLEEP_TIME;
 private boolean keepRunning = true;

 ArrayList decodedItems = new ArrayList();

 public Decoder(InputStream is) {
  br = new BufferedReader(new InputStreamReader(is));
 }

 public void run() {
  while (keepRunning) {
   try {
    String searchString = br.readLine();
    while(null != searchString && keepRunning) {
     Collection items = decode(searchString);
     decodedItems.addAll(items);
     searchString = br.readLine();
    }

    try {
     sleep(sleepTime);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   } catch (IOException e) {
    e.printStackTrace();
    keepRunning = false;
   }
  }
 }

 protected abstract Collection decode(String searchString);

 public boolean hasItems() {
  return !decodedItems.isEmpty();
 }

 public DecodedItem getItem() {
  if (!decodedItems.isEmpty()) {
   return decodedItems.remove(0);
  }
  return null;
 }

 public void stopRunning() {
  keepRunning = false;
 }
}

CallsignDecodedItem.java

package com.foomoo.aprs.decoder;

public class CallsignDecodedItem extends DecodedItem {

 String callsign;

 @Override
 public String toString() {
  return callsign;
 }

 public String getCallsign() {
  return callsign;
 }

 public void setCallsign(String callsign) {
  this.callsign = callsign;
 }
}

AprsisCallsignDecoder.java

package com.foomoo.aprs.decoder;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;

public class AprsisCallsignDecoder extends Decoder {

 public AprsisCallsignDecoder(InputStream is) {
  super(is);
 }

 @Override
 protected Collection decode(String searchString) {
     ArrayList retList = new ArrayList();
  String splitString [] = searchString.split (">");
        if (splitString.length > 1) {
         CallsignDecodedItem di = new CallsignDecodedItem();
         di.setCallsign(splitString[0]);

         retList.add(di);
        }
  return retList;
 }

}

Change Aprs.java to contain the following:

package com.foomoo.aprs;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import org.hibernate.Session;

import com.foomoo.aprs.data.Callsign;
import com.foomoo.aprs.data.util.HibernateUtil;
import com.foomoo.aprs.decoder.AprsisCallsignDecoder;
import com.foomoo.aprs.decoder.CallsignDecodedItem;

public class Aprs {

    private static int MAX_CALLSIGNS = 20;

    public static void main(String[] args) throws UnknownHostException,
            IOException {
        System.out.println("Hello, hibernate!");

        Socket socket = new Socket("aprsdcsp.kc7zru.net", 10152);
        InputStream is = socket.getInputStream();

        AprsisCallsignDecoder decoder = new AprsisCallsignDecoder(is);
        decoder.start();

        int count = 0;
        while (count < MAX_CALLSIGNS) {
            while (decoder.hasItems() && count < MAX_CALLSIGNS) {
                CallsignDecodedItem di = (CallsignDecodedItem) decoder
                        .getItem();

                Callsign c = new Callsign();
                c.setCallsign(di.getCallsign());
                System.out.println(di.getCallsign());
                Session session = HibernateUtil.getSession();
                session.beginTransaction();
                session.save(c);
                session.getTransaction().commit();

                count++;
            }

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        decoder.stopRunning();
    }
}

Run the program, if a connection is made to the server, the program should run until 20 callsign records are created. Check the contents of the Callsign table after running to observe that 20 callsigns have been captured.

Categories
development

Config file for Log4J and using a HibernateUtil class

The hibernate tutorial recommends using a utility class for getting access to a hibernate session. We use the session to open and close transactions and to associate object instances with hibernate for persistence and management.

Create a new package, com.foomoo.aprs.data.util. Add file HibernateUtil.java with the following contents:

package com.foomoo.aprs.data.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static final SessionFactory sessionFactory;

    static {
        sessionFactory = new Configuration().configure().buildSessionFactory();
    }

    public static Session getSession() {
        return sessionFactory.getCurrentSession();
    }
}

Change Aprs.java to contain the following:

package com.foomoo.aprs;

import org.hibernate.Session;

import com.foomoo.aprs.data.Callsign;
import com.foomoo.aprs.data.util.HibernateUtil;

public class Aprs {
    public static void main(String[] args) {
        System.out.println("Hello, hibernate!");

        Session session = HibernateUtil.getSession();

        session.beginTransaction();

        Callsign callsign = new Callsign();
        callsign.setCallsign("M0EBK");
        session.save(callsign);
        callsign = new Callsign();
        callsign.setCallsign("M3DBO");
        session.save(callsign);

        session.getTransaction().commit();
    }
}

It is about time we keep log4j happy. Create a new file in the lib directory called log4j.properties with the following contents taken from the hibernate tutorial:

### direct log messages to stdout ###log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ####log4j.appender.file=org.apache.log4j.FileAppender#log4j.appender.file.File=hibernate.log#log4j.appender.file.layout=org.apache.log4j.PatternLayout#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=warn, stdout
#log4j.logger.org.hibernate=infolog4j.logger.org.hibernate=warn
### log HQL query parser activity#log4j.logger.org.hibernate.hql.ast.AST=debug
### log just the SQL#log4j.logger.org.hibernate.SQL=debug
### log JDBC bind parameters ###log4j.logger.org.hibernate.type=info#log4j.logger.org.hibernate.type=debug
### log schema export/update ###log4j.logger.org.hibernate.tool.hbm2ddl=warn
### log HQL parse trees#log4j.logger.org.hibernate.hql=debug
### log cache activity ####log4j.logger.org.hibernate.cache=debug
### log transaction activity#log4j.logger.org.hibernate.transaction=debug
### log JDBC resource acquisition#log4j.logger.org.hibernate.jdbc=debug
### enable the following line if you want to track down connection ###### leakages when using DriverManagerConnectionProvider ####log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
Categories
development

Setting up the hibernate compile time and runtime environments with eclipse

This post will deal with getting the relevant jars to compile and execute programs using hibernate with eclipse. The first persistent class will not use annotations, but will use a mapping file – this reduces (by a tiny amount) the number of jars we need to get setup.

Go to http://www.hibernate.org and download Hibernate Core. I downloaded version 3.3.1.GA. From the main page, click on the download link at the left of the page. Review the Binary Releases and click on the download link to the right of the Hibernate Core entry.

I downloaded Hibernate Core to /home/user/hibernate-distribution-3.3.1.GA-dist.tar.gz. This file was extracted using:

tar zxfv hibernate-distribution-3.3.1.GA-dist.tar.gz

executed in a terminal (open terminal using Ctrl+Alt+t).

Next we need to get the JDBC driver for the mysql database. Visit http://dev.mysql.com/downloads/and scroll down to the MySql Connector/J entry. I downloaded version 5.1 as a tar.gz file to /home/user/mysql-connector-java-5.1.7.tar.gz and extracted the file using:

tar zxfv mysql-connector-java-5.1.7.tar.gz

Next we need to gather the libraries for logging. Hibernate uses SFL4J – Simple Logging Facade for Java. SFL4J provides a logging interface and then several adaptors to other logging systems. The only logging system I’ve had experience with is Log4J, so I will be using that.

I visited http://www.slf4j.org/download.html and downloaded and extracted slf4j-1.5.6.tar.gz to /home/user. I then visited http://logging.apache.org/log4j/1.2/download.html and downloaded and extracted apache-log4j-1.2.15.tar.gz to /home/user.

We now have all needed libraries to compile and execute a Hibernate program. Next job is to set up an eclipse project to use them.

Open eclipse and create a new java project called aprs. APRS is a signalling protocol used by, among others, amateur radio operators to provide location reports. Most people broadcasting APRS signals will do so when mobile and have equipment configured with a GPS receiver to determine their location. Many amatuer radio stations will receive the APRS broadcasts and will forward them onto various internet servers. These servers combine the APRS signal reports and provide a datastream for any interested listeners. This datastream will be a good source of data for us to populate a database with.

Accept all defaults when creating the new project.

About the only thing we need to tell eclipse about when compiling our project is the location of the Hibernate API. Right-click on the aprs project in package explorer and select Build Path -> Configure Build Path…

Select the Libraries tab and click on the Add External Jars… button. In the file selection dialog, navigate to the /home/user/hibernate-distribution-3.3.1.GA directory and then select the hibernate3.jar file. Click OK.

Click OK to close aprs properties dialog.

Next we will want to configure the runtime environment. While doing this we’ll create a main() method to execute.

Right-click on aprs in package explorer. Select New -> Package. Name the package com.foomoo.aprs then click Finish.

Right-click the newly created package in package explorer and select New -> Class. Name the new class Aprs and check the check-box to create the stubs for public static void main(). Click Finish.

The Aprs.java file should be displayed in the editor. Add the following statement to the main method.

System.out.println("Hello, hibernate!");

Save the file. Right-click in the editor and select Run As -> Java Application. The program should run and the console display the hello message.

From the Run menu, select Debug Configurations… An entry for the Aprs Java Application should already be highlighted. We need to add our runtime libraries to the class path for this configuration. The libraries we are adding are the support libaries needed by hibernate, the logging libraries (SLF4J and Log4J), and the mysql java connector library.

Click on the classpath tab then the Add External Jars button. Navigate to /home/user/hibernate-distribution-3.3.1.GA/lib/required. Add all files except for slf4j-api-1.5.2 by selecting them and clicking OK. The slf4j file on its own will not provide the full logging hibernate needs, that’s why we have downloaded a copy of the slf4j distribution to use.

Click on Add External Jars button again. Navigate to /home/user/slf4j-1.5.6/ and add files slf4j-api-1.5.6.jar and slf4j-log4j12-1.5.6.jar.

Click on Add External Jars button. Navigate to /home/user/apache-log4j-1.2.15 and add file log4j-1.2.15.jar.

Click on Add External Jars button. Navigate to /home/user/mysql-connector-java-5.1.7/ and add file mysql-connector-java-5.1.7.jar.

Click Apply then click Debug. The program should execute and disply the hello message in the console.

That’s the runtime environment setup. My next post will create a small program that actually persists something to the database using hibernate.

Categories
development

A basic hibernate program

If you’ve followed my previous posts you should now have eclipse running with a basic project created called aprs. In this post we are going to add a Callsign class to the project. All amateur radio broadcasts are identifiable by a callsign. Eventually we will capture those call signs and persist them to the database along with associated location reports. Add a new package, com.foomoo.aprs.data, to the project. Create a new class, Callsign, in the new package. Save the new java file with the following contents:

package com.foomoo.aprs.data;

public class Callsign {
    private Long id;
    private String callsign;

    public Long getId() {
        return id;
    }

    private void setId(Long id) {
        this.id = id;
    }

    public String getCallsign() {
        return callsign;
    }

    public void setCallsign(String callsign) {
        this.callsign = callsign;
    }
}

Create the file Callsign.hbm.xml in the com.foomoo.aprs.data package with the following contents:

<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.foomoo.aprs.data.Callsign">

        <id name="id">
            <generator class="native"/>
        </id>

        <property name="callsign"/>

    </class>

</hibernate-mapping>

So above we have defined a POJO, Callsign, that contains 2 attributes, id and callsign. We then created a mapping file for the Callsign POJO that will map the contents of Callsign to a database. The mapping is pretty sparse in that it includes virtually now database specifics at all. It will use the database specified for the hibernate session in use when Callsigns are read or written. The above mapping file simply declares the Callsign attributes that should be persisted, and declares that id, the primary key, should be generated according to the native method of the system. All database specifics in this example, i.e. the table and columns to be written will be set as hibenate defaults inferred from the class and attribute names.

There is one more essential configuration item before we get to the code. We need to setup hibernate.cfg.xml. Right-click on aprs in package explorer and create a new folder called lib. Create a new file in this folder called hibernate.cfg.xml with the following contents:

<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="connection.url">jdbc:mysql://localhost/ham</property>
        <property name="connection.username">root</property>
        <property name="connection.password"></property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
        <property name="current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>

        <!--This statement will cause the database schema to be dropped andrecreated at startup. We will comment it out after initial programruns to maintain our data between program executions.-->
        <property name="hbm2ddl.auto">create</property>

        <mapping resource="com/foomoo/aprs/data/Callsign.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

Open our aprs debug configuration and select the classpath tab. Select User Entries and then click on the Advanced… button. Add the aprs/lib folder to the classpath. Click apply then close in the Debug Configuration dialog.

If the mysql server isn’t running, start it from a terminal (Ctrl+Alt+t) using the command:
sudo /home/user/lampp/lampp start

Then access http://localhost in a web browser, click on phpMyAdmin and create a new database called ham. Don’t worry about creating any tables, they will be created for us when we run our hibernate program.

Setup Aprs.java with the following contents:

package com.foomoo.aprs;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

import com.foomoo.aprs.data.Callsign;

public class Aprs {
    public static void main(String[] args) {
        System.out.println("Hello, hibernate!");

        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.getCurrentSession();

        session.beginTransaction();

        Callsign callsign = new Callsign();
        callsign.setCallsign("M0EBK");
        session.save(callsign);

        session.getTransaction().commit();
    }
}

From the Run menu click Run. The console should show the following:

Hello, hibernate!
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into Callsign (callsign) values (?)

The warnings are from log4j complaining that we haven’t provided a properties file. We’ll ignore that for now.

Go back to phpMyAdmin in the browser and refresh the page. Click on the Browse action icon for the Callsign table.

Categories
development

Setting up a development environment on the Eee PC 901 Linux variant

A couple of months ago I purchased an Eee PC 901. A couple of days ago I decided it was about time I set it up for a bit of Java development.

All discussions about whether the 901 is suitable for active development aside, it should be possible.

My Eee PC is still in its stock configuration. A lot of people will install a different OS, but I’m still using the ASUS Xandros Linux variant that came pre-installed.

First tool to be installed was eclipse. I downloaded package ‘Eclipse IDE for Java EE Developers‘ from www.eclipse.org to /home/user/eclipse-jee-ganymede-SR1-linux-gtk.tar.gz. To extract this file use Ctrl+Alt+t to open up a terminal and enter the following command:

tar zxfv eclipse-jee-ganymede-SR1-linux-gtk.tar.gz

This will extract the eclipse distribution to an eclipse directory. To launch eclipse from the same terminal window enter the command:

eclipse/eclipse

The JRE installed on the eeePC is 1.5.0.10. Eclipse will detect the available JRE and will setup code compliance to 1.5. Right now I don’t have any need to go to 1.6 so won’t worry about installing a new JRE.

Categories
development

Getting Mysql On Eee Pc 901

I fancied doing some database work on the Eee PC so needed a DBMS to play with. Mysql is a very popular database system so would be worth getting installed.

The ASUS dpkg repositories for the eee pc don’t contain mysql server, and I didn’t want to mess around with other repositories. One day I’ll probably install debian on this netbook and then be able to seamlessly install practically anything I want – but for now I’ll just still with binary tar balls.

Checking on the eeeuser.com website and searching for mysql I came across several references to LAMP (Linux Apache Mysql Php) – basically defining a software development stack. I thought this would be a simpler way to get mysql running and have some other tools (namely phpMyAdmin) to boot.

Apachefriends.org have built a lamp distribution called xammp for linux. I downloaded version 1.7 from it from http://www.apachefriends.org/en/xampp-linux.html to /home/user/xampp-linux-1.7.tar.gz. The instructions at the website call for the tar.gz file to be extracted to /opt, but on my eee pc there isn’t much space:

/home/user> df -h
 Filesystem            Size  Used Avail Use% Mounted on
 rootfs                680M  487M  159M  76% /
 /dev/sda1             680M  487M  159M  76% /
 none                  680M  487M  159M  76% /
 tmpfs                 503M   12K  503M   1% /dev/shm
 tmpfs                 128M  484K  128M   1% /tmp
 /dev/sdb1              15G  1.2G   13G   9% /home
 /dev/sda1             3.1G  2.8G  259M  92% /ro

/opt sits on /dev/sda1 and only has 159M left and the xampp distribution when extracted requires around 210M.

I extracted the downloaded tar.gz file by opening a terminal (Ctrl+Alt+t) and using the command:

tar xvfz xampp-linux-1.7.tar.gz

This command will extract the xampp distribution to directory /home/user/lampp. You can start the mysql and apache servers with the command:

sudo lampp/lampp start

but you will get error messages relating to lampp not being installed in /opt. To remedy this create a symbolic link from /opt to /home/user/lampp with the command:

sudo ln -s /home/user/lampp/ /opt/lampp

You can then launch the servers with:

sudo /opt/lampp/lampp start

You can then point your browser at http://localhost and hopefully see this:

From here you can click on the link to phpMyAdmin to get to some database management tools.