Skip to content

2.3 Create Classifier (Java)

Nepomuk Seiler edited this page Feb 2, 2012 · 1 revision

This tutorial explains how to create a fully Knowing compatible classifier with the java API and Eclipse IDE. First of all configure Knowing as explained in Knowing-Launch.

Create Project

Create an new Eclipse Plug-in Project with following options

  • No contributions to the UI
  • Activator yes

Configure Project

NOTE: When the Java API is finished, this step will be unnecessary Right-click on the project->configure->Add Scala nature

Configure Dependencies

Open META-INF/MANIFEST.MF and open tab dependecies

  • Add de.lmu.ifi.dbs.knowing.core as required Plugin-in.
  • Remove _org.scala-ide.library.2.9.0
  • Add imported packages -> NONE

Optional

Eclipse automatically adds org.eclipse.core.runtime as dependency. If you want to use another OSGi runtime, remove org.eclipse.core.runtime from required bundles and add org.osgi.framework to imported packages as shown here:

Create Classifier

Currently we use the WEKA API as Java API. So you have to implement weka.classifiers.Classifier to create a classifier. WEKA offers an AbstractClassifier class with some default implementations. For furtherinformation on that, please consult WEKA Homepage. Knowing uses WEKA 3.7.3.

Create and Configure your Classifier

Every node in a dpu is created via a factory, so you have to provide a factory for you classifier, which configures and creates the classifier. This factory is registered as an OSGi-Service.

NOTE: Currently there is no Java API for this purpose!

Create the WEKA Classifier Wrapper and Factory

Create the factory

Create a new Scala class MyClassifierFactory and extend de.lmu.ifi.dbs.knowing.core.weka.WekaClassifierFactory . Scala IDE doesn't correctly create the class. Here is an example how it should (could) look like:

package com.example.knowing

import java.util.Properties
import scala.collection.immutable.Map
import akka.actor.Actor

import de.lmu.ifi.dbs.knowing.core.factory._
import de.lmu.ifi.dbs.knowing.core.factory.TFactory._
import de.lmu.ifi.dbs.knowing.core.weka.{WekaClassifier, WekaClassifierFactory }
import de.lmu.ifi.dbs.knowing.core.weka.WekaClassifierFactory._


class MyFactory extends WekaClassifierFactory[MyClassifierWrapper, MyClassifier]
(classOf[MyClassifierWrapper], classOf[MyClassifier]) {
  
  //Creates default Properties which are used if properties aren't set
  override def createDefaultProperties: Properties = {
    val returns = new Properties
    returns.setProperty(DEBUG, "false")
    returns
  }

  //Possible property values. Editor should show these
  override def createPropertyValues: Map[String, Array[_<:Any]] = {
    Map(DEBUG -> boolean_property)
  }

  //Property description
  override def createPropertyDescription: Map[String, String] = {
        Map(DEBUG -> "Debug true/false")
  }

}

The tricky part is the constructor. In the first brackets [...] there are two generics. The first one is the original WEKA Classifier and the second one is the WEKA Classifier Wrapper, which we will created next. The following constructor parameters (...) are the two classes for WEKA Classifier and WEKA Classifier Wrapper.

MyClassifier has only one option to set, the DEBUG option, which is a standard value imported from WekaClassifierFactory.

Create the wrapper

In the same class you add this piece of code

class MyClassifierWrapper extends WekaClassifier(new MyClassifier()) {
  
   override def configure(properties:Properties) = {
     //Configure your classifier here with
     val myClassifier = classifier.asInstanceOf[MyClassifier]
     val debug = properties.getProperty(DEBUG)
     myClassifier.setDebug(debug.toBoolean)
   }
}

This class takes an Instance of your WEKA Classifier as constructor parameter and manages the rest. In this example we override the configure method to set the classifier options based on the given properties.

Register as OSGi Service

Last but not least we have to register MyFactory as an OSGi service. Open your Activator.java and add the following line of code into the start method

myFactory = context.registerService(TFactory.class.getName(), new MyFactory(), null);

Your Activator should look like this.

package com.example.knowing;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

import de.lmu.ifi.dbs.knowing.core.factory.TFactory;

public class Activator implements BundleActivator {

	private static BundleContext context;

	static BundleContext getContext() {
		return context;
	}

	private ServiceRegistration myFactory;

	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext bundleContext) throws Exception {
		Activator.context = bundleContext;
		myFactory = context.registerService(TFactory.class.getName(), new MyFactory(), null);
	}

	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext bundleContext) throws Exception {
		myFactory.unregister();
		Activator.context = null;
	}

}

Make sure that your plugin gets started at the beginning so the service is registered. You can use declarative services as well if you want. The id of MyClassifier is com.example.knowing.MyClassifier if you use it in a DPU.