Lotus Notes / Domino Sonstiges > Java und .NET mit Notes/Domino

neues Projekt: Poo Monkey

(1/2) > >>

Marinero Atlántico:
Hi,

es soll nun um einen Webbot für Atnotes gehen.
Für irgendetwas muß die DSL Verbindung ja gut sein.
Das Projekt soll das folgende abbilden:
Ein Webbot namens Poo Monkey soll mit den Login-Daten des Users, der ihn laufen lässt, die neuen Postings im Offtopic-Forum von Atnotes in bestimmten Zeitabständen scannen.
Falls der Webbot in einem Posting den Text "Poo Monkey, how do you do?" (mit Variationen) findet, postet Poo Monkey zurück: "Poo Monkey says: I am fine".

Da ich nicht vermute, dass besonders viele Leute mitmachen, glaube ich nicht dass das Projekt "Poo Monkey" Atnotes lahmlegen wird.

Technisch geht es um die folgenden Themengebiete:
- viel HTTP mit der wie ich finde ziemlich guten HTTPClient-jar von jakarta commons.
- httml-parsing
- scheduling in Java (scheduling ist so was wie zeitgesteuerte Agenten).
- ein bischen OO
- vermutlich ein bischen RegEx
- v.a. auch so allgemeine Dinge, die ich für sinnvoll halte, wie z.B. benutzen von Property-Files, Log4U sowie irgendwann sicher UnitTesting. Das kann dann auch von Leuten benutzt werden, die für Notes Java-Agenten schreiben.
- natürlich Eclipse

Mir ist es zumindest jetzt schon mal in 60 Minuten gelungen, mit dem Java Code authentifiziert als Marinero Atlantico gegen Atnotes ein HTTP-GET abzusetzen und das zurückkommende HTML in eine Datei zu schreiben.

Gruß Axel

Marinero Atlántico:
Hier ist schon mal ein bischen Source-Code.
Das Projekt hat 2 Klassen und 2 Konfigurationsdateien:

Die überhaupt nicht objektorientierte Klasse "FirstTest" baut sich zunächst einen HTTP Get Request zusammen.
Dieser enthält:
- die URL des Requests.
- den HTTP-Parameter action=recent (damit geht er gegen die: die neuesten 10 Posting Seite von atnotes.
- das Cookie, das atnotes zum einloggen benötigt (YaBBSE140usernamev14). Ihr findet diesen Wert in eurem Browser. Ganz praktisch ist in Mozilla Web Developer-plugin: Information: View Cookie Information.

Httpclient sendet den Request.
Atnotes erhält diesen Request und schickt die Seite der 10 neuesten Postings das von httpClient gesendete Cookie und ein neues php-session Cookie zurück.
Die Werte der Cookies sowie ein http Statuscheck werden per log4j an der Konsole ausgegeben.
Der HttpStream der zurückgelieferten Seite wird in eine Datei gespeichert (namens atnotes.de in <projekt-root>/genFiles.
Im Quellcode steht eindeutig das Hallo, Marinero Atlántico. Also hat die Authentifizierung mit dem Cookie geklappt. Wenn jemand anders das ausprobiert, sieht er natürlich nicht Hallo Marinero Atlántico sondern seinen eigenen Namen.


Klasse 1:

--- Code: ---package de.aja.commons.dabbling;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.log4j.Logger;

import de.aja.dabbling.commons.utils.DabblingHelpers;

/*
 * Created on 05.06.2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */

/**
 * @author Axel
 *
 * simply dabbling with jakarta httpClient
 */
public class FirstTest {

final static Logger logger = Logger
.getLogger(de.aja.commons.dabbling.FirstTest.class);

/** requested base url without Query String */
private String urlToRequest;

/** an instance of apache jarkarta HttpClient. */
private HttpClient client;

protected void setUrlToRequest(String urlToRequest) {
this.urlToRequest = urlToRequest;
}

FirstTest() {
client = new HttpClient();

}

/**
* here's where the action takes place.
*
*/
void doGetRequest() {
logger.info("doGetRequest start.");
DabblingHelpers.setStartTime();
HttpMethod method = new GetMethod(urlToRequest);
// name value pair für HTTP-GET

NameValuePair nvp1 = new NameValuePair("action", "recent");
method.setQueryString(new NameValuePair[] { nvp1 });

// user spezifischer Wert aus properties File lesen
Properties props = new Properties();
InputStream iStr = getClass().getClassLoader().getResourceAsStream(
"config.properties");
try {
props.load(iStr);
} catch (IOException e1) {
logger
.error(
"Properties File \"config.properties\" konnte nicht geladen werden",
e1);
System.exit(0);
} catch (NullPointerException npe) {
logger
.error(
"Properties File \"config.properties\" konnte nicht gefunden werden.",
npe);
System.exit(0);

}

// folgende für Cookie setzen:
Cookie cookie = new Cookie("217.160.137.156", "YaBBSE140usernamev14",
props.getProperty("user.cookie.YaBBSE140usernamev14"), "/", -1,
false);
HttpState initialState = new HttpState();
initialState.addCookie(cookie);
initialState.setCookiePolicy(CookiePolicy.COMPATIBILITY);
client.setState(initialState);

try {

int statusCode = client.executeMethod(method);
logger.info("Query String>>>=" + method.getQueryString());
logger
.info("Status Text>>>"
+ HttpStatus.getStatusText(statusCode));

// cookie stuff
logger.info("Start Cookies------------------>");
Cookie[] cookies = client.getState().getCookies();
for (int i = 0; i < cookies.length; i++) {
logger.info("A Cookie");
logger.info("Name=" + cookies[i].getName());
logger.info("Value=" + cookies[i].getValue());
logger.info("Domain=" + cookies[i].getDomain());
}
logger.info("End Cookie<------------------");
// System.out.println(method.getResponseBodyAsString());
InputStream is = new BufferedInputStream(method
.getResponseBodyAsStream());
// byte [] res = method.getResponseBody();

BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("genFiles\\atnotes.de"));
// fos.write(is);
byte[] buf = new byte[4096];
int len;
while ((len = is.read(buf)) > 0) {
bos.write(buf, 0, len);
}
bos.close();
is.close();
method.releaseConnection();
method = null;

} catch (IOException e) {
logger.error(e);

} catch (Throwable t) {
logger.error(t);
}

finally {
if (method != null) {
logger
.debug("connection of object \"method\" closed in finally!");
method.releaseConnection();

}
}
logger.info("doGetRequest end. This method took more or less:"
+ DabblingHelpers.getTimeDifInSeconds() + " seconds to run.");

}

public static void main(String[] args) {
FirstTest fTest = new FirstTest();
fTest.setUrlToRequest("http://www.atnotes.de/index.php");
fTest.doGetRequest();
}
}

--- Ende Code ---

Globale Helper-Klasse. Zur Zeit nur zum Zeitstoppen:

--- Code: ---/*
 * Created on 05.06.2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package de.aja.dabbling.commons.utils;

/**
 * @author Axel
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class DabblingHelpers {
private static long startTime;



public static void setStartTime() {
startTime = System.currentTimeMillis();
}

public static double getTimeDifInSeconds() {
if (startTime == 0) {
throw new RuntimeException("startTime not set");
}
return System.currentTimeMillis() / 1000;
}

}

--- Ende Code ---

1 Konfigurationsdatei für log4j.xml

--- Code: ---<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-4r [%t.%L] %-5p %c %x.%M - %m%n"/>
</layout>
</appender>
<appender name="pooMonkey" class="org.apache.log4j.FileAppender">
<param name="File" value="C:\\funLogs\\PooMonkey.log"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-4r [%t.%L] %-5p %c %x.%M - %m%n"/>
</layout>
</appender>

<logger name="de">
<level value="info"/>
<appender-ref ref="STDOUT"/>
</logger>



</log4j:configuration>

--- Ende Code ---

1 Properties Datei config.properties

--- Code: ---user.cookie.YaBBSE140usernamev14=a%3A3%3Avonmirgeändert

--- Ende Code ---

Marinero Atlántico:
Jetzt noch ein paar Zusatzdinge, die man benötigt. Inklusive ein paar nicht so schlechter Ideen bzgl. der Strukturierung von Projekten in Eclipse.

1. Folgende jars werden benötigt:
von Jakarta.commons
http://jakarta.apache.org/commons/index.html und können dort runtergeladen werden:
-httpclient (ich verwende 3.0 Release Candidate 2)
-codecs (ich verwende eine vermutlich veraltete Version, die auf meiner Festplatte rumlag--> kann zu Problemen führen)
- commons-logging (hab auch veraltete Version, aber ihr könnt euch neue runterladen).
Die Projekte haben einen Downloadlink und im Download ist auch die recht umfangreiche Dokumentation mit Beispielen dabei.

- Log4j. Ich benutze hier auch noch die ältere 1.2.8. Hier gibts noch was neueres: http://logging.apache.org/log4j/docs/

Externe Jars können in Eclipse imho am saubersten so eingebunden werden.
Rechte Maustaste auf lib Verzeichnis des Projekts (sollte standardmässig da sein). Dann Import im PopupMenü hinter der wie gesagt rechten Maustaste wählen und der folgende Dialog ist selbsterklärend.
Das erstmal so machen.

2. Die beiden Config-Files müssen in einem Classpath Root liegen und die Namen besitzen wie oben angegeben. Ich find es am besten, mehrere Classpath Roots in einem Projekt zu haben. Mit Eclipse kann man das problemlos hinkriegen.

Die restlichen Einstellungen befinden sich alle im Projekt-Properties Dialog. Rechte Maustaste auf den Projekt Stammordner im linken Navigator und dort den letzten Eintrag Properties wählen.
Alle Einstellungen im Punkt Java Build Path:
Erstmal Libraries aus Lib in den Projekt-Buildpath eintragen. Also: 3. Reiter "Libraries" und hier mit Button "Add Jars". Sofern wie bisher beschrieben gemacht, selbsterklärend.

Dann auf Tab Source wechseln.
Hier können nun neben dem standardmässigen src 3 Folder angelegt werden.
Ich verwende: config, genFiles und test.
Unter src kommen die .java Dateien. In config alle Konfigurationsdateien (log4j.xml, config.properties, etc.), in test die unitTests (bisher gibts da keinen) und in genFiles die aus der Anwendung heraus generierten Dateien.
Das sieht dann am Ende so aus:
Bei größeren Projekten mit mehreren Config-Files, erhöht sich dadurch die Übersicht immens. Die kompilierten Dateien landen alle in classes. 
 

Marinero Atlántico:
Das Projekt ist bisher überhaupt nicht OO. Aber das kann zu einem späteren Zeitpunkt noch refaktoriert werden. Ich hab nicht so viel Erfahrung mit httpClient und dann ist es sinnvoll erstmal in einer Art proof of concept alles nach Altvätersitte in eine Methode zu packen. Wenn ich mich ein bischen sicherer fühle, wie diese Library aufgebaut ist, mach ich das OO.

Das Projekt benutzt konsequent log4j und nicht irgendwelche babarischen System.out.println Statements. Das wird zwar bisher alles auf der Konsole wie System.out.print ausgegeben, kann aber über log4j.xml jederzeit auf Filelogging umgestellt werden.
Weiterhin gibt es ein externes .properties File, wo Konfigurationsdinge hin ausgelagert werden können. Ähnlich wie Konfigurationsdokumente in Notes. Die Klasse Properties und v.a. wie die Datei über getClass().getClasspath().getRessourceAsStream(String fileName) eingebunden wird ist auch nicht unbedingt offensichtlich aber gut zu wissen.

Gruß Axel

TMC:
Axel,

nur als Anregung:
Vielleicht möchtest Du auch noch berücksichtigen, RSS auszuwerten bzw. aufzubereiten.

Siehe: Getting the Most Out of SMF - XML, RSS and RSS2 feeds

Ist vermutlich aber sehr viel einfacher umzusetzen, als das was Du hier als Workshop / Projekt machst (als Bot mit User-Login-Daten zugreifen).

Navigation

[0] Themen-Index

[#] Nächste Seite

Zur normalen Ansicht wechseln