Anteriormente se ha mencionado la posibilidad de crear aplicaciones en I2P utilizando librerías como Streaming Library y BOB, desde FreeNet también es posible crear aplicaciones (en este caso concreto, Plugins) que permitan ejecutar diversas operaciones utilizando FreeNet como pasarela, estos Plugins como se ha enseñado anteriormente, pueden contener código que permita interactuar directamente con el nodo de FreeNet o interactuar con otros nodos disponibles en la darknet. En caso de FreeNet, el uso de las librerias disponibles es bastante similar a I2P, ya que cuenta con una API de Java bastante completa, aunque desafortunadamente con muy poca documentación disponible, por lo tanto es necesario investigar por cuenta propia, estudiando el código disponible en algunos de los plugins existentes, tales como Thaw, FreeMail o jSite. En este punto, la intensión de esta publicación, es permitirle al lector un punto de inicio en el desarrollo de plugins en FreeNet sin profundizar demasiado, (esto se hará en proximas publicaciones de este blog).


Antes de comenzar, es necesario cumplir con los siguientes requisitos:

  1. Instalación de Freenet (se da por ello si el lector ha llegado hasta esta publicación)
  2. Instalación de Java 1.6 o superior: http://www.oracle.com/technetwork/java/javase/downloads/jdk6-jsp-136632.html
  3. Instalación de Apache ANT: http://ant.apache.org/bindownload.cgi
  4. Instalación de GIT: http://git-scm.com/download
  5. Instalación de eclipse (opcional): http://www.eclipse.org/downloads/

Los requerimientos anteriores son solamente utiles para “clonar” y compilar el proyecto de ejemplo distribuido desde Freenet para el desarrollo de plugins, para comenzar a estudiar el código fuente y los elementos principales de la API de Freenet, se comenzará por importar el proyecto “HelloWorldPlugin” del repositorio GIT, para esto se ejecutan los siguientes pasos:

  1. Clonar el proyecto GIT a la máquina local.
    >git clone git://github.com/freenet/plugin-HelloWorld-staging.git
  2. Una vez clonado el proyecto, se procede a abrir Eclipse e importar el proyecto AT existente tal como se enseña en las siguientes imagenes:
  3. Una vez el proyecto se ha importado a Eclipse, es necesario establecer la ubicación de las librerías necesarias, que en este caso son freenet.jar y freenet-ext.jar, de este modo el proyecto compilará correctamente. Estas librerias se encuentran incluidas en la instalación estándar de Freenet justo en el directorio raíz.
  4. Una vez que el proyecto compile adecuadamente, es necesario importar un fichero JAR con las clases compiladas, para hacer esto desde Eclipse,  es tan sencillo como seleccionar el proyecto, click derecho → Export … → Java → JAR file  ahora como se ha hecho en las entradas anteriores, este plugin se carga directamente desde FProxy o desde TCMI tal y como se ha indicado en entradas anteriores.

Esta primera aproximación del uso de la API de Freenet, da paso a explicar los detalles del proyecto anterior y las clases que se encuentran involucradas, además de entender como hacer plugins “ricos” en funcionalidades gracias a las clases e interfaces disponibles en la API.
Para empezar es necesario entender que todos los Plugins en Freenet deben implementar la interfaz freenet.pluginmanager.FredPlugin esta interfaz es de carácter obligatorio para todos los plugins que se desarrollan en FreeNet, de esta forma cada nodo tiene una “signature” valida para cada uno de los plugins que podrían instalarse en una instancia de Freenet. Si un fichero JAR no contiene una clase que implemente dicha interfaz y esta a su vez no es definida en el fichero META-INF de dicho JAR, el plugin simplemente no podrá ser cargado. De hecho, este uno de los principales “problemas” que podrá encontrar un desarrollador que nunca ha creado un plugin en Freenet, ya que el hecho de que una clase implemente la interfaz adecuada, no es suficiente para que el nodo identifique a dicha clase como un “plugin” valido. El contenido del siguiente fichero META-INF puede ser un ejemplo útil

Manifest-Version: 1.0
Main-Class: AdastraPlugin.PluginHello
Plugin-Main-Class: AdastraPlugin.PluginHello

Como puede verse el fichero META-INF contiene una linea con:

Plugin-Main-Class: AdastraPlugin.PluginHello

La cual es responsable de “decirle” al nodo de Freenet cual es la clase que implementa la interfaz FredPlugin. Este es solo el inicio, ya que la API de freenet es bastante extensa y es posible realizar muchísimas actividades relacionadas con la administración del plugin e interacción con la darknet.
Las principales interfaces y clases que es necesario tener presente son las siguientes:

freenet.pluginmanager.FredPlugin

Como se ha indicado anteriormente, es la interfaz principal para todos los plugins en FreeNet, define dos métodos:
plugin void runPlugin(PluginRespirator pr);
plugin void terminate();
El primer método define la lógica que debe ejecutarse cuando el plugin es invocado desde la instancia de Freenet. El método terminate simplemente permite definir la rutina de liberación de recursos una vez que el plugin ha finalizado su ejecución. En el ejemplo indicado anteriormente, se ha declarado esta interfaz y sus correspondientes metodos, si se aprecia determinadamente, solamente declará en el método runPlugin  un ciclo indefinido que escribe en los logs del nodo un mensaje de texto plano, es un ejemplo muy básico que permite entender el uso más basico de esta interfaz. Por otro lado, tambien es importante aclarar que TODOS los eventos y logs que registra un nodo de Freenet son almacenados en el fichero <FREENET_INSTALL_DIR>/wrapper.log

freenet.pluginmanager.FredPluginHTTP

Se trata de una interfaz que implementa los métodos necesarios para procesar peticiones HTTP estándar desde los plugins de Freenet, sin embargo se trata de una interfaz poco flexible por lo que se aconseja utilizar Toadless en su lugar, los cuales permiten interactuar con la interfaz web de Freenet (FProxy) y declarar menús y otras opciones adicionales. Por otro lado, existe otra interfaz adicionar que debe implementarse con el fin de ejecutar el método “runPlugin” después de registrar la interfaz. Si no se hace de este modo, lo más probable es que existan numeras excepciones de NullPointerException. Los métodos implementados en esta interfaz son: handleHTTPGet y handleHTTPPost

freenet.pluginmanager.FredPluginThreadless

Se trata de una interfaz de apoyo de la interfaz FreedPluginHTTP, es su única finalidad.

freenet.pluginmanager.Toadlet

Se trata de una clase que es el “reemplazo” de la clase javax.servlet.Servlet de la especificacion de Servlet API, pero enfocada completamente al entorno de Freenet, dándole una rica funcionalidad a cada plugin que se intente implementar utilizando esta clase y otros elementos auxiliares.

freenet.node.Node

Se trata de una clase que permite acceder a información del nodo local de Freenet y realizar labores de administración tales como adicionar o consultar Peers, modificar o consultar propiedades del nodo, administrar el DataStore de la instancia local, entre muchas otras ventajas, es probablemente, la clase más completa que hay en la API de FreeNet y es una fuente de información inestimable para realizar diferentes tipos de consultas en el interior de la Darknet.

freenet.client.HighLevelSimpleClient

Como su nombre lo indica, es una interfaz de alto nivel para que un cliente pueda interactuar con Freenet. Esta clase permite realizar consultas sobre Freenet y obtener resultados validos que posteriormente pueden ser manipulados.

freenet.client.FreenetURI

Representa simplemente una clave Freenet. Para un cliente, se trata de un elemento importante ya que le permite definir un criterio de búsqueda valido para enviar a Freenet y obtener información sobre el documento almacenado en Freenet con la clave que representa este objeto.

freenet.client.FetchResult

Esta clase contiene toda la información sobre el retorno de una consulta sobre una clave de Freenet, contiene información sobre los metadatos del cliente que ha realizado dicha consulta, así como un array de bytes con el contenido completo del documento consultado en Freenet. (En el caso de que exista tal fichero y que no se haya lanzado ninguna excepción  intentado realizar la consulta).

Ahora bien, estas son las clases principales y de uso más común en Freenet, sin embargo no son las únicas, de hecho, como se ha mencionado anteriormente, la API de Freenet es muy extensa y cuenta con múltiples funcionalidades que desafortunadamente no se encuentran correctamente documentadas, por este motivo es necesario revisar directamente el código fuente de plugins disponibles para Freenet, tanto los oficiales como los no oficiales. Al decir que la documentación que existe sobre el desarrollo de plugins es bastante escasa, me refiero a documentación escrita en ingles e incluso en alemán, ya que buscar documentación en castellano sobre desarrollo de plugins en Freenet es como buscar agua en el Sahara, es practica mente inexistente.
Se han explicado estas clases, pero aun no se han puesto ejemplos sobre su uso, en el papel todo suena muy bien, pero si no viene acompañado conocimiento practico que le acompañe, probablemente resultará inútil. El siguiente plugin enseña el uso de algunas de las clases indicadas anteriormente para obtener información sobre el Nodo de Freenet en donde se ejecuta y además, consultar el contenido de un fichero de texto almacenado en freenet (clave CHK).


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.MalformedURLException;

import freenet.client.FetchException;
import freenet.client.FetchResult;
import freenet.client.HighLevelSimpleClient;
import freenet.keys.FreenetURI;
import freenet.node.Node;
import freenet.node.useralerts.UserAlert;
import freenet.pluginmanager.FredPlugin;
import freenet.pluginmanager.PluginRespirator;

public class PluginHello implements FredPlugin {

/** The plugin respirator. */
private PluginRespirator pluginRespirator;

@Override
public void runPlugin(PluginRespirator respirator) {
this.pluginRespirator = respirator;

/* create freenet interface. */
Node node = pluginRespirator.getNode();
System.out.println("StartUp Time of this node: "+node.startupTime);
System.out.println("Node Name: "+node.getMyName());
System.out.println("Downstream Max Bit Rate "+node.ipDetector.getBandwidthIndicator().getDownstreamMaxBitRate());
System.out.println("Downstream Max Bit Rate "+node.ipDetector.getBandwidthIndicator().getUpstramMaxBitRate());
System.out.println("Alerts in this node.");
UserAlert[] userAlerts = node.clientCore.alerts.getAlerts();
for(UserAlert userAlert : userAlerts) {
System.out.println("Text: "+userAlert.getText());
System.out.println("Priority: "+userAlert.getPriorityClass());
}

System.out.println("Starting Location Manager...");
node.lm.start();
System.out.println("Location of this Node: "+node.lm.getLocation());
System.out.print("Known Locations...");
for(Object location : node.lm.getKnownLocations(node.swapIdentifier)) {
System.out.println("Location: "+location);
}

HighLevelSimpleClient simpleClient = pluginRespirator.getHLSimpleClient();
FreenetURI freeNetURI;
try {
freeNetURI = new FreenetURI("CHK@6xW-CwEp5uSpsb7u7pqlQJpfDORfWo5hLlV8pAc6e38,cKBwXQ5ug0sIgPeZUA0O9jv7r8l6NvY5ut0Et4S-zRw,AAIC--8/file.txt");
FetchResult result = simpleClient.fetch(freeNetURI);
System.out.println("Result Returned: "+result.asBucket().getName());
ByteArrayInputStream bais = new ByteArrayInputStream(result.asByteArray());
int ch;
while((ch = bais.read()) != -1)
{
System.out.print((char)ch);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FetchException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public void terminate() {
System.out.println("Terminating the Plugin... Bye my friend...");
}

}

En la clase anterior, se pueden apreciar algunos métodos de las clases mencionadas anteriormente, para ver los resultados (salida en la consola) puede verse el fichero <FREENET_INSTALL_DIR>/wrapper.log el contenido de dicho fichero deberá tener algo como esto:

INFO   | jvm 1    | 2012/02/01 00:51:02 | Downloading plugin /home/adastra/Escritorio/PluginHello.jar
INFO   | jvm 1    | 2012/02/01 00:51:02 | StartUp Time of this node: 1328045714025
INFO   | jvm 1    | 2012/02/01 00:51:02 | Node Name: ADASTRA
INFO   | jvm 1    | 2012/02/01 00:51:02 | Downstream Max Bit Rate 9999000
INFO   | jvm 1    | 2012/02/01 00:51:02 | Downstream Max Bit Rate 796000
INFO   | jvm 1    | 2012/02/01 00:51:02 | Alerts in this node.
INFO   | jvm 1    | 2012/02/01 00:51:02 | Starting Location Manager…
INFO   | jvm 1    | 2012/02/01 00:51:02 | Location of this Node: 0.4353910916952617
INFO   | jvm 1    | 2012/02/01 00:51:02 | Known Locations…Location: [Ljava.lang.Double;@20122306
INFO   | jvm 1    | 2012/02/01 00:51:02 | Location: [Ljava.lang.Long;@6b96f5d
INFO   | jvm 1    | 2012/02/01 00:51:02 | Result Returned: Encrypted:temp-cfe8e34fdbefa1f8
INFO   | jvm 1    | 2012/02/01 00:51:02 | text file created to test the TCMI Client…

RECORDATORIO:
Todas las clases que implementen plugins en Freenet deben estar empaquetadas en un fichero JAR y además de esto, se debe indicar de forma explicita el nombre de la clase (junto con el paquete al que corresponda) en el fichero META-INF del JAR para que Freenet pueda encontrarlo y cargarlo en el Nodo local.

Esto ha sido una introducción sobre el desarrollo de plugins en Freenet, sin embargo como se ha dicho anteriormente, se trata de una API bastante completa con muchisimas clases que permiten hacer «casi» cualquier cosa dentro de Freenet, por lo tanto se recomienda estudiar la documentación de la API (JavaDoc) desde aquí:

http://freenet.github.com/fred-staging/api/index.html?overview-tree.html