PPSA

Suche


14.03.2012

Grails Plugins – lokal und global / Grails Plugins – local and global

Florian Loeffler, 12:58 Uhr in PPSA; Tags:

English Version

Bei der Verwaltung von Grails Plugins in einem zentralen Repository gestaltet sich die lokale Weiterentwicklung der Plugins ggf. schwierig.

Mit dem folgenden Code-Block in der BuildConfig.groovy kann mittels des Kommandozeilenparameters -Dlive=... eines, oder mit
Komma getrennt, mehrere Plugins lokal geladen werden.

Wichtig ist dabei, dass neben dem Hauptprojekt auch alle Plugins mit diesem Code Block ausgestattet werden. So werden auch transitive Abhängigkeiten wie gewünscht mittels relativem Pfad oder aus dem Repository aufgelöst.

/*
* PPSA PLUGIN CONFIGURATION
*/
def runtimePlugins = [
    ":ppsa-menu:latest.release",
    ":ppsa-layout:latest.release",
]

/* ----- BEGIN: no changes past here ----- */
def live = System.getProperty("live")?System.getProperty("live").split(','):[]
def filteredLivePlugins = []
if (live) {
    filteredLivePlugins = live.inject([]) { list, lib ->
        runtimePlugins.grep { it.contains(lib) } ? list << lib : list
    }
    if (filteredLivePlugins) {
        println "[YOUR_PROJECT_NAME_HERE] Loading some plugins from relative paths in the workspace: " + filteredLive
        filteredLivePlugins.each { pluginName -> grails.plugin.location."${pluginName}" = "../${pluginName}" }
    }
}
/* ----- END: no changes past here ----- */

grails.project.dependency.resolution = {
    plugins {
        def filteredRuntimePlugins = runtimePlugins.inject([]) { list, lib ->
            filteredLivePlugins.grep { lib.contains(it) } ? list : list << lib
        }
        if (filteredRuntimePlugins) {
            println "[YOUR_PROJECT_NAME_HERE] Loading plugins from release repository: " + filteredRuntimePlugins
            filteredRuntimePlugins.each { pluginDef -> runtime "${pluginDef}" }
        }
    }
}

Beispiel, wie man zur live Umgebung für das Plugin ppsa-layout wechseln kann:

grails clean -Dlive=ppsa-layout
grails run-app -Dlive=ppsa-layout


Grails Plugins – local and global


With the management of Grails Plugins in a central repository the local further development of plugins could become quite difficult.

With the following Code Block in the  BuildConfig.groovy one or more plugins — sepearted via commas — can be loaded locally instead of from the repository with the help of the command line parameter  -Dlive=....

In order to also resolve transitive dependencies in this way it is important that beside the main project all plugins are also provided with this code block. This way also transitive dependencies can be loaded from a relative path or from the repository.

/*
* PPSA PLUGIN CONFIGURATION
*/
def runtimePlugins = [
    ":ppsa-menu:latest.release",
    ":ppsa-layout:latest.release",
]

/* ----- BEGIN: no changes past here ----- */
def live = System.getProperty("live")?System.getProperty("live").split(','):[]
def filteredLivePlugins = []
if (live) {
    filteredLivePlugins = live.inject([]) { list, lib ->
        runtimePlugins.grep { it.contains(lib) } ? list << lib : list
    }
    if (filteredLivePlugins) {
        println "[YOUR_PROJECT_NAME_HERE] Loading some plugins from relative paths in the workspace: " + filteredLive
        filteredLivePlugins.each { pluginName -> grails.plugin.location."${pluginName}" = "../${pluginName}" }
    }
}
/* ----- END: no changes past here ----- */

grails.project.dependency.resolution = {
    plugins {
        def filteredRuntimePlugins = runtimePlugins.inject([]) { list, lib ->
            filteredLivePlugins.grep { lib.contains(it) } ? list : list << lib
        }
        if (filteredRuntimePlugins) {
            println "[YOUR_PROJECT_NAME_HERE] Loading plugins from release repository: " + filteredRuntimePlugins
            filteredRuntimePlugins.each { pluginDef -> runtime "${pluginDef}" }
        }
    }
}

Example on how to switch to the live environment for the plugin ppsa-layout:

grails clean -Dlive=ppsa-layout
grails run-app -Dlive=ppsa-layout

13.03.2012

Grails Plugins in eigenem Maven Repository verwalten / Manage Grails Plugins in own Maven Repository

Florian Loeffler, 11:01 Uhr in PPSA; Tags:

English Version

In diesem Beitrag zeige ich kurz wie man Grails 2 Plugins als Releases in einem eigenen Maven Repository wie Archiva oder Artifactory verwalten kann. Dafür sind grundsätzlich zwei seperate Konfigurationen für Upload und Download vom Repository nötig.

Upload

Für das Releasen neuer Versionen in das eigene Repository bietet Grails 2 das Release Plugin, das man allerdings erst selbst nachinstallieren muss:

grails install-plugin release

Die nötige Konfiguration dazu wird in die ~/.grails/settings.groovy ausgelagert. Einträge in dieser Datei gelten für alle Grails Projekte und sind äquivalent zu Einträgen in der BuildConfig.groovy.

Hier ein Beispiel zur Einrichtung von Archiva als Release Repository:

/*
* THIS IS FOR UPLOADING TO THE REPO (via release plugin)
* To use this do:
* grails2 install-plugin release
*/
grails.project.repos.default = "rrzeArchiva"

grails.project.repos.rrzeArchiva.url = "http://REPOSITORY:PORT/archiva/repository/internal/"
grails.project.repos.rrzeArchiva.username = "USERNAME"
grails.project.repos.rrzeArchiva.password = "PASSWORD"

// disable nagging about not having committed all changes...
grails.release.scm.enabled = false

Das Beispiel Konfiguriert das rrzeArcchiva Repository als Default für Uploads und deaktiviert die SVN Überprüfung für nicht eingecheckte Änderungen.

Fertig.

Neue Releases können dann mit folgenden Build Targets ins Repository geladen bzw. in den lokalen Maven Cache installiert werden.

grails maven-deploy
grails maven-install

Download

Für den automatischen Download von Plugins aus dem Repository sind folgende Einträge in der BuildConfig.groovy des Projektes nötig.

grails.project.dependency.resolution = {
repositories {
mavenLocal()
mavenRepo name:"rrzeArchiva", root:"http://REPOSITORY:PORT/archiva/repository/internal"
}
plugins {
runtime ":ppsa-menu:latest.release"
}
}

Der Repositories Abschnitt Konfiguriert den lokalen Maven Cache und das Archiva Repository als zu durchsuchende Quellen für Plugin Abhängigkeiten.
Die Plugin Abhängigkeiten selbst werden im eigenen Abschnitt Plugins verwaltet. Der Beispieleintrag lädt immer die aktuellste Version des Plugins ppsa-menu.

Eigentlich würde das nun schon reichen, um Plugins auf dem Repository zu installieren.

Die allermeisten internen Repositories werden jedoch noch zusätzlich eine Authentifizierung erfordern. Hierfür muss noch der folgende Eintrag zur ~/.grails/settings.groovy hinzugefügt werden.

/*
* THIS IS FOR DOWNLOADING FROM THE REPO
* To use this add this to your BuildConfig.groovy:
* mavenRepo name:"rrzeArchiva", root:"http://REPOSITORY:PORT/archiva/repository/internal"
*/
grails.project.ivy.authentication = {
credentials {
realm = "Repository Archiva Managed internal Repository"
host = "REPOSITORY(ohne Port!)"
username = "USERNAME"
password = "PASSWORD"
}
}

—- WICHTIG —-

Für Archiva muss der Realm wie angegeben konfiguriert werden. Sonst geht nix!

Außerdem muss der Host hier ohne Port angegeben werden. Wenn das Archiva also z.B. auf Port 8080 läuft ist diese Angabe hier wegzulassen.
—- WICHTIG —-

Managing Grails Plugins in own Maven Repository


 

In this post I will briefly show how to manage Grails 2 Plugins as releases in an own Maven Repository such as Archiva or Artifactory. Therefore basically two separate configurations for Upload and Download from the repository are needed.

Upload

To release new versions in the own repository Graisl 2 offers the Release Plugin, which though must be installed additionally by yourself:

grails install-plugin release

The necessary configuration for this is stored in the  ~/.grails/settings.groovy. Entries in this data are valid for all Grails projects and equivalent entries in the BuildConfig.groovy.

Here is an example of installing Archiva as release repository:

/*
* THIS IS FOR UPLOADING TO THE REPO (via release plugin)
* To use this do:
* grails2 install-plugin release
*/
grails.project.repos.default = "rrzeArchiva"

grails.project.repos.rrzeArchiva.url = "http://REPOSITORY:PORT/archiva/repository/internal/"
grails.project.repos.rrzeArchiva.username = "USERNAME"
grails.project.repos.rrzeArchiva.password = "PASSWORD"

// disable nagging about not having committed all changes...
grails.release.scm.enabled = false

The example configures the rrzeArcchiva repository as Default for Uploads and deactivates the SVN proof for not-checked-in changes.

Done.

New releases can be loaded with the following Build Target in the Repository, respectively installed the local Maven Cache.

grails maven-deploy
grails maven-install

Download

For the automatic download of plugins from the repository the following entries in the  BuildConfig.groovy of the project are needed.

grails.project.dependency.resolution = {
repositories {
mavenLocal()
mavenRepo name:"rrzeArchiva", root:"http://REPOSITORY:PORT/archiva/repository/internal"
}
plugins {
runtime ":ppsa-menu:latest.release"
}
}

The repository paragraph configures the local Maven Cache and the Archiva Repository as sources for plugin dependencies which have to be browsed.
The plugin dependencies themselves are managed in the own paragraph plugins. The example entry is always loading the most current version of the plugin ppsa-menu.

Actually this would be enough to install plugins on the repository.

Most of all intern repositories will though need further authentication. Therefore the following entry has to be added ~/.grails/settings.groovy.

/*
* THIS IS FOR DOWNLOADING FROM THE REPO
* To use this add this to your BuildConfig.groovy:
* mavenRepo name:"rrzeArchiva", root:"http://REPOSITORY:PORT/archiva/repository/internal"
*/
grails.project.ivy.authentication = {
credentials {
realm = "Repository Archiva Managed internal Repository"
host = "REPOSITORY(ohne Port!)"
username = "USERNAME"
password = "PASSWORD"
}
}

—- IMPORTANT —-

For Archiva the realm must be configured as shown. Or nothing will work!

Also, the Host must be stated here without Port. If the Archiva is also running e.g. on Port 8080 these information can be omitted here.
—- IMPORTANT —-

12.12.2011

Postgres, Serials und Rules: Achtung!

Peter Reiß, 17:50 Uhr in PPSA; Tags:

Achtung bei folgender Konstellation:

CREATE TABLE source
(
id serial NOT NULL,
"text" character varying(255),
CONSTRAINT id_pkeyPRIMARY KEY (id)
)

mit Regel:

CREATE RULE "insert_rule" AS
ON INSERT TO source DO  INSERT INTO dest (sourceid)
VALUES (NEW.id);

Serial und Bigserial sind in Postgres intern so konzipiert, dass der Wert für das Feld durch einen Aufruf von

nextval('source_id_seq')

erzeugt wird. Entgegen der Erwartung wird nach Ausführen der Regel aber nicht source.id in die Tabelle dest eingetragen, sondern source.id +1. Bug oder einfach so gewolltes Verhalten von Postgres? In der Postgres-Dokumentation zu Regeln findet sich zumindest ein entsprechender Kommentar:
“Beware that CREATE RULE insert_foo AS ON INSERT TO foos DO ALSO INSERT INTO bar (foo_id) VALUES (NEW.id); won’t work if foo.id is a serial (and foo_id REFERENCES foos ofcourse) because nextval() is evaluated twice.”

Sauberste Lösung: Hier keine Regel, sondern einen Trigger verwenden.

Postgres, Serials and Rules: Caution!

Caution with the following constellations:

CREATE TABLE source
(
id serial NOT NULL,
"text" character varying(255),
CONSTRAINT id_pkeyPRIMARY KEY (id)
)

with rule:

CREATE RULE "insert_rule" AS
ON INSERT TO source DO  INSERT INTO dest (sourceid)
VALUES (NEW.id);

Serial and Bigserial are so in Postgres internally designed that the data for the field can be generated with the request of

nextval('source_id_seq')

Against all expectations after carrying out the rule it is not source.id which is filled into the table dest but source.id +1. Bug or simply unwanted behavior of Postgres? In the Postgres-Documentation the rules can be found at least in on depending commentary:
“Beware that CREATE RULE insert_foo AS ON INSERT TO foos DO ALSO INSERT INTO bar (foo_id) VALUES (NEW.id); won’t work if foo.id is a serial (and foo_id REFERENCES foos ofcourse) because nextval() is evaluated twice.”

Cleanest solution: Do not use a rule here but a trigger.

23.08.2011

Properties einer Grails-Domänenklasse auslesen

Peter Reiß, 11:47 Uhr in PPSA; Tags:

Bei der Deklaration einer Domänen-Klasse in Groovy wird auf die Bean-Notation für Variablen (Variable deklariert als private, zusätzlich Getter und Setter) verzichtet. Stattdessen werden sogenannte Properties definiert, die durch Groovy intern in Java-Beans umgewandelt werden.

class MyTest {
Long id
String val1, val2
}

Oftmals ist es nötig, die Liste der Properties auszulesen, etwa um ein Objekt in ein anderes zu mappen. Hierfür bietet Groovy die Methode getProperties():

def x = new MyTest()
x.properties.each { key, value ->
println "Property ${key}. ${value}"
}

Hier tauchen aber plötzlich Properties auf, die in MyType gar nicht definiert wurden, wie version, class, metaClass, constraints! Grails Magic, hier wurden durch das Grails-Framework weitere Properties hinzugefügt!

Wie kommt man nun an die Liste nur der selbst definierten Properties? Die Antwort liegt bei den persistentProperties, die in der Klasse liegen. Diese erreicht man über die DefaultGrailsDomainClass.

def persistentProps = new DefaultGrailsDomainClass(MyTest.class).persistentProperties
persistentProps.each {
println "${it.name}"
}

Et Voila: Die Liste der persistenten Properties, in aller Regel der selbstdefinierten.

 

Read Properties of a Grails Domain Class

With the declaration of a domain class in  Groovy it is done without a Bean-Notation for variables (variable declared as  private, furthermore Getter and Setter). Instead so called properties are defined which are changed internally into Java-Beans by Groovy

class MyTest {
Long id
String val1, val2
}

It is often necessary to read out the list, for example to map an object into another. Therefore Groovy offers the methode getProperties():

def x = new MyTest()
x.properties.each { key, value ->
println "Property ${key}. ${value}"
}

At this points properties suddenly appear which haven’t been defined like version, class, metaClass, constraints! Grails Magic, further properties have been added by the Grails-Framework!

So how do you get the list of the self defined properties only? The answer lies within the persistentProperties, which are within the class. You can reach it via DefaultGrailsDomainClass.

def persistentProps = new DefaultGrailsDomainClass(MyTest.class).persistentProperties
persistentProps.each {
println "${it.name}"
}

Et Voila: The list of persistent properties, usually the self defined.

17.03.2011

Logging von Grails-Applikationen in JBoss

Peter Reiß, 11:52 Uhr in PPSA; Tags:

Kurzanleitung:

  • Aus Classloader-Gründen darf die Applikation nicht eine eigene log4j-xxx.jar beinhalten. Diese kann aus dem war-file entfernt werden durch folgenden Eintrag in BuildConfig.groovy:
    grails.war.resources = { stagingDir ->
    delete(file:"${stagingDir}/WEB-INF/lib/log4j-1.2.16.jar")
    delete(dir:"${stagingDir}/WEB-INF/classes/org/grails/tomcat")
    }
    

    (Der Eintrag mit tomcat ist für das Thema hier nicht relevant, entfernt aber unnötige Klassen aus dem war-file.)

  • Die Grails-Log4j-Konfiguration aus Config.groovy wird im JBoss nicht ausgewertet. Daher sind die Einträge in der JBoss-Log4j-Konfiguration ${JBOSS}/server/default/conf/jboss-log4j.xml nachzuziehen.
    Beispiel für Controller:

    <category name="grails.app.controller.MyController">
    <priority value="INFO"/>
    </category>
    

    Man beachte auch das “grails.app.controller“-Präfix!

  • Eine weitere Seltsamkeit ist, dass nach einem Undeploy oder Redeploy eines Grails-war-files das Logging des JBoss komplett beendet wird, d.h. absolut nichts mehr in die Logdatei geschrieben wird. Den Grund und eine saubere Lösung dafür habe ich noch nicht gefunden, aber als Workaround ist es möglich, ein touch auf ${JBOSS}/server/default/conf/jboss-log4j.xml auszuführen. Da diese Konfiguration regelmäßig eingelesen wird, erscheinen die Logmeldungen wieder.

Logging of Grails application in JBoss

Brief administration:

  • Because of  Classloader reasons the application can not have an own  log4j-xxx.jar. It can be deleted from the war-file with the following entry in  BuildConfig.groovy:
    grails.war.resources = { stagingDir ->
    delete(file:"${stagingDir}/WEB-INF/lib/log4j-1.2.16.jar")
    delete(dir:"${stagingDir}/WEB-INF/classes/org/grails/tomcat")
    }
    

    (The entry with tomcat is not relevant for this topic, but deletes unnecessary classes from the war-file.)

  • The Grails-Log4j-configuration from Config.groovy is not evaluated in JBoss. Therefore the entries need to be moved in the JBoss-Log4j-onfiguration ${JBOSS}/server/default/conf/jboss-log4j.xml.
    Example for Controller:

    <category name="grails.app.controller.MyController">
    <priority value="INFO"/>
    </category>
    

    Keep the “grails.app.controller“-Prefix in mind!

  • Another oddity is that after an undeploy or redeploy of a Grails-war-file the logging of the JBoss will be completely closed,  which means that absolutely nothing is written in the log file. We haven’t found yet the reason and a clean solution for this problem, but as a Workaround it is possible to make a touch to ${JBOSS}/server/default/conf/jboss-log4j.xml. Because this configuration will be importet regularly the logging messages will reoccur.

17.11.2010

Promovierendenverwaltung wird selbständig

Peter Reiß, 15:35 Uhr in Allgemeines, PPSA, PV; Tags:

English Version

Mit Version 5.1 von mein campus wird die Promovierendenverwaltung der Graduiertenschule der Friedrich-Alexander-Universität auf einen eigenen Internet-Auftritt ausgegliedert. Sie ist seit Mittwoch, 17.11. unter der URL https://www.docdaten.uni-erlangen.de zu erreichen.

Unter dieser Adresse können Promotionen registriert werden, registrierte und freigeschaltete Promovenden haben Einsicht in ihre Daten (Benutzername und Passwort bleiben hier unverändert). Zugleich mit der Ausgliederung hat sich auch der Funktionsumfang erweitert:

Neue Features bei der Registrierung von Promotionen:

  • Datenübernahme aus mein campus für (ehemalige) Studierende der FAU ist möglich!Nach Eingabe von Benutzername und Passwort werden persönliche Daten sowie sowie Informationen über Studiengang und -abschluss aus mein campus ausgelesen und in das Registrierungsformular übertragen.
  • Die Registrierung kann zwischengespeichert werden, woraufhin ein Link per E-Mail zugesandt wird, unter dem sie innerhalb zwei Wochen fortgesetzt werden kann. Nach zwei Wochen werden nicht vollständige Registrierungen gelöscht, um Datenmüll zu vermeiden.

Neue Features bei Dateneinsicht:

  • Eine PDF-Ansicht der Daten kann erzeugt und heruntergeladen werden.

Zudem gibt es einige neue Funktionen für die Administratoren des Systems

Wir wünschen viel Spaß mit Docdaten!

PhD Administration becomes independent

With version 5.1 of mein campus the PhD administration of the grad school of the Friedrich-Alexander-Universität will be removed to its own internet appearance. You can reach it since Wednesday, 17.11. under the URL  https://www.docdaten.uni-erlangen.de  .

Under this adress PhD-students can be registered, already registered and activated PhD-students can view their data (username and password remain unchanged here). At the same time with the new appearance the functional range has expanded:

New features at the registration of PhD-students:

  • Data transfer from mein campus for (former) students of the FAU is possible! After inserting username and password, personal data as well as information on the study course and degree can be read from mein campus and be transfered into the register form.
  • The registration process can be saved within, whereupon a link is sent vie e-mail under which the process can be continued within two weeks. After two weeks uncomplete registrations will be deleted to avoid data trash.

New features for data overview:

  • A PDF overview of data can be generated and downloaded.

Further there are some new functions for the admins of the systems.

We hope you have a lot of fun with Docdaten!

12.10.2010

Spring, Maven und Variablen

Peter Reiß, 15:45 Uhr in PPSA; Tags:

Das Ziel ist: In der Spring-Konfiguration Platzhalter verwenden, die je nach Maven-Profil anders belegt werden.

pom.xml, hier muss Filterung konfiguriert werden:

  • Properties definieren:
    <properties>
    <location>remote</location>
    </properties>
    
  • Ersetzung aktivieren, im build-Baum der POM bzw. des jeweiligen Profils. Dies ersetzt in den spezifizierten Dateien Einträge der Form ${location} durch die entsprechenden Werte:
    <resources>
      <resource>
        <directory>src${file.separator}main${file.separator}resources</directory>
        <filtering>true</filtering>
        <includes>
          <include>**${file.separator}*.xml</include>
          <include>*.xml</include>
          <include>**${file.separator}*.properties</include>
          <include>*.properties</include>
        </includes>
      </resource>
      <resource>
        <directory>src${file.separator}main${file.separator}resources</directory>
        <filtering>false</filtering>
        <excludes>
          <exclude>**${file.separator}*.xml</exclude>
          <exclude>*.xml</exclude>
          <exclude>**${file.separator}*.properties</exclude>
          <exclude>*.properties</exclude>
        </excludes>
      </resource>
    </resources>
    

Spring-Konfiguration (applicationContext.xml in WEB-INF/):

  • placeholderConfig definieren:
    <bean id="placeholderConfig"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations">
        <list>
          <value>classpath:ejb-configuration.properties</value>
        </list>
      </property>
    </bean>
    
  • Variable verwenden:
    <jee:jndi-lookup id="gradschoolManager" jndi-name="GradschoolManagerBean/${location}">
      <jee:environment>
        java.naming.factory.initial=org.jboss.naming.NamingContextFactory
        java.naming.provider.url=${java.naming.provider.url}
        java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
      </jee:environment>
    </jee:jndi-lookup>
    

Jetzt muss nur noch eine entsprechende Properties-Datei angelegt werden, z.B. unter /src/main/resources:

java.naming.provider.url=${java.naming.provider.url}
location=${location}

Wie läuft das ab?

Bei einem Maven-Lauf werden zunächst die Variablen in der Properties-Datei ersetzt. Spring ist so konfiguriert, dass die (ersetzten) Werte aus dieser Datei gelesen und genutzt werden.

11.10.2010

Log4j Appender mit automatischem Zip-nach-Rotate

Florian Loeffler, 14:32 Uhr in PPSA; Tags:

English version

Um Pattenplatz bei der Benutzung von Log4j zu sparen wurden zwei alternative Appender implementiert die neben der üblichen Rotation der Logdatei, diese auch gleich mittels zip komprimiert.

Die beiden Appender Klassen sind entsprechend ihren Pendants auf die Namen DailyRollingZipAppender und RollingZipAppender getauft und im repo unter ppsa-log4j-zip zu finden.
Beide Klassen erweitern die ursprünglichen Appender Implementierungen und können deshalb 1:1 ohne zusätzlichen Konfigurationsaufwand ausgetauscht werden.

Um eine Logdatei beim rotieren automatisch zu zippen müssten zum Beispiel folgende Anpassungen vorgenommen werden:

     <appender name="FILE" class="org.jboss.logging.appender.DailyRollingFileAppender">

ersetzen durch

     <appender name="FILE" class="de.rrze.ppsa.log.DailyRollingZipAppender">

Außerdem muss die ppsa-log4j-zip-0.0.1.jar im Classpath liegen.

Die Implementierung wurde inspiriert von einem Blogbeitrag auf ralfhaug.de.

Log4j appender with automatic zip-after-rotate

In order to save precious disc space when using Log4j, we developed to alternative appender implementations with zip functionality. In addition to the usual rotation of the log files, these appenders also compress the rotated logs using the zip algorithm.

The two appender classes are named after their respective counterparts: DailyRollingZipAppender and RollingZipAppender. They are available in our repo under ppsa-log4j-zip.
Both classes extend the oiginal appender implementations and can be used as plug-in replacements without any additional configuration.

An example configuration to automatically zip a logfile after rotate would be done something like this:

     <appender name="FILE" class="org.jboss.logging.appender.DailyRollingFileAppender">

must just be replaced with

     <appender name="FILE" class="de.rrze.ppsa.log.DailyRollingZipAppender">

Additionally the ppsa-log4j-zip-0.0.1.jar has to be in the classpath.

The implementation was inspired by this blog entry on ralfhaug.de.

01.09.2010

Sortierbare Elemente in Tapestry per jQuery UI

Martin Fischer, 15:06 Uhr in PPSA; Tags: ,

Problembeschreibung

 
English Version
Zur Sortierung von Elementen in einem Webfrontend kann man sich verschiedene Realisierungen vorstellen. Beispielsweise könnte man für jedes Element eine Textbox bereitstellen, in denen der Benutzer den Index innerhalb der Sortierreihenfolge definiert. Das ist zwar sehr barrierefrei, aber auch unhandlich, wenn man sich vorstellt, dass in einer bestehenden Sortierung das letzte Element weit nach oben verschoben werden soll, da dann viele Indizes angepasst werden müssen. Eine andere Realisierung wären Links mit denen die Elemente jeweils eine Position nach oben oder unten verschoben werden können. Das erfordert jedoch häufiges Neuladen der Seite und ist für den Benutzer nicht sonderlich ästhetisch anzusehen.

Daher wurde für eine konkrete Realisierung des Problems in FAU.ORG auf die wunderbare Welt der AJAX/Javascript Komponenten von jQuery zurückgegriffen. Konkret auf die Komponenten von jQuery UI. Diese Komponentensammlung umfasst sowohl Interactions (also Elemente wie Draggable, Droppable, Sortable, etc.) als auch Widgets (Datepicker, Progressbar, etc.). Die Sortable-Komponente entsprach genau den Anforderungen des oben beschriebenen Problems.

Integration von jQuery:Sortable in Tapestry

Zunächst müssen die notwendigen Javascript-Bibliotheken eingebunden werden:

<link type="text/css" href="${asset:jqueryui/themes/base/jquery.ui.all.css}" rel="stylesheet" />
<script type="text/javascript" src="${asset:scripts/jquery-1.4.1.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.core.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.widget.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.mouse.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.sortable.js}"></script>

Optional kann man die sortierbaren Elemente (die als unordered List gerendert werden) als Kästen formatieren:

<style type="text/css">
#sortable { list-style-type: none; margin: 0; padding: 0; float: left; margin-right: 10px; }
#sortable li { margin: 0 5px 5px 5px; padding: 5px; font-size: 1.0em; width: 350px; }
</style>

Danach wird die Liste um Sortierfähigkeit erweitert und ein Handler entwickelt, der die Sortierergebnisse speichert:

<script type="text/javascript">
$(function() {
$("#sortable").sortable();
$("#sortable").disableSelection();
});

function storeOrder() {
var result = $('#sortable').sortable('toArray');
var context = "";
for (var id in result) {
if (context == "") {
context = result[id];
} else {
context = context + "_" + result[id];
}
}
window.location = "/${pageName}" + ".save/" + context;
}
</script>

Schließlich wird die Liste selbst noch eingebunden und ein Speichernlink bereitgestellt:

<ul id="sortable">
<li t:type="loop" source="elements" value="element" id="${element.id}">${element.name}</li>
</ul>
<a href="#" id="prepare" onClick="storeOrder()">${message:save}</a>
<t:actionlink t:id="save" context="literal:dummy"></t:actionlink>

Die einzelnen Listenelemente werden durch invisible Instrumentation der Loop-Komponente von Tapestry erzeugt. Die erste Funktion im Javascript-Block sorgt für die Verschiebbarkeit per Maus der Elemente innerhalb der Liste und verbietet deren Selektierbarkeit.

Beim Klicken auf den HTML-Link wird lediglich die Javascript-Funktion storeOrder() aufgerufen. Diese erzeugt ein Array der IDs der Elemente in ihrer vor dem Klicken aktuellen Reihenfolge. Da diese Liste per ActivationContext an einen Actionhandler auf Tapestry-Serverseite übergeben werden soll, kommaseparierte Liste in diesem Kontext jedoch nicht zugelassen sind, wird die Liste in einen String umgewandelt, in dem die Element-IDs per Unterstrich voneinander getrennt vorkommen. Dies kann auf Serverseite durch ein simples String.split() wieder zerlegt werden. Man beachte, dass dafür ebenfalls ein Actionhandler auf Serverseite bereitgestellt werden muss. Dafür wurde der pseudo-Actionlink erzeugt. Der Redirect der storeOrder()-Funktion ruft den Actionhandler mit dem vorbereiteten ActivationContext schließlich manuell auf.

Demo

Eine Demo der Sortable-Komponente von jQuery UI ist hier zu finden.

Sortable Elements in Tapestry with jQuery UI

Problem description

There are many ways to realize sorting elements in a web frontend. One could provide a textbox for every element, which holds the index within the sorting order. This is barrier free to the fullest, but also very inconvenient. Imagine a ordered list, where the last element should be moved to the very top. With this technique almost all element indexes must be adopted. Another realization would be two links on every element. One to move itself upwards and one downwards. This requires a lot of page reloading and is not really state of the art.

To solve the concrete problem within the FAU.ORG project the wonderful AJAX/Javascript components of jQuery were used. More specific the components of jQuery UI. This collection of components includes Interactions (elements like Draggable, Droppable, Sortable, etc.) and Widgets (Datepicker, Progressbar, etc.). The sortable component matched exactly the requirements of the above mentioned problem.

Integration of jQuery:Sortable in Tapestry

First of all one has to include the required Javascript-libraries:

<link type="text/css" href="${asset:jqueryui/themes/base/jquery.ui.all.css}" rel="stylesheet" />
<script type="text/javascript" src="${asset:scripts/jquery-1.4.1.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.core.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.widget.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.mouse.js}"></script>
<script type="text/javascript" src="${asset:jqueryui/ui/jquery.ui.sortable.js}"></script>

The next step comprises optional formatting of the elements (which are rendered as an unordered list):

<style type="text/css">
#sortable { list-style-type: none; margin: 0; padding: 0; float: left; margin-right: 10px; }
#sortable li { margin: 0 5px 5px 5px; padding: 5px; font-size: 1.0em; width: 350px; }
</style>

Now the list is enriched to support sortability and a handler is developed, which stores the order of elements:

<script type="text/javascript">
$(function() {
$("#sortable").sortable();
$("#sortable").disableSelection();
});

function storeOrder() {
var result = $('#sortable').sortable('toArray');
var context = "";
for (var id in result) {
if (context == "") {
context = result[id];
} else {
context = context + "_" + result[id];
}
}
window.location = "/${pageName}" + ".save/" + context;
}
</script>

Finally the list itself must be included and a link to store the changes provided:

<ul id="sortable">
<li t:type="loop" source="elements" value="element" id="${element.id}">${element.name}</li>
</ul>
<a href="#" id="prepare" onClick="storeOrder()">${message:save}</a>
<t:actionlink t:id="save" context="literal:dummy"></t:actionlink>

Every list element is created with invisible instrumentation of the Loop-component of Tapestry. The first function in the Javascript-block enables moving elements by mouse within the list and disables selections.

When clicking the HTML-link the storeOrder() Javascript-function is called. This function creates an array of the element-IDs in the most recent defined order. As the list will be handled over to an ActionHandler-function by ActivationContext on Tapestry server-side and comma-separated lists are forbidden within this context, the array is converted to a single string, where the IDs are separated by underscore. This string can easily be converted to an array again with the  String.split() Java-function.  Be aware, that therefore an ActionHandler has to be provided on server-side. To accomplish this a pseudo ActionLink is created within the Tapestry-Template. The redirect of the storeOrder()-function calls the ActionHandler with the given context manually.

Demo

A demo of the Sortable-component of jQuery UI can be found here.

08.04.2010

Isoliertes Classloading auf dem JBOSS AS

Martin Fischer, 15:15 Uhr in PPSA; Tags:

English Version

Durch Verwendung von Technologien wie Enterprise Java Beans (EJB) und Enterprise Service Bus (ESB) ist es heute ohne Weiteres möglich, lose gekoppelte und feingranular modulare Dienste zu entwerfen, die in Kollaboration eine verteilte Anwendung ausmachen. Einheiten des Entwurfs sind nicht mehr ganze Anwendungen, sondern klar abgegrenzte Dienste, die lokal oder entfernt benutzt werden können. Diese Dienste werden auf ein oder mehreren Applikationsservern (AS) deployed und können dort benutzt werden. Wenn die Dienste selbst auf Funktionen aus Bibliotheken zugreifen müssen, so kann es passieren, dass zwei unterschiedliche Dienste dieselbe Bibliothek in unterschiedlichen Versionen verwenden (müssen). Da beim Standard-Deployment allerdings alles von einem Classloader geladen und instanziert wird, tritt das Problem auf, dass nicht genau vorherzusagen ist, welche der beiden Versionen ein und derselben Bibliothek zuerst geladen wird. Darüberhinaus ist es ja gerade notwendig beide Versionen vorzuhalten, da sie in unterschiedlichen Contexten verwendet werden. Dies ist ohne weiteres Zutun nicht möglich. Daher bietet der JBOSS AS die Möglichkeit, Einheiten des Deployments (EJBs oder ESBs) so zu konfigurieren, dass sie von einem isolierten Classloader geladen werden. Damit ist es möglich die gleiche Bibliothek mehrfach zu laden (insbesondere mit unterschiedlichen Versionen), wenn dies gewünscht wird.

Eine Beispielkonfiguration für ESBs ist im Folgenden dargestellt:

[deployment.xml]
<jbossesb-deployment>
	<loader-repository>
		fauorg.esb.loader:loader=simple-scoped
		<loader-repository-config>java2ParentDelegaton=false</loader-repository-config>
	</loader-repository>
</jbossesb-deployment>

Die Datei sollte deployment.xml heißen und unter src/main/resources/META-INF abgelegt werden. Sie sorgt dafür, dass Klassen in folgender Reihenfolge geladen werden:

  1. /lib/ (aus dem verpackten ESBs)
  2. server/default/lib/

Dabei werden also lediglich Klassen aus dem server/default/lib/ Verzeichnis geladen, die nicht mit verpackt wurden. Der Name des JMX Classloader Objekts ist im Beispiel fauorg.esb.loader und sollte vom Entwickler festgelegt werden. Damit lassen sich auch mehrere Einheiten des Deployments in einem Classloader zusammenfassen.

Nähere Informationen hierzu finden sich hier. Eine Zusammenstellung von UseCases zum Thema isoliertes Classloading auf dem JBOSS AS gibts hier. Zum Aktivieren eines Loggers für den Classloader verfolgt man am besten diese Anleitung.

Isolated loading of classes on JBOSS AS

Using techology like Enterprise Java Beans (EJB) and Enterprise Service Bus (ESB), it is easily possible to design granular, losely connected modular services. In collaboration, these create distributed applications. Design units are no longer complete applications, but clearly separated services that can be used locally or remotely. These services are deployed on one ore more application servers (AS), and can be used there. If the services themselves need to access libraries, it is possible for two different services to use the same library in different versions. Since in standard deployment, everything is loaded and instanced by a classloader, the problem can occur that it becomes impossible to predict which library will be loaded first. It is furthermore necessary to keep both versions available, since they are used in different contexts. Without additional information, that is not possible. That is why JBOSS AS offers the possibility of configuring deployment units (EJBs or ESBs) in such a way that they are loaded by an isolated classloader. That makes it possible to load the same library multiple times (especially in different versions) if that is necessary.

Here is an exemplary configuration for ESBs:

[deployment.xml]
<jbossesb-deployment>
	<loader-repository>
		fauorg.esb.loader:loader=simple-scoped
		<loader-repository-config>java2ParentDelegaton=false</loader-repository-config>
	</loader-repository>
</jbossesb-deployment>

The file should be named deployment.xml and put in src/main/resources/META-INF. It ensures that classes are loaded in the following order:

  1. /lib/ (from the packaged ESBs)
  2. server/default/lib/

That means that only classes from the directory server/default/lib/ that weren’t packaged are loaded. The name of the JMX Classloader Object can be found in example fauorg.esb.loader and should be defined by the developer. This allows for several deployment units to be combined into one classloader

You can find further information here. A summary of UseCases regarding isolated classloading on the JBOSS AS can be found here. To activate logging for the classloader, follow these instructions.

Nach oben