programmieren...

programmieren...

 

08 Sep

sarah kirchner

08.09.2011 13:55

der anfang

am anfang weiß ich nicht, was soll ich schreiben. aber es ist ein versuch. und da die webseite noch überall eine baustelle ist, kommt hier erst einmal ein kleiner artikel, der auch nicht mit programmieren zu tun hat.

zuerst einmal ist das eine baustelle noch.

 

15 Sep

sarah kirchner

15.09.2011 17:12

Logging

Dieser Blog Eintrag beschreibt das Aussetzen und konfigurieren des Loggings.

Immer wenn ich ein neues Java Projekt beginne, stehe ich vor dem Problem, wie logge ich etwas, welche Strategie soll ich verwenden, usw. Da kommt ein Konfigurationsmarathon auf einen zu. Ich merke mir nicht immer alle Einstellungen und Schritt und so beginnt das Suchen im Internet.

Java besitzt seit 1.4 ein eigenes Logging Framework (http://download.oracle.com/javase/6/docs/technotes/guides/logging/overview.html). Trotzdem will ich dieses nicht verwenden, da mir die API nicht gefällt. Ich verwende gerne das Commons Logging von Apache (http://commons.apache.org/logging/), weil es von der API Seite sehr einfach ist (die Log Levels sind für mich intuitiver) und was ein großer Vorteil ist, die tatsächliche Ausgabe wird an ein darunterliegendes Logging Framework delegiert.

Commons Logging verwende ich im Java Code und Log4J wird für die tatsächliche Ausgabe verwendet.

An Log4J finde ich gut, das die Ausgabe der Log Meldung sehr flexible verwendet wird mit Appender, Layout und Ausgabeformat. Bei der Entwicklung kann sehr einfach auf der Console die Meldung geschrieben werden. Im Produktionseinsatz lassen sich verschiede Strategien wie Daily Rolling Files oder Netzwerk basiertes Logging und andere Strategien konfigurieren.

Konfiguration

Dem Commons Logging muss mitgeteilt werden, welches Logging Framework verwendet werden soll. Eingebaut ist die Unterstützung von Java Logging und Log4J.

In der Porperties Datei commons-logging.properties wird die Klasse für das Loggen eingestellt.

commons-logging.properties

#
# Configuration of the Commons Logging
#
# Using Log4J
#
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger

 

log4j.properties

#
# Configuration of the Log4J
#
log4j.rootLogger=INFO, console

#
# Appender console
#
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss:SSSS} %6p [%c{1}]%n> %m%n

#
# Categories
#
log4j.category.kirchnerei=DEBUG

 

Die beiden Properties Dateien müssen im ClassPath der JVM liegen. In dem Beispiel Projekt habe ich mit Maven die beiden Dateien in das Verzeichnis „src/main/resources“ abgelegt. Da die Resourcen Dateien in die Jar (oder War) Datei kopiert werden, sind diese in dem ClassPath der JVM enthalten.

Sicher ist nicht alles gesagt worden in diesem Eintrag was über Logging und das Konfigurieren möglich wäre, aber es ist ein Anfange, womit man schnell zum Programmieren übergehen kann.

Links *

* Es gibt das Plugin HTTPS Everywhere (https://www.eff.org/https-everywhere) für Firefox, das immer versucht bei bestimmte Domains, die HTTPS Variante anzusprechen, daher sind viele angegeben Links, die HTTPS Varianten.

Download

logging.zip (ca 4618 byte) Eclipse Projekt mit Maven.

Das Eclipse Projekt ist wie es ist, es gibt keine Gewähr auf Richtigkeit.

 

 

28 Sep

sarah kirchner

28.09.2011 12:25

Mit Maven Site generieren

Damit die Migration von Maven 2 auf Maven 3 einfach von statten geht, haben die Maven Leute darauf geachtet, das in den Konfigurationen für die Maven Plugins sind wenig bis gar nichts ändert.

Eine größere Änderung ist aber notwendig, wenn eine Site erstellt werden soll. Ich musste ein Umstellung eines größeren Projekts mich auf die Suche machen, warum keine Site mehr erstellt wird.

Dazu habe ich einen schönen Blogeintrag gefunden: http://www.wakaleo.com/blog/292-site-generation-in-maven-3. Drin ist schön geschrieben, was an der pom.xml geändert werden muss.

In Maven 2 wurde das Site erstellen mit dem Knoten reporting konfigueriert. Maven 3 ignoriert diesen Knoten.

<reporting>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>javancss-maven-plugin</artifactId>
            <version>2.0</version>
        </plugin>
        <!-- weiter plugins -->
    </plugins>
</reporting>

Maven 3 verwendet ein eigenes Plugin, in dem dann die anderen Plugins aufgeführt werden, die abgearbeitet werden sollen.

<build>
    ...
    <!-- Maven 3: Side Generator -->
        <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.0</version>
        <configuration>
            <reportPlugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>javancss-maven-plugin</artifactId>
                    <version>2.0</version>
                </plugin>
                <!-- alle weiteren Plugins für Site -->
                ...
            </reportPlugin>
        </configuration>
    </plugin>
    ...
</build>    

Anschließend sollte das erstellen einer Site mit Maven wieder funktionieren

$ mvn site

Zum Schluß: Ich habe diesen Artikel geschrieben, weil ich für mich noch einmal zusammen fassen wollte. Der Blogeintrag der neuseeländischen Firma beschreibt es genauso (oder sogar besser).

 

26 Feb

sarah kirchner

26.02.2012 17:52

Ein Url (Path) kann Informationen enthalten

Einleitung

So nun habe ich eine neue Aufgabe. Für eine Webseite, die mit Tomcat läuft, sollen die Urls Information über den Inhalt transportieren (ähnlich wie bei RESTful).

http://localhost/dashboard/user/edit/254.html

Listing 1

Was bewirkt diese Url? Die Domain und der ContextPath werden nicht weiter beachtet (http://localhost/dashboard). Uns interessiert nur der anschließende Teil (Path).

  • "user" ist die Aktion.
  • "edit" ist das Verb (oder auch die Unteraufgabe)
  • "254" die Id bestimmt den zu bearbeitenden Datensatze.

Anderes Beispiel:

http://localhost/dashboard/blog/daily-2.html

Listing 2

  • "blog" ist die Aktion
  • "daily" ist das Blog Thema
  • "2" die 2 Seite

 

Muster

Damit solle Path Muster erkannt werden, kann ein Vorlage zu einem bestimmten Muster erstellt werden. Mit regulare Expression werden dann die Informationen aus der Vorlage bestimmt und ausgelesen.

Für das Listing 1 könnte es so lauten: /{action}/{verb}/{id}.html.

Oder für das Listing 2 so: /{action}/{thema}-{page}.html

In einem paar geschweiferter Klammern {} wird eine Variable definiert. Es ist nun möglich die Werte zu einer Variablen auszulesen, ähnlich wie bei der Schnittstelle HttpServletRequest.

 

RegEx

Mit regulare Expression ließe sich so etwas auslesen. In den geschweiften Klammer wird ein regulare Expression Ausdruck definiert.

// Url
String url = "/menu/edit/254.html";
// Template (die Umwandlung von Template zu Pattern folgt später)
String template = "/{action}/{thema}-{page}.html";
// Pattern
String pattern = "/([a-zA-Z0-9]+?)/([a-zA-Z0-9]+?)/([a-zA-Z0-9]+?).html";

Pattern p = Pattern.compile(pattern);
Mathcer m = p.matcher(url);
if (m.find() && m.start() == 0) {
    // Nur wenn es von Beginn bis zum Ende passt
    String action = m.group(1);
    String thema = m.group(2);
    String page = m.group(3);

    log.debug("Action=" + action + ", Thema=" + thema + ", page=" + page);
}

Listing 3: Es soll in diesem Artikel nur einfach gleiche Pattern in den geschweiften Klammern vorkommen.

 

Umwandlung einer Vorlage in ein Pattern

Damit eine Path Vorlage in ein Pattern für Regulare Expression umgewandelt werden kann, muss zuerst die Vorlage durchgelaufen und die verschiedenen Elemente gekennzeichnet werden.

 

PathIterator

// PathIterator
public class PathIterator {
    private final CharSequence text;
    private int pos = 0;
    public PathIterator(CharSequence text) {
        super();
        this.text = text;
    }

    public boolean hasNext() {
        return text != null && pos < text.length();
    }

    public char next() {
        return text[pos++]);
    }

    @Override
    public String toString() {
        return String.format("[%s] %s", pos, text);
    }
}

Listing 4: PathIterator durchläuft die Zeichenketter.

 

PathPattern

// PathPattern
public class PathPattern {

    private static final String TEXT_PATTERN = "([a-zA-Z0-9]+?)";

    private final String pattern;
    private final Map<String, Integer> groups;
    private final Pattern regex;

    public PathPattern(CharSequence template) {
        super();
        StringBuilder sb = new StringBuilder();
        PathIterator it = new PathIterator(template);
        Map<String, Integer> groupIndex = new HashMap<String, Integer>();
        int index = 0;
        while (it.hasNext()) {
            char ch = it.next();
            if (Character.isWhitespace(ch)) {
                continue;
            }
            if (ch == '{') {
                // lese Variablenname
                String name = readName(it);
                groupIndex.put(name, Integer.valueOf(++index));
                sb.append(TEXT_PATTERN);
                continue;
            }
            // normaler Text
            sb.append(ch);
        }
        this.pattern = sb.toString();
        this.groups = Collections.unmodifiableMap(groupIndex);
        this.regex = Pattern.compile(this.pattern);
    }

    public Pattern getRegex() {
        return regex;
    }

    private String readName(PathIterator it) {
        StringBuilder name = new StringBuilder();
        while (it.hasNext()) {
            char ch = it.next();
            if (ch == '}') {
                // Ende des Name gefunden
            }
        }
        return name.toString();
    }
}

Listing 5: Erstellen eines Pattern aus einer Vorlage

 

PathMatch

// PathMatch
public class PathMatch {

    private CharSequence path = null;
    private Map<String, String> params = null;

    public boolean isMatch() {
        return params != null;
    }

    public boolean contains(String name) {
        return isMatch() && params.containKey(name);
    }
    
    public String get(String name, String def) {
        return contains(name) ? params.get(name) : def;
    }

    protected final void matching(PathPattern p, CharSequence path) {
        Matcher m = p.getRegex().matcher(path);
        if (m.find() && m.start() == 0) {
            // url / path passt mit Pattern überein!
            this.path = path;
            if (params == null) {
                params = new LinkedHashMap<String, String>();
            } else {
                params.clear();
            }
            Map<String, Integer> groups = p.getGroup();
            for (String name: groups.keySet()) {
                int groupPos = groups.get(name);
                String value = m.group(groupPos);
                params.put(name, value);
            }
        } else {
            // stimmt nicht überein!
            params = null;
            this.path = null;
        }
    }
}

Listing 6: PathMatch für die Überprüfung aus und nimmt im Erfolgsfall die Information (Parameter) auf.

 

Damit kann nun eine Vorlage erstellt werden und diese gegen eine Path verglichen werden. Wenn es passt, sind die Variablen in der PathMatch Klasse enthalten und können bequem abgefragt werden.

// PathPattern
    ...
    public PathMatch match(CharSequence path) {
        PathMatch pm = new PathMatch();
        pm.matching(this, path);
    }
    ...

Listing 7: PathPattern um die Metode "match" erweitert

 

In Benutzung

Es ist empfehlenswert zuerst einmal Tests zu schreiben, ob diese Klassen die Aufgabe erfühlen.

// Testcases
@Test
public void testSimplePathTemplate1() {
    PathTemplate pt = new PathTemplate("/{action}.html");
    PathMatch pm = pt.match("/help.html");
    assertNotNull(pm);
    assertTrue(pm.isMatch());
    assertTrue(pm.contains("action"));
    assertEquals("action must be 'help'", "help", pm.get("action", null));
}

@Test
public void testSimplePathTemplate2() {
    PathTemplate pt = new PathTemplate("/{action}.html");
    PathMatch pm = pt.match("/HELP.html");
    assertNotNull(pm);
    assertTrue(pm.isMatch());
    assertTrue(pm.contains("action"));
    assertEquals("action must be 'HELP'", "HELP", pm.get("action", null));
}

@Test
public void testComplexPathTemplate() {
    PathTemplate pt = new PathTemplate("/{action}/{verb}/{id}.html");
    PathMatch pm = pt.match("/menu/edit/203.html");
    assertNotNull(pm);
    assertTrue(pm.isMatch());
    assertTrue(pm.contains("action"));
    assertEquals("action must be 'menu'", "menu", pm.get("action", null));
    assertTrue(pm.contains("verb"));
    assertTrue(pm.contains("id"));
    assertEquals("203", pm.get("id", null));
}

Listing 8: Ausschnitt aus der Test Klasse

 

Abschluß

Nun ist es möglich in einem Servlet für Html Anfragen schon in der Url Informationen einzupacken. Wünschenswert ist natürlich auch, das zu den Variablendefinition {action} auch ein RegEx Ausdruck definiert läßt (beispielsweise {id: [0.9]+?}).

Die Listing sind stark vereinfacht, d. h.: In frei Wildbahn sind einige Überprüfungen notwendig, damit der Code funktioniert und man ihm vertrauen kann.

 

Hinweis: Ich habe die Implementierung von Jersey (RESTfull) als Inspiration verwendet. Dort gibt es auch ein Path Deklaration un Methode mit einer Abfrage zu befinden.

 

24 Aug

sarah kirchner

24.08.2012 17:42

Shell Programmierung

So selten wie ich mit Shell programmiere, habe ich mir überlegt, den eine oder anderen Trick, der mir so über den Weg läuft, in einem Blogeintrag zu beschreiben.

Script nur im eigenen Verzeichnis starten

Oft müssen Shell Scripts nur in ihrem eigenen Verzeichnis ausgeführt werden, weil zum Beispiel das Script von einer bestimmten Verzeichnisstruktur ausgehen muss / will.


#!/bin/sh
# das Verzeichnis des Scripts auslesen
US=`dirname $0`;
# in das Verzeichnis wechseln und das aktuelle Ausführungsverzeichnis lesen
US=`cd $US; pwd;`;

# wenn das Ausführungsverzeichnis nicht dem Scriptverzeichnis ist
[ "$US" = "$PWD" ] || {
  echo "Fehler: bitte das Script im Projektverzeichnis ausführen" >&2
  exit 1;
}
# weiter ...

Etwas besonderes ist die Zeile [ "$US" = "$PWD" ] || { ... } Damit wird ein Test durch geführt, der im Fehlerfall (else Zweig) die Meldung  ausführt.

Danke an meinen Shell Script Guru.