Child pages
  • Practical Java Algorithm Development

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

It is recommended that you read CIShell Basics and run through Tutorial 1: Creating a Hello World Java Algorithm if you are not already familiar with basic CIShell development. You will need to have installed Eclipse and the CIShell Java Algorithm Wizard, as described in Tutorial 0: Setting Up the Development Environment. The algorithm produced in this tutorial requires the Network Workbench Sci2 tool environment to run, so you will need to have downloaded the Network Workbench Sci2 tool installed to complete this tutorial.

...

Whether you are building a plugin for an existing CIShell tool, or creating a new tool, it will be useful to have read access to the CIShell repository. A guide to configuring Eclipse for CIShell repository access can be found here. (It shouldn't be too complicated. It only involves downloading a plugin which allows Eclipse to access Subversion (SVN) Github repositories, and then connecting to the CIShell sourceforge.net Subversion repositoryCIShell repository).

Once you have downloaded and configured the "Subclipse" Subversion plugin configured a Git plugin as described in the document linked above, do the following:

1) Switch to the "SVN Repository Exploring" perspective  perspective in Eclipse by clicking "Window" -> "Open >Open Perspective" -> "Other>Other..." -> "SVN >SVN Repository Exploring".

2) Create a repository location in Eclipse by right-clicking in the "SVN Repository" view -> "New" -> "Repository Location" and pasting in "https://cishell.svn.sourceforge.net/svnroot/cishell", then clicking "Finish".

3) Expand the repository location we just created, and navigate to "trunk/libs/edu.uci.ics.jung".

4) Right-click the "edu.uci.ics.jung" project, and select "Checkout", then click "Finish".

Follow the "Maven Integration for Eclipse (a.k.a. m2eclipse or m2e)" instructions here after forking a copy of the CIShell Git. This will give you access to all necessary plugins for these tutorials.

 

We have now checked out the Jung library plugin. If you switch back to the Java perspective, you will notice that the plugin is in your Eclipse workspace. If you open the "META-INF/MANIFEST.MF" file of this plugin, you'll notice that it is exporting a variety of packages from the Jung java library. The plugin we are about to create will import some of these packages.

...

To start the Java Algorithm Wizard, go to File->New->Project.. in Eclipse, and select Java Algorithm Wizard from the CIShell folder.

The first few pages are much the same as they were in Tutorial 1: Creating a Hello World Java Algorithm. For this tutorial, we'll presume that you're using the default values for the first few pages (up until the second Algorithm Properties page), with the following exceptions:

Project Properties Page: Name your project '"org.my.attacktolerance'".

Bundle Properties Page: Set your Bundle Name to "Attack Tolerance". Set your Bundle Symbolic Name to "org.my.attacktolerance".

Algorithm Properties Page 1: Set your Algorithm Name to "Attack Tolerance" Set your Algorithm Class name to "AttackToleranceAlgorithm" Set your Algorithm Package to '"org.my.attacktolerance'".

On the second Algorithm Properties page we'll be doing something new, which requires a bit of explanation. in_data for a CIShell algorithm can be either files or Java objects. in_data for files is in the form of a MIME type, like "file:text/nwb" for the Network Workbench network format. in_data for Java objects is the class name of the object, such as prefuse.data.Graph for the prefuse Graph object. For this tutorial we will be using the data type "edu.uci.ics.jung.graph.Graph", which is the full class name for the Graph object in the Jung graph library. You should also check the checkbox for "On the menu", and set the menu path to be "Analysis".

...

Your AttackToleranceAlgorithm file should initially look like this:

Code Block
package org.my.attacktolerance;

...

Code Block

 
import java.util.Dictionary;

import org.cishell.framework.CIShellContext;
import org.cishell.framework.algorithm.Algorithm;
import org.cishell.framework.algorithm.AlgorithmExecutionException;
import org.cishell.framework.data.Data;

public class AttackToleranceAlgorithm implements Algorithm {
private Data\[\] data;
private Dictionary parameters;
private CIShellContext context;

public AttackToleranceAlgorithm(Data\[\] data, Dictionary parameters, CIShellContext context) {
        this.data = data;
        this.parameters = parameters;
        this.context = context;
    }

public Data\[\] execute() throws AlgorithmExecutionException {
    	return null;
    }
}

...

Because we specified the Jung graph as the in_data type, CIShell will arrange for us to be given a Jung graph object when this algorithm is run (if a network in the Data Manager is of some other type, CIShell's conversion service will convert that network to a Jung graph). The input objects are held in the data array that is passed as a constructor to the algorithm. We use the following code to access the Jung graph in this array.

Code Block
	public Data\[\] execute() throws AlgorithmExecutionException {
        Graph graph = (Graph)(data[0].getData());
    	return null;
    }

...

Returning to the AttackToleranceAlgorithm, we should now be able to import the edu.uci.ics.jung.graph.Graph class with no errors. (Eclipse Tip: Write the Java code which requires some import, then press Control-Shift-O, and let Eclipse handle the imports for you. This will only work in OSGi if you have already imported the packages in your manifest.) Be careful. Organizing missing imports will remove them from your code. This could cause some confusion).

Handling input data in CIShell Algorithms

...

To copy the graph object, we use the following code:

Code Block
	Graph attackGraph = (Graph) graph.copy();

Before we begin our analysis, there is one last thing we need to do. We must get the value specified by the user for how many of the highest degree nodes we should remove. These values are stored in the "parameters" Dictionary object. The keys in this dictionary are the "id" values we chose when designing the GUI in the wizard, and the values of this Dictionary are the values the user supplies when presented with the GUI we designed. If you don't recall the id or the type we gave to our parameter, you can look in the /OSGi-INF/metatype/METADATA.XML file. This file is the result of the parameters step in the wizard. (For more information about this file, see OSGi Compendium 'Metatype Service Specification' Section 105.

The following line is used to extract the number of nodes we should delete, "numNodesToDelete" from the Dictionary.

Code Block
	int numNodesToDelete = ((Integer) parameters.get("numNodesToDelete")).intValue();


Now we have obtain everything we need to do the attack tolerance analysis. Since the specifics of this algorithm are not important to learning about CIShell, I we have provided the core code in the class below:

AttackTolerance:

Code Block
package org.my.attacktolerance;

import java.util.Iterator;

import edu.uci.ics.jung.algorithms.importance.DegreeDistributionRanker;
import edu.uci.ics.jung.algorithms.importance.NodeRanking;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Vertex;

public class AttackTolerance {

/*\*
* Perform an attack tolerance test on the graph.
\*
* @return a Graph with 'numNodesToDelete' of its highest degree nodes deleted.
* Edges associated with deleted nodes will be removed as well.
\*/
	public static Graph testAttackTolerance(final Graph graph, int numNodesToDelete) {

		Graph graphToAttack = (Graph) graph.copy();

		DegreeDistributionRanker rankByDegree = new  DegreeDistributionRanker(graphToAttack);
		rankByDegree.evaluate();

		Iterator nodesByDegree = rankByDegree.getRankings().iterator();
		for (int numNodesDeleted = 0;
	      numNodesDeleted < numNodesToDelete && nodesByDegree.hasNext();
		  numNodesDeleted++) {

			Vertex v = ((NodeRanking) nodesByDegree.next()).vertex;

			graphToAttack.removeVertex(v);
		}

		return graphToAttack;
	}
}

Here we've created a third class, AttackTolerance, which will be used by AttackToleranceAlgorithm. It is a good practice to separate the CIShell Algorithm code from the domain-specific analysis code for reasons of modularity. You can have as many additional classes or packages as you like in a plugin, and can also share classes between plugins by importing and exporting packages through a plugin's manifest, as we saw in previous sections with the Jung library.

...

Below is the updated code for AttackToleranceAlgorithm, which now utilizes the AttackTolerance class, and returns the modified graph as Data:

Code Block
package org.my.attacktolerance;

...

Code Block

 
import java.util.Dictionary;

import org.cishell.framework.CIShellContext;
import org.cishell.framework.algorithm.Algorithm;
import org.cishell.framework.data.BasicData;
import org.cishell.framework.data.Data;
import org.cishell.framework.data.DataProperty;

import edu.uci.ics.jung.graph.Graph;

public class AttackToleranceAlgorithm implements Algorithm {

	private Data\[\] data;
	private int numNodesToDelete;

	public AttackToleranceAlgorithm(Data\[\] data, Dictionary parameters, CIShellContext context) {
		this.data = data;
		this.numNodesToDelete = ((Integer) parameters.get("numNodesToDelete")).intValue();
	}

	public Data\[\] execute() {
		Graph inputGraph = (Graph) (data[0].getData());

		Graph outputGraph = AttackTolerance.testAttackTolerance(inputGraph, numNodesToDelete);

		Data outputData = prepareOutputData(outputGraph);

		return new Data\[\] { outputData };
	}

	private Data prepareOutputData(Graph outputGraph) {
		Data outputData = new BasicData(outputGraph, Graph.class.getName());

		Dictionary metadata = outputData.getMetadata();
		metadata.put(DataProperty.PARENT, data[0]);
		metadata.put(DataProperty.TYPE, DataProperty.NETWORK_TYPE);
		metadata.put(DataProperty.LABEL, "High Degree Node Deletion (Attack Tolerance)");

		return outputData;
	}
}

The important part of this code is the prepareOutputData() method, specifically the metadata of the Data object. When returning data to CIShell, we must set certain metadata on the data to tell CIShell various things about the data, such as its type (NETWORK_TYPE, in the case, which is primarily used by the Data Manager to give the data the appropriate icon for a network), label, which is used as the name of the data in the Data Manager, and parent, which is used to specify which data item this file should be a child of in the data manager. All of the possible Data properties are documented in the CIShell Specification.

...

To do this, we must modify the AttackToleranceAlgorithmFactory. Our AlgorithmFactory must implement the ParameterMutator interface, which includes only a single method: public ObjectClassDefinition mutateParameters(Data[] data,
ObjectClassDefinition parameters).
The Data[] array is the data which the algorithm will be operating on, and the parameters object is an object representation of the pre-mutated GUI (the one we defined in our wizard, and which is defined in file form in METADATA.XML). In order to fully understand the parameters object, you will need to read OSGi Compendium 'Metatype Service Specification' Section 105, and The CIShell Specification, 'MetaTypeProvider Extensions', Section 2.5.2. For now it should suffice to provide the relevant source code, and briefly describe its operation.

Source code for AttackToleranceAlgorithmFactory as a ParameterMutator:

Code Block
package org.my.attacktolerance;

...

Code Block

 
import java.util.Dictionary;

import org.cishell.framework.CIShellContext;
import org.cishell.framework.algorithm.Algorithm;
import org.cishell.framework.algorithm.AlgorithmFactory;
import org.cishell.framework.algorithm.ParameterMutator;
import org.cishell.framework.data.Data;
import org.cishell.reference.service.metatype.BasicAttributeDefinition;
import org.cishell.reference.service.metatype.BasicObjectClassDefinition;
import org.osgi.service.metatype.AttributeDefinition;
import org.osgi.service.metatype.ObjectClassDefinition;

import edu.uci.ics.jung.graph.Graph;

public class AttackToleranceAlgorithmFactory implements AlgorithmFactory, ParameterMutator {
	public Algorithm createAlgorithm(Data\[\] data, Dictionary parameters, CIShellContext context) {
        return new AttackToleranceAlgorithm(data, parameters, context);
    }

	public static final int DELETE_ONE_IN_N_NODES_BY_DEFAULT = 10;

	/\*
	* set the default value for 'Number of Highest Degree Nodes to Delete'
	* to 1/Nth of the total number of nodes in the graph.
	\*/
	public ObjectClassDefinition mutateParameters(Data\[\] data, ObjectClassDefinition parameters) {

		//calculate how many nodes to delete by default

		Graph inputGraph = (Graph) data[0].getData();
		int defaultNumNodesToDelete = calculateDefaultNumNodesToDelete(inputGraph);

		BasicObjectClassDefinition newParameters = new BasicObjectClassDefinition(
		parameters.getID(),
		parameters.getName(),
		parameters.getDescription(), null);

	
		//for each attribute definition in the original attribute parameters
		AttributeDefinition\[\] paramAttributes =
		parameters.getAttributeDefinitions(ObjectClassDefinition.ALL);
		for (int ii = 0; ii < paramAttributes.length; ii++) {
		AttributeDefinition paramAttribute = paramAttributes[ii];
		//if it is the numNodesToDelete attribute...
		if (paramAttribute.getID().equals("numNodesToDelete")) {
				/*
				 * add that attribute to our new set of attributes,
				 *  but with the new default value we calculated
				 */
				AttributeDefinition modifiedAttribute = new BasicAttributeDefinition(
						paramAttribute.getID(), paramAttribute.getName(),
						paramAttribute.getDescription(), paramAttribute.getType(),
						String.valueOf(defaultNumNodesToDelete));
				newParameters.addAttributeDefinition(
						ObjectClassDefinition.REQUIRED, modifiedAttribute);

			} else {
				//add any other attribute to our new set of attributes as-is.
				newParameters.addAttributeDefinition(
						ObjectClassDefinition.REQUIRED, paramAttributes[ii]);
			}
	}

	//return our new attribute parameters
	return newParameters;
}

private int calculateDefaultNumNodesToDelete(Graph graph) {
		int numNodesInGraph = graph.numVertices();
		int recommendedToDelete = Math.max(numNodesInGraph / DELETE_ONE_IN_N_NODES_BY_DEFAULT, 1);
		return recommendedToDelete;
	}
}

...