Posts mit dem Label Gradle werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Gradle werden angezeigt. Alle Posts anzeigen

Sonntag, 8. Mai 2016

Java 8 on OpenShift

For some time now - supported by many articles I read on the web - I thought it was a fact that

OpenShift does not support Java 8

While the OpenShift Platform as a Service environment - even some time after Java 7 left the public support track - doesn't really push you to use Java 8, it well supports it in the sense that it's simply there - but just not the default.
Of course this has been mentioned before, but there are too many recent post around stating the opposite.

OpenShift has Java 8 in Place

When you stop reading articles and just dig around in the system, you can very quickly come to the point where you see, that OpenShift has numerous Java Development Kits installed and that the default is set by Linux standard means: /etc/alternatives.
So you don't have to download it, invest space and update it regularly to the current version. This is handled by OpenShift.

Wide Choice of JDKs

The /usr/lib/jvm folder has everything you might need - JDK-wise - and a simple scriptlet in your deployment will bring you to the needed JDK:

JAVA_VERSION=1.8.0
export JAVA_HOME=`find /usr/lib/jvm -name "$JAVA_VERSION"`
if [ -z JAVA_HOME ] ; then
  echo "INTENDED JAVA VERSION NOT FOUND!"
fi 

So, no need to stay away from Java 8 because of something related to OpenShift (anymore).

Prepared Quickstart for Java 8, Gradle, and Tomcat

For the users of Gradle and Tomcat I updated this in my quickstart on github.com based on the DIY cartridge.

Freitag, 6. Mai 2016

Tangram Release 1.1

The 1.1 release is a consolidation release removing outdated stuff leaving
room for latest versions of the used libraries and modules and providing some
polishing in the existing feature set.

Beyond the App Engine

The sources and release notes can be found on github.com, the binaries again reside on JCenter. This release includes some notable overdue dependency updates like
  • Servlet 3.1 API now included
  • JSP 2.1 now included
  • DataNucleus updated to 4.1
  • dinistiq updated to 0.5
To allow this, it leaves out the Google App Engine support.

Gretty Plugin

Since not only the Google App Engine has a defect with old releases of Java
Web APIs but also Gradle has a obsolete Jetty plugin with an old version of
Jetty, all examples have been migrated to Gretty which is now the preferred container integration and even application packaging option.

Morphia Support

Latest Tangram applications always used Mongo DB as the storage backend with either JPA or JDO as the API. If you don't need the API abstraction or want an even leaner storage integration (before that DataNucleus JDO and EBean had the smallest footprint) you now can start using the "Mongo DB only Mapping" Morphia.

Markdown Support

Up to now every property using char[] for getters and setters was handled as structured text. It was edited as an HTML fragment (p or div) using the CKEditor.
Starting with this release, every property using org.tangram.content.Markdown for the getters and setters is handled through the CodeMirror editor as Markdown syntax. It is transformed to HTML in rendering.
Also templates can now be in Markdown syntax whenever appropriate while also here the output is transformed to HTML before passing the templates to the other layers of Tangram.

Taking Care of the Modification Date and Time

If you need to track the modification time of an object through the Tangram editor module or the ftp service, you can now do so and the standard storage backed classes of Tangram now do so.

File Restart Cache

The restart caches used for faster restarting of the application with data which should not be changing between sessions of the application now only have to be cleared manually on error conditions but not on changes of the application any more.

Exporting and Importing

The export and import features of Tangram are now strictly symmetrical: Full content export and import  - more or less independent of the storage flavour in use - and a code exporter and importer for the sources stored in the repository.

Fat JARs

Additionally it is now possible to leave out the "war" dependencies since
resources like templates can be placed in the respective JARs.

Extended testing

Tangram now for the first time has a decent test coverage. While there still
is room for more in depth testing of many functions, we now can put more trust
into the single snapshot builds. This was quite important since most Tangram
applications not residing on the Google App Engine already migrated to the 1.1
level.

Sonntag, 22. November 2015

Tangram Release 1.0

Since Tangram reached more than the set of features originally intended back in 2009, it was time to call the current state a 1.0 release.

Why?

The name Tangram points out that the main objective was to create web applications from a fixed set of modules and options which can form nearly any shape you would need. You can start quickly with a limited set of functionality and let the application grow. Fixed set in this case only means that you will need e.g. a model implementation but still have a choice, which one you would want to use. Also the glueing together with a Dependency Injections solution provides several options.
Another intention was to provide reasonable defaults for nearly every aspect where this is possible. You don't need to copy tons of files which do things technically necessary, which you didn't think of at that time. You only set up the things you know that you need them, and all the other features stay quiet. At least they should do something reasonable without getting in your way.

Users

Tangram is in production use since 2011 by Provocon, Ponton, Naturinspiriert.org and others.

Dynamic Extendibility

For many extensions you will not even need a deployment but a change in the data repository forming small and stable cores where the application as whole can be modified to changing requirements quickly, easily and securely.
Apart from the obvious presentation stuff this includes Groovy codes to create and parse URL, extend the business logic, and even the model layer with new model classes.

Core Features

We provide object oriented templating for objects from JPA, JDO, EBean, and CoreMedia repositories. The core system emphasises dynamic web programming through CSS, JavaScript, Velocity Templates, and Groovy Codes in the repository together with basic CDN support, code minification, and caching support.

URLs can be formatted in nice, SEO friendly schemes and be easily mapped to actions to provide the result views for users.
The implementation of authentication and authorization now uses the pac4j set of libraries to provide a seamless and easy integration of plain user lists in files or the repository, OAuth providers, OpenID providers, Google App Engine user service and others for the web application and the base system itself.
This authentication solution is used grant access to the system itself but also to provide support for protected content areas in your applications. You only have to focus on the question, which content needs to be protected by a access granting scheme. Logins can be a generic login page or integrated in you application design.

Generic Editor

Except for CoreMedia we provide a generic web editor which is now responsive (there is now separate mobile editor anymore) which can be loaded from the respective cloud locations of the used components (CKEditor, CodeMirror). Also it can be extended with our dynamic web programming facilities.

Glueing Stuff Together

The mentioned default set of configurations and the option to customize this to your needs is achieved by Dependency Injection libraries. Tangram supports the usage of the Spring Framework, Google Guice, and dinistiq. It should be possible to add other solutions as well provided that they offer a decent set of functionality (which is not always the case for smaller DI libs.) including e.g. optional values, overriding of configurations, and deep generics introspection.

Readware

To illustrate the usage and provide a nice starting point, a set of example applications is provided in sync with the releases.
A documentation wiki is now starting.

Where the 1.0 Release Ends

One real limitation is the older set of JSP/Servlet APIs which need to stay in place to support the Google App Engine. So this is the final release to support the App Engine to be able to move ahead to newer APIs for several areas. This will not be achieved in the 1.0 branch.

Mittwoch, 28. Oktober 2015

Gradle Plugin for EBean, JPA, and JDO Enhancing along with Minification and Overlaying

During the development of the Tangram framework project a set of build related things went into a plugin for the Gradle tool.
The functionality of this Tangram Gradle Plugin is only in very small parts directly related to Tangram. It is more or less a general purpose plugin for applications needing
  • Byte-code transformation of model classes for
    JDO, JPA, and Ebean ORM layers
  • Minification of CSS and JavaScript codes to be placed in WAR artifacts
  • Support underlying of WAR files into others (similar to overlays)
The good news for today is, that with the latest version 1.0.5 the plugin can be used from the central Gradle plugins repository with the - not very surprising - id tangram.gradle.plugin. So some of the usage notes have to be aligned with this situation.

Usage

Just a few lines have to be added to your Gradle build script to use the plugin:

plugins {
  id "tangram.gradle.plugin" version "1.0.5"
}

All of the following steps described here take place without any additional configuration.

Prepare EBean, JPA, and JDO Model Classes

When used with Java projects - and when some data model classes are discovered, - the plugin tries to prepare them for use the respective Object Relational Mapper (ORM). The ORM APIs supported are
These APIs in turn are supported by a number of implementations. The supported implementations are
These OR-Mapper API implementations require (DataNucleus and EBean) or recommend (the others) to apply byte-code transformations called "Enhancing" or "Weaving" to the class files. The compiled code is extended with some database access support to implement the active record pattern more or less seamless.
The API and implementation library in use is discovered from the names of the elements of the class path of the project. If one of the mentioned libraries is found, the corresponding byte-code transformation is applied to the appropriate step (post compile or pre jar creation).

martin@nelson:~/proj/tangram/sites/naturinspiriert$ gradle clean build
:clean
:compileJava
Performing DataNucleus JPA byte code transformation.
ENHANCED (Persistable) : org.naturinspiriert.RootGroup
ENHANCED (Persistable) : org.naturinspiriert.Topic
ENHANCED (Persistable) : org.naturinspiriert.AbstractGroup
ENHANCED (Persistable) : org.naturinspiriert.ImageData
ENHANCED (Persistable) : org.naturinspiriert.Article
ENHANCED (Persistable) : org.naturinspiriert.Linkable
ENHANCED (Persistable) : org.naturinspiriert.Group
DataNucleus Enhancer completed with success for 7 classes. Timings : input=60 ms, enhance=54 ms, total=114 ms. Consult the log for full details
7 classes enhanced.
:processResources
:classes
:jar
:war

:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 4.53 secs

(Highlighted message to indicate use of the DataNucleus Enhancer)

The byte-code transformations directly use the transformer of the respective library in use except for the OpenJPA case, where the ant task of the enhancer is integrated. Some of the transformers issue some logging.

Switching Off

Additionally it is possible to switch of the byte code transformation for JPA annotated classes by adding to your build file

// build.gradle: 
enhancer.enabled=false

in case this might be necessary e.g. to only use the other parts of the plugin. Also EclipseLink, Hibernate, and OpenJPA support the use of plain Java classes without the byte-code transformation.

JavaScript and CSS Minification

When used in conjunction with the war plugin, CSS and JavaScript resources are automatically minified.
The plugin checks for resources with a filename extension .css for Cascading Style Sheets and .js for JavaScript. Matching resources are minified using the YUI Compressor.
It is not possible to minify resource included from archive files but only file resources local to your project. So contents from archives - while being included in the resulting web archive - cannot be minified. (We would expect WAR files to contain minified resources like WAR files generated using this plugin do.)

Web Application Underlying

The plugin introduces a configuration named webapp for modules using the war plugin. Dependencies given for this configuration are extracted into the resulting war artifact.

// build.gradle:
dependencies {
  webapp "tangram:tangram-core:$tangram_version:war@war"
  webapp "tangram:tangram-editor:$tangram_version:war@war"


  compile "tangram:tangram-core:$tangram_version"
  // Persistence API JPA
  compile "tangram:tangram-jpa:$tangram_version:nucleus"
  compile "org.datanucleus:datanucleus-api-jpa:$versions.datanucleus"
  compile "org.datanucleus:datanucleus-core:$versions.datanucleus"
  compile "$versions.jdo_api"
  compile "$versions.persistence_api"
  runtime "org.datanucleus:datanucleus-mongodb:$versions.datanucleus"

  compile "tangram:tangram-editor:$tangram_version"
  runtime "tangram:tangram-dinistiq:$tangram_version"
  runtime "org.slf4j:slf4j-log4j12:$versions.slf4j"


  providedCompile "$versions.servlet_api"
  providedCompile "$versions.jsp_api"
}


This process is not really described well if called overlay so I call in underlying, since your web application's directory in fact is the overlay so the other archives referenced and included must be an underlying.
If your WAR relies on the contents of another pre-packaged or incomplete WAR, the contents of the latter will be copied into your resulting web application while you can override any file in this archive from your local web application contents directory.

Version List

The plugin introduces a version object which collects some version strings for a number of libraries. This ensures that any project using the plugin can use these libraries with recent versions and version changes are applied in sync. The use of this part is optional and you have to explicitly use the versions in your build file since this cannot be applied transparently.
Some random examples:

dependencies {
  compile "org.pac4j:pac4j-openid:$versions.pac4j"

  compile ("org.apache.openjpa:openjpa:$versions.openjpa") {
    exclude group: 'asm'
  }

  compile "org.eclipse.persistence:org.eclipse.persistence.jpa:$versions.eclipselink"
  compile "$versions.persistence_api"

  compile "org.hibernate:hibernate-core:$versions.hibernate"
   
  compile "org.datanucleus:datanucleus-api-jpa:$versions.datanucleus"
  compile "org.dataucleus:datanucleus-core:$versions.datanucleus"
  compile "$versions.jdo_api"
  runtime "org.slf4j:slf4j-log4j12:$versions.slf4j"

  testCompile "org.testng:testng:$versions.testng"

  // your container will have this for you
  providedCompile "$versions.servlet_api"
  providedCompile "$versions.jsp_api"
}


Manual Mode

Of course it is still possible to call the methods performing the different tasks directly like described in the 0.9 plugin blog post. This should only be necessary if you e.g. want to enhance files in the unit test section of your code, which is considered a very rare case.

Samstag, 3. Oktober 2015

Gradle Plugin for JDO, JPA, Ebean etc in Tangram grown up

As part of the Tangram framework project a set of build related things went into a plugin for the Gradle build tool.
Only in part this plugin is directly related to Tangram but offers some generic tools suitable for many projects.
The plugin does
  • Support underlying of WAR files into others (similar to overlays)
  • Minify CSS and JavaScript codes to be placed in WAR artifacts
  • Byte-code transform model classes for JDO, JPA, and Ebean ORM layers
Up to version 0.9 the plugin was more or less a collection of utility methods. It is now redesigned to do the same job transparently when appropriate and at the appropriate stage of the build process.

Usage

Just a few lines have to be added to your Gradle build script to use these add on functionality:

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'tangram:gradle-plugin:1.0.0'
  }
}


apply plugin: 'tangram'

All of the following steps described here take place without any additional configuration.

Web application underlying

The plugin introduces a configuration named webapp for modules using the war plugin. Dependencies given for this configuration are extracted into the resulting war artifact.
This process is not really described well if called overlay so I call in underlying, since your web application's directory in fact is the overlay so the other archives referenced and included must be an underlying.
If your WAR relies on the contents of another pre-packaged or incomplete WAR, the contents of the latter will be copied into your resulting web application while you can override any file in this archive from your local web application contents directory.

Minification

When - again - used in a project also using the war plugin, all files included from the project directory will be checked for CSS and JavaScript file extensions. Matching files are minified using the YUI Compressor.
It is not possible to minify resource included from archive files. So contents from archives while being included in the resulting WAR cannot be minified. (We would expect WAR files to contain minified resources like WAR files generated using this plugin do.)

Handle Model Classes

When used with Java projects and when some data model classes are discovered, the plugin tries to prepare them for use with one of the ORM frameworks Java Data Objects (JDO), Java Persistence API (JPA), or Ebean.
The OR-Mappers require or recommend to apply byte-code transformations called "Enhancing" or "Weaving" to the class files for the data model related classes.
The plugin supports DataNucleus Access Platform (JDO and JPA), EBean, EclipseLink, Hibernate, and OpenJPA. All the calling options from the 0.9 version of the plugin have be removed and the APIs are auto-discovered so the whole process is now - except for logging output - transparent during build.

Version List

The plugin introduces a version object which collects some version strings for a number of libraries. This ensures that any project using the plugin can use these libraries with recent versions and version changes are applied in sync. The use of this part is optional and you have to explicitly use the versions in your build file since this cannot be applied transparently.
Some random examples:
dependencies {
  compile "org.pac4j:pac4j-openid:$versions.pac4j"

  compile ("org.apache.openjpa:openjpa:$versions.openjpa") {
    exclude group: 'asm'
  }

  compile "org.eclipse.persistence:org.eclipse.persistence.jpa:$versions.eclipselink"
  compile "$versions.persistence_api"

  compile "org.hibernate:hibernate-core:$versions.hibernate"
   
  compile "org.datanucleus:datanucleus-api-jpa:$versions.datanucleus"
  compile "org.dataucleus:datanucleus-core:$versions.datanucleus"
  compile "$versions.jdo_api"
  runtime "org.slf4j:slf4j-log4j12:$versions.slf4j"

  testCompile "junit:junit:$versions.junit"

  // your container will have this for you
  providedCompile "$versions.servlet_api"
  providedCompile "$versions.jsp_api"
}

Manual Mode

Of course it is still possible to call the methods performing the different tasks directly like described in the 0.9 plugin blog post.
Also it is possible to switch of the byte code transformation for JPA annotated classes by adding to your build file

// build.gradle: 
enhancer.enabled=false

in case this might be necessary e.g. to only use the other parts of the plugin.

Sonntag, 26. April 2015

Stock Apache Tomcat and Gradle on OpenShift

For my ongoing work on Tangram a wanted to use the RedHat OpenShift platform. In this post I'm going to outline the generic part of this which now is extracted as a Quickstart on github via https://github.com/mgoellnitz/openshift-tomcat-gradle-quickstart.
The requirements where the following: Build must be possible with Gradle and a plain Apache Tomcat webcontainer - not a JBoss Tomcat derivative - is needed. For the time being I still left out the option to use Jenkins, since the way to re-deploy on git-push OpenShift gives me, is quite usable.
So this results in the idea of a combination of the work of Shekhar Gulati at https://www.openshift.com/blogs/run-gradle-builds-on-openshift with the code at https://github.com/shekhargulati/gradle-openshift-quickstart and the tomcat Quickstart https://github.com/openshift-quickstart/openshift-tomcat-quickstart
Looking at the changes needed to use Gradle, I decided to integrate them manually and modify them along the way. Also the Tomcat Quickstart will not be used directly but serves as a template, since I don't want to pull the Apache Tomcat binaries into the git repository. As opposed to many other examples I came across, the installation of the RedHat cloud command line tools is completely left out. You might want to use them, but they are not needed for this example.
First of all I had to create an application (named "test") and had to take "DIY 0.1" as the type of application and clone the resulting repository.

git clone ssh://524e65...094@test-application.rhcloud.com/~/git/test.git/
cd test


This repository resembles the working directories of your application on OpenShift and is not just a development set of folders. So you will find some scripts here to interact with the infrastructure of OpenShift. These scripts have to be customized to fetch and configure Apache Tomcat and Gradle.
Now it's time to add script element to fetch the tool.

# .openshift/action_hooks/build:

# gradle build
set -x

DIR=`pwd`

# obtain tomcat
export TOMCAT_VERSION=7.0.61
if [ ! -f $OPENSHIFT_DATA_DIR/apache-tomcat-${TOMCAT_VERSION}.tar.gz ] ; then
  wget http://apache.../v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz
fi
if [ -f $OPENSHIFT_DATA_DIR/apache-tomcat-${TOMCAT_VERSION}.tar.gz ] ; then
  rm -rf tomcat
  tar xvzf $OPENSHIFT_DATA_DIR/apache-tomcat-${TOMCAT_VERSION}.tar.gz
  mv apache-tomcat-${TOMCAT_VERSION} tomcat
  cd $OPENSHIFT_DATA_DIR/tomcat
  rm -rf logs
  ln -s $OPENSHIFT_LOG_DIR logs
  cd $OPENSHIFT_DATA_DIR
fi

# obtain gradle
export GRADLE_VERSION=2.2.1
if [ ! -d $OPENSHIFT_DATA_DIR/gradle-${GRADLE_VERSION} ] ; then
  cd $OPENSHIFT_DATA_DIR
  wget http://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip
  unzip gradle-${GRADLE_VERSION}-bin.zip
  rm -f gradle-${GRADLE_VERSION}-bin.zip
fi

cd $DIR

cd $OPENSHIFT_REPO_DIR
export GRADLE_USER_HOME=${OPENSHIFT_DATA_DIR}gradle
export GRADLE_HOME=${OPENSHIFT_DATA_DIR}gradle-${GRADLE_VERSION}
export PATH=$GRADLE_HOME/bin:$PATH
if [ -f openshift-build.gradle ] ; then
 gradle --stacktrace -b build.gradle clean build
fi


Use at least version 2.2 of Gradle due to http://issues.gradle.org/browse/GRADLE-2871. Also Gradle needs some local directory on the target host.


# .openshift/action_hooks/pre_build:
 

# gradle storage dir build
set -x

if [ ! -d $OPENSHIFT_DATA_DIR/gradle ] ; then
  mkdir $OPENSHIFT_DATA_DIR/gradle
fi


The default stop action hook script contains some references on a ruby server and is missing a nice tomcat handling.


# .openshift/action_hooks/stop:
source $OPENSHIFT_CARTRIDGE_SDK_BASH

# The logic to stop your application should be put in this script.
if [ -z "$(ps -efww | grep tomcat | grep -v grep)" ] ; then
  client_result "Tomcat is already stopped"
else
  if [ -x $OPENSHIFT_DATA_DIR/tomcat/bin/shutdown.sh ] ; then
    $OPENSHIFT_DATA_DIR/tomcat/bin/shutdown.sh
  else
    kill `ps -efww | grep tomcat | grep -v grep | awk '{ print $2 }'` > /dev/null 2>&1
  fi
fi
exit 0



For safety reasons it might be a good idea to delete unexploded wars from the webapp directory of the tomcat installation


# .openshift/action_hooks/deploy:
set -x

# Addition:
for i in `ls $OPENSHIFT_DATA_DIR/tomcat/webapps/*.war`; do
  dn=`basename $i .war`
  echo "removing $dn"
  rm -rf $OPENSHIFT_DATA_DIR/tomcat/webapps/$dn
done


The start script has to be modified not to use ruby but the tomcat installation


# .openshift/action_hooks/start:
set -x
cp -rdp ${OPENSHIFT_REPO_DIR}/tomcat/conf/* ${OPENSHIFT_DATA_DIR}/tomcat/conf/
cp -rdp ${OPENSHIFT_REPO_DIR}/tomcat/lib/* ${OPENSHIFT_DATA_DIR}/tomcat/lib/
cp -rdp ${OPENSHIFT_REPO_DIR}/tomcat/webapps/* ${OPENSHIFT_DATA_DIR}/tomcat/webapps/
cd $OPENSHIFT_DATA_DIR/tomcat
sed -ig 's/OPENSHIFT_DIY_IP/'$OPENSHIFT_DIY_IP'/g' conf/server.xml
sed -ig 's/OPENSHIFT_APP_DNS/'$OPENSHIFT_APP_DNS'/' conf/server.xml
bin/startup.sh


After these preparation steps it would be time to integrate your application by providing a build.gradle script in the root path and all the needed source files.
When using a custom domain, let this domain point to your Applications hostname via a DNS CNAME record. Then create an alias with your domain name for the application using the RedHat Cloud command line tool. (Or create the alias via the console web application) Don't forget to add this domain name to your server.xml override.

Samstag, 30. August 2014

Tangram CoMA erwacht

Da ich nach einem langen Sommeurlaub nun wieder schwerpunktmäßig mit dem CoreMedia CMS zutun habe, habe ich mir auch im Tangram nach sehr langer Ruhe mal wieder die entsprechende Anbindung angesehen.

Ein altes Beispiele

Seit geraumer Zeit gibt es eine Beispielanwendung zum CoreMedia Adapter CoMA im Tangram Framework. Diese litt aber daran, daß das Aufsetzen der Umgebung dazu nicht ganz trivial war. Alleine schon deshalb konnte ich nicht soviel Energie in die Weiterentwicklung stecken.
Für diese Anwendung stehen Möglichkeiten von Tangram, Content einzugeben, nicht zur Verfügung, da der CoMA nur lesend arbeitet. Um dennoch Inhalte und ein paar Templates zum Zeigen zu bekommen, nutze ich die uralte Beispielanwendung MenuSite von CoreMedia selbst.
Man benötigt also zum Nutzen der Anwendung eine Datenbank, wie sie ein CoreMedia Content Server für die MenuSite angelegt und mit Daten befüllt hätte.
Bisher waren die Schritte, die nötig waren, um diese Datenbank aufzusetzen nur zur Ausführung beschrieben. Alle andere Beispiele kann man einfach bauen und starten.

Neue Server

Meine Experimente mit Gradle zum Zusammenbau von CoreMedia Softwarekomponenten ohne das bei mir nicht sehr beliebte Maven haben allerdings nun einen Stand erreicht, in dem man Skripte und kleine Workspaces präsentieren kann, mit denen man die benötigte Datenbank erstellen kann.
Die Content Management Server Webanwendung unter
https://github.com/mgoellnitz/cm-cms-webapp
und die Content Management Server Tools unter
https://github.com/mgoellnitz/cm-cms-tools
Die harte Nuß dabei war weniger der Server als das Tools Paket. Aber ohne die Tools konnte ich die Daten natürlich nicht in den Server importieren. Außerdem hat das von mir gewählte Tomcat Plugin für Gradle sich nicht von der freundlichen Seite gezeigt. Mittlerweile weiß ich, daß auch andere Anwendungen als der CoreMedia Content Server unter dem falsch zusammengestellten Classpath leiden.

Und alles ohne Maven

Die genaus Beschreibung findet sich im Beispiel Repository zu Tangram.

Sonntag, 13. Juli 2014

Byte-Code Transformation is no big deal

To avoid another language in my web projects I'm using Java as the design language for the Objects to be persisted as well. It seemed easy to use with the different ORM Standards and implementations available.

Take me from Java to the Database

Many ORM implementors tend to recommend using a byte-code transformation process to make the classes usable in their respective persisting contextes (e.g. http://www.avaje.org/doc/ebean-userguide.pdf - Chapter 15). This in fact means, after you did your job of coding and compiling the classes, some other component takes this code and transforms it into some other code additionally dealing with the ORM/Database related stuff.
The idea is, to avoid runtime penalties or the generation of subclasses dealing with the additional database related issues which would show up at runtime potentially screwing up your idea of the class hierarchy. (Which it did for me. See below.)

Why class weaving or enhancing is a big deal

Of course this still means that you are running code, you don't now in detail.
The assumption of any of the ORM framework authors is, that the byte-code transformation process can be easily automated and as far as possible be hidden from the application developer. JPA based JEE applications are expected to do the transformation at deployment time to the container - so this doesn't even happen within your development tool-set.
My experience is a different story. And the hiding of things during development once again was no good idea for me. 

The easy Start

I started with the Eclipse IDE some years ago and the Google App Engine Plugin. It does the byte-code transformation for the JDO implementation from DataNucleus automatically at compile time. This worked fine as long as I was coding in "play-around" mode. When the code started to grow into modules, from time to time the classes were propagated to the client module unenhanced. This is were I learned what the use of DataNucleus feels like, when in fact just the transformation is missing (of course it doesn't tell "you missed to transform classe this-and-that"). I got around these issues with the dumb "clean nearly everything in your work environment" pattern.

Build System Integration

Things got even more complicated when I started to write build scripts, since the project grew and was supposed to be published. You don't want to give anyone a 20 page description on how to setup the IDE just as you did. You simply give friends a script which describes the necessary parts in human and machine readable form. So the project gets cloned from the source and a simple build tool call - hopefully in default mode with no to few options -  will create a usable result.
But the promise to support me as a developer from the ORM provider still holds true for these situations. I was just expected to change the way I was using the transformation tool. DataNucleus comes with an ant task, a compiler plugin and so on. Since I didn't want to use the obsolete legacy tools Maven or ANT (hey, why not use make or punch cards?) I "simply" plugged in the compiler plugin since there is no direct support for Gradle and the integration of the ANT task was not that easy at the initial try.
First of all this compiler plugin was not able deal with all of the versions of the Oracle Java Compiler and all language levels beyond "Java 6" so I had to prepare the source code carefully.
This gave me enhanced classes and sure the enhancer was running, but...
When packaging the classes to JARs as build systems tend to do after compilation nearly automatically, those classes where unusable again with the error messages I was already familiar with.

Unit-Testing the Byte-Code Transformation instead of my Code

At this point in time I started writing JUnit tests to test, if my build environment was working and not to test if my code was correct. This gave me the impression that some things are going wrong.
I learned, that the compiler plugin took some time after compilation before it started to "enhance" (byte-code transform) the class files. It used some sort of threading for this so that Gradle already had packaged the jar files, before the process was completed. I started to add some 10s waiting to my build scripts. Argh...

Refactoring - Get the same Thing you already had

I took a second look at the DataNucleus Enhancer's ANT task to integrate this into the build process as a Gradle task without those eratic 10s of waiting. I also needed this step since I was updating DataNucleus from the old version used in the Google App Engine at that time to a newer one also meant for stand-alone use.

Use other APIs as well like they were simple Libraries

After all these pieces were working, I started playing around with the Java Persistence API JPA. Also the implementations of JPA I came accross - OpenJPA, EclipseLink, and again DataNucleus - recommended the use of byte-code transformations called Enhancement (OpenJPA and DataNucleus) or Weaving (EclipseLink).
The integration of that many APIs and byte-code transformers made things more complicated again, while the code I wrote still is not that complicated. It's just the byte-code transformation which adds to the complexity. I needed to present OpenJPA, EclipseLink, and DataNuceus Versions of my single JAR archive with only very few classes and only two of them needed to be byte-code transformed. Additionally with JPA I have the option to use the original classes without byte-code transformation in some scenarios with certain limitations (Only DataNucleus is really capable of a automatic discovery of available classes for database access, the others need detailed lists passed over to the implementation in different ways. This is anything but portable!)

Stop pretending it is easy and write a decent Tool to do the Job

Since not all of the implementations can be on the compile time classpath of the JPA relying portions of my project, it was now time - just because of the necessary byte-code transformations - to write a Gradle Plugin dealing with this.
Very easily this plugin was generic enough to be used in any project using JPA, JDO, or Ebean as the ORM Solution for Java and the Gradle build tool.
Two third of the work on the build-scripts of the Tangram dynamic webapplication framework were related to the byte-code transformations over the last five years.

Conclusion after some Years

So my best friend now is OpenJPA which can relatively easy be used without transformation. Yes, it presented me the nice subclassing issue where I am at runtime dealing with subclasses of the classes I designed myself, but this was solvable with half a dozen lines of code.
My second best friend is DataNucleus where I am now able to integrate the byte-code transformer into the runtime environment of my framework and write JDO annotated classes in groovy, put the code into the JDO based database layer itself and thus be able to extend the object oriented storage at runtime. This is what adds very nicely to Stylesheets, JavaScript Codes, URL-Formats and Business Logic in Groovy in the Database layer resembling the dynamic part of Tangram. I tried this with the OpenJPA Enhancer and EclipseLink Weaver as well but with no success.
Also I now got a code base which was easily extended with another ORM Solution called EBean.  It was meant as an option with a smaller footprint but does not present any advantaged over the other options already implemented and proven in real projects live on the web using the Tangram dynamic web application framework.
So, anyone still thinks that byte-code transformation is a non-issue as you may read on introductory web pages on ORM? Give me some 30s to make your build process break - at least every once in a while when you don't expect it and won't easily discover the source of your pain.
But in the end with my Gradle based plugin, things are definitely a lot easier and reliable - again after a lot of work with things that were supposed to be easy, automatic, or hidden from me.

Montag, 23. Juni 2014

CoreMedia CMS und Gradle - just for the LOLs

Um das tangram-coma Modul wieder mehr in meinen Fokus zu bringen, brauche ich zum Testen immer einen CoreMedia CMS Content Server als backend.

Bisher war dieser Server entsprechend einer Anleitung und mit der Lieferung des Produktes als ZIP-Datei von Hand herzustellen. Das paßt natürlich nicht so richtig in die Tangram Beispiele, die in sich abgeschlossen sein sollten, und die bisher genutzte Version CMS 2008 (5.2) läuft nun unwidderruflich auch aus.

Mit den aktuellen Versionen wird das Produkt handlich in Form von Artefakten in einem Maven-Repository geliefert und es stehen Maven-Module zur Verfügung, daraus komplette Server zu erstellen, zu customizen und zu bestücken.

Leider ist dieser Bereich eben noch mit Maven formuliert und außerdem müßte ich mich dann - und dafür ist es ein wenig früh - vom Minimalbeispiel MenuSite verabschieden.

Also habe ich mich gefragt, ob ich fit genug bin, die Baupläne von CoreMedia mal im ganz kleinen nach gradle zu übersetzen und mir so den Content Management Server mit Build-Script in die Tangram Beispiele zu integrieren. Dabei habe ich natürlich wieder einen Dienst menr aus der Cloud als Entwicklungsunterstützung hinzugezogen, wie es zum Entwicklungsmodell von Tangram am besten paßt: Gut funktionierende Dienste und Komponenten nutzen. Da ich mit CoreMedia in der aktuellen Version hsqldb nicht mehr nutzen kann und nicht mehr wie im bisherigen Beispiel postgresql lokal installieren und nutzen wollte, habe ich mir mal schnell eine Testdatenbank bei DB4Free besorgt, da das "default" Datenbanksystem im CoreMedia CMS derzeit MySQL ist.

Um's kurz zu machen: Das Zusammenstellen eines Content Management Servers geht ganz wunderbar einfach und auch die Maven-Vorlagen sind - für Maven's Verhältnisse - relativ kompakt (d.h. unlesbar, häßlich, langatmig aber nicht lang). Den Prototyp für einen komplett Maven-freien und nicht Tangram-bezogenen Content Management Server gibt's ab heute unter

https://github.com/mgoellnitz/cm-cms-webapp

Und mit diesem Werkzeug gehe ich nun bei Tangram in die nächste Runde. Außerdem sieht man, daß man langsam den Weg weg von Maven (unter Beibehaltung der großartigen Dependency und Repository-Bereitstellung) beschreiten kann hin zu Gradle. Das ist kein harter Schnitt, das ist ein Weg, sodaß ich nun auch im CoreMedia Kontext keinen Neubau mit Maven-Syntax beginnen werde. Gradle verspricht, uns dabei zu unterstützen, da sie die installierte Maven-Basis im Blick haben.

Donnerstag, 21. November 2013

Gradle Plugin for JPA, JDO, and EBean Bytecode Transformers

The plugin referenced here is meant for use with the tangram framework but there are a few calls which are helpfull for any user of Gradle and
It simply has easy to use wrappers for the enhancer/wever components for the persistence frameworks above since the solutions presented by those projects each for its own reason needed some cosmetic enhancement to fit into my gradle builds.

Prerequisites

Of course you will have some classes with persistence annotations. I'm assuming that you have a (sub)module containing the model classes for the API to be used (JDO, JPA with OpenJPA, DataNucleus, or EclipseLink, or Ebean with JPA annotations) which get compiled by some task of your project.

Preparation

The plugin can be obtained from the tangram snapshots repository.

// build.gradle
buildscript {
  repositories {
    mavenCentral()
    maven { url "http://repository-tangram.forge.cloudbees.com/snapshot" }
  }
  dependencies {
    classpath "tangram:gradle-plugin:0.9-SNAPSHOT"
  }
}


And of course it must be applied

apply plugin: 'tangram'

When using EclipseLink the bytecode transformer called weaver will already be contained in the compile dependencies of your module. The same applies for OpenJPA except that here the bytecode transformer is called enhancer.
For DataNucleus und Ebean the bytecode transformer is called enhancer again and is a dependency of the plugin itself. There is no generic need to have it in the compile dependencies of your module.
So in short, you will not have to modify your dependencies and the resulting package will not contain any additional classes or jars.

Solution

The plugin does not introduce any new tasks but just some methods that can be placed anywhere in the build process. In a standard gradle task-wiring of the java plugin the methods the following locations make sense:

// JDO with DataNucleus
compileJava.doLast {
  nucleusJdoEnhance()
}


// JPA with DataNucleus
compileJava.doLast {
  nucleusJpaEnhance()
}


// JPA with OpenJPA
jar.doFirst {
  openjpaEnhance()
}


// Ebean
compileJava.doLast { 
  ebeanEnhance()
}


// JPA with EclipseLink
compileJava.doLast {

  eclipselinkWeave()
}


But you may decide to use it at other points within your build process.

Background

For OpenJPA the wrapper presented here is a simple wrapper for the ant tasks provided by this project wired up for the given gradle build setup.
For EclipseLink the weaver the jar is included as a dependency for the plugin itself and the weaver is called called via its Java API. So woven codes can be generated independent of the using projects build setup.
For DataNucleus and Ebean the solution is somewhat more complicated since the enhancers are in separate jars which you most likely don't want to include in your resulting packages.
So those jars are also included as a dependency of the plugin, and again the plugin itself calls the Java APIs of the enhancers directly.

Mittwoch, 13. November 2013

mavenLocal() - remote and clean

Working in the cloud even for development tasks oftentimes needs what used to be the local maven artifact repository – referred to as mavenLocal() – available somewhere remote, accessible by your cloud continuous integration server.

The usual Suspect

A very easy way is to use an e.g. WebDAV accessible folder somewhere for every days snapshots.
For gradle users this has two drawbacks and for all others still at least one:
You will get a bunch of snapshots over time and the housekeeping there is as time consuming as with your local maven artifact repository, which – from time to time – needs some cleaning to avoid unreproducable build on your machine.

Cloudbees humming to a Gradle Blues

This is where my latest suggestion comes in: The cloudbees forge.
This is still a more or less normal WebDAV accessible storage but it has one important feature: Just with a check box in the administration panels you can ask for snapshot clean up to be done for you.
The one additional problem for Gradle users is the fact, that the latest maven-publish plugin from the distribution cannot publish to WebDAV resources until http://issues.gradle.org/browse/GRADLE-2919 is resolved.

Cloudbees Forge cleans my local Repository

As a workaround I'm publishing to a local folder and using a synchronisation software (https://github.com/mgoellnitz/JFileSync3 or AllwaySync) to bringt the stuff online. This in turn has the advantage that the clean up of cloudbees hums over to my local drive. Thus I'm not really sure if I'm waiting for a solution to the Gradle WebDAV publish problem...

Tangram Snapshot Artifact Repository

As a result I now – without any additional effords on my side – present public snapshots of the tangram system.

Tangram Snapshot Maven Artifact Repository now to be found at:
https://repository-tangram.forge.cloudbees.com/snapshot

And I myself am using these on any cloud platform I'm trying some remote build on, still having the latest changes for these plattforms available. All this avoiding the need to release to my old Ad-hoc Maven Artifact Repository at

http://my-amor.appspot.com/repository/

which still holds the releases.
I expect to be using the cloudbees solution for my releases some day soon as well. It's way easier to handle.

Donnerstag, 3. Oktober 2013

Danke Gradle!

Evtl. bin ich nach über 25 Jahren professioneller Softwareentwicklung und Projektgeschäft etwas abgebrüht, desillusioniert und blind gegen die Schönheit des Neues geworden, aber ich finde Maven nicht besser als ANT und ANT nicht grundlegend mächtiger als make. Bisher habe ich noch mit jedem Build-Werkzeug meine Ziele erreichen können und mich dabei immer in dem Sinne verrenken müssen, daß ich nie aufscheiben konnte, was ich eigentlich meine.

"Das war schon immer so", "Da kann ja jeder kommen" und andere Leitsätze

Egal, ob es make, ant oder maven waren: Meist bestimmte das Werkzeug den Build und nicht die Entwickler oder die Art der Software, die es zusammenzubauen galt. Im wesentlichen nutze ich die Werkzeuge daher immer nur deshalb, weil sie Verfügbar im jeweiligen Kontext sind, oder weil es einfach eine breite Masse von Nutzern gibt, sodaß man nicht so alleine ist.
Im Moment nutze ich in jeden professionellen Projekt Maven und weiß daher, welche Kosten es entfesselt, um seinen Nutzen zu bringen. Den Nutzen der vernünftigen Abhängigkeitsumsetzung will ich nicht missen, aber der Rest ist weder gut lesbar noch gut im Ablauf verständlich. Ich finde die Schreibeweisen nach wie vor unleserlich und das Werkzeug unverständlich in seiner Sprache und Komplexität.

Peak Maven?

Nachdem ich also nun viele Jahre lang leidenschaftslos beim Buildwerkzeug war, sehe ich jetzt auf die umfassenden Lösungenmit Maven in großen Projekten (wie dem CoreMedia 7 Blueprint) wie auf die letzten Werke des Maven-Barock. Großartige umfassende Lösungen, die mir netto sehr helfen (weil ich sie nicht selbst schreiben mußte), Maven und das Projekt im Griff zu halten, und die so vieles bedacht haben, das ich nicht einmal bemerke.

Kleine Danksagung zum 1.8er Release

Den Grund, warum sich meine Sicht auf Buildwerkzeuge nun geändert hat, möchte ich anhand einer kleinen Begebenheit illustrieren: Immer wenn ich kleinere Softwareprojekte anderen Menschen bereitstellen will, reicht es nicht, daß ich reprodzierbar aus meiner IDE getestet eine Software purzeln lassen kann. Bevor ich also beschreibe, was ich getan habe und was eventuell bei anderen dann gerade einmal nicht genauso aussieht, schreibe ich lieber gleich die notwendigen Build-Skripte, um den Vorgang zu dokumentieren und dabei gleich zu automatisieren.
Bei tangram habe ich mich noch lange mit der Auswahl des Werkzeugs gequält, weil ich mich nicht auch noch in meiner Freitzeit mit Maven herumschlagen wollte und kein anderes Tool mir spontan unter "das machen alle so" oder "das war einfach da" vorlag. Letztlich hat Gradle über Maven gewonnen und ich habe mich ahnungslos in die Umsetzung gestürzt. Heute verstehen wir uns recht gut.
Als ich vor kurzem wieder ein paar Zeilen Code herausgeben wollte, stand ich mit einem kompletten Fremdprojekt und einem alten ANT-Script da. JFileSync sollte - weit vor der NSA-Affäre, die mir keine neuen Erkenntnisse brachte - um für mich notwendige Features erweitert werden. Das Ergebnis wollte ich wieder gerne reprodzierbar als nutzbares Programm erzeugen können. Und das ging mit Gradle so erschreckend gut und einfach - und mit wenigen Worten, daß ich mich gleich wieder in Goodies für ein schöneres Ergebnis stürzen konnte.

Gradle-Deutsch / Deutsch-Gradle

Wir haben also ein Java-Programm

apply plugin: 'java'

Und wir hätten den Code gerne sauber in aktueller Java-Version, mit UTF-8 und ohne alten Kram.

sourceCompatibility = 1.7
targetCompatibility = 1.7
compileJava.options.encoding = 'UTF-8'
compileJava.options.deprecation = true

Der Source-Code und die Ressourcen finden sich zusammen im Verzeichnis src.

sourceSets {
  main {
    java {
      srcDir 'src'
    }
    resources {
      srcDir 'src'
    }
  }
}

Damit man das ganze kompilieren kann, muß man aus diesen Quellen

repositories {
  maven { url "http://repo1.maven.org/maven2" }
  maven { url "http://sardine.googlecode.com/svn/maven/" }
  maven { url "http://repo2.maven.org/maven2/org/bouncycastle" }
}

diese Dinge besorgen

dependencies {
  compile 'org.slf4j:slf4j-api:1.5.8'
  compile 'org.slf4j:slf4j-log4j12:1.5.8'
  compile 'org.apache.httpcomponents:httpclient:4.2.5'
  compile 'commons-lang:commons-lang:2.6'
  compile 'org.apache.commons:commons-compress:1.3'
  compile 'org.bouncycastle:bcprov-jdk16:1.46'
  compile 'javax.servlet:servlet-api:2.5'
}

Ach, und ich würde gerne weiterhin Eclipse nutzen

apply plugin: 'eclipse'

Könnt Ihr Euch bitte auf ein Vereichnis für den Output einigen?

eclipse.classpath.conventionMapping.defaultOutputDir = { 
  new File(project.projectDir, 'build/classes/main')
}
Du solltest aber sauberer Arbeiten als meine IDE!

defaultTasks 'clean', 'build'

Das ganze soll am Ende ein benutzbares Programm werden.

apply plugin: 'application'

Den Einstieg findest Du dabei in dieser Java-Klasse:

mainClassName = 'jfs.JFileSync'

Wow! Du hast ja automatisch Start-Scripts für Unix und Windows erzeugt! Danke! Ich hätte da noch ein paar Verbesserungsvorschläge...

startScripts {
  doLast {
    // Add some more reasonable memory settings to JFileSync3
    unixScript.text = 

      unixScript.text.replace('DEFAULT_JVM_OPTS=""', 
                                     'DEFAULT_JVM_OPTS="-ms1280m -mx1536m -Xms1280m -Xmx1536m"')
    windowsScript.text = 

      windowsScript.text.replace('DEFAULT_JVM_OPTS=', 
                                        'DEFAULT_JVM_OPTS=-ms1280m -mx1536m -Xms1280m -Xmx1536m')
    windowsScript.text = windowsScript.text.replace('java.exe', '%JAVA_CMD%')
    windowsScript.text = 

      windowsScript.text.replace('@rem Find %JAVA_CMD%', 
                                 'set JAVA_PREFIX=start ... java.exe')
    windowsScript.text = 

      windowsScript.text.replace('"%JAVA_EXE%" %DEFAULT_JVM_OPTS%', 
                                 '%JAVA_PREFIX% "%JAVA_EXE%" %DEFAULT_JVM_OPTS%')
  }
}
Und ich habe da gerade diesen netten Launcher für Java-Anwendungen unter Windows gefunden

buildscript {
  repositories {
    maven { url "http://repo.smokejumperit.com" }
    ivy {artifactPattern 'http://gradle-launch4j.googlecode.com/files/[module]-[revision].[ext]'}
  }
  dependencies {
    classpath 'com.smokejumperit:gradle-plugins:0.8.2'
    classpath 'edu.sc.seis:gradle-launch4j:1.0.5'
  }
}

den will ich haben!

apply plugin: 'launch4j'

Schreib doch bitte einfach rein, wer das gemacht hat, danke.

launch4j {
    mainClassName = project.mainClassName
    version = '3.0.0'
    copyright = '(C) 2002-2013, J. Heidrich, M. Goellnitz'
    downloadUrl = 'https://www.dropbox.com/s/3n4snlbw9tyjgec/JFileSync3.zip'
    supportUrl = 'https://github.com/mgoellnitz/JFileSync3'
    icon = "$project.projectDir/win/JFileSync.ico"
    dontWrapJar = true
    xmlFileName = 'JFileSync3-launcher.xml'
    initialHeapSize = 1024
    maxHeapSize = 1536
}
Ach, und wo Du gerade da bist: Pack das doch noch mal etwas anders ein, als sonst

distZip {
  into(project.name) {
    from "$buildDir/launch4j"
    include '*.exe'
    include '*.xml'
  }
  into(project.name) {
    from '.'
    include 'legal/*.*'
    include 'profiles/*.*'
    include 'README.md'
  }
}

Oh, äh. Testen. Machst Du das noch schnell? Ich habe da vier Beispiele abgelegt.

apply plugin:com.smokejumperit.gradle.ExecPlugin

task(encryptionTest) << {
  // Extract distribution
  println "Extracting distribution"
  ant.unzip(src: "$buildDir/distributions/${project.name}.zip", dest: "$buildDir")
  // Extract test data
  println "Extracting test data"
  ant.unzip(src: "test/test-folders.zip", dest: "$buildDir/${project.name}")

  println "Encrypting"

  String cmd = "";
  cmd = "cmd /c bin${File.separator}${project.name} -config 1encrypt.xml -nogui -nohistory -quiet"
  project.exec(cmd, "$buildDir/${project.name}")

  println "Decrypting taking every folders metadata into account"
  cmd = "cmd /c bin${File.separator}${project.name} -config 2decrypt1.xml -nogui -nohistory -quiet"
  project.exec(cmd, "$buildDir/${project.name}")

  println "Decrypting ignoring every folders metadata"
  cmd = "cmd /c bin${File.separator}${project.name} -config 3decrypt2.xml -nogui -nohistory -quiet"
  project.exec(cmd, "$buildDir/${project.name}")

  println "And now please compare by hand"
  cmd = "cmd /c bin${File.separator}${project.name} -config 4compare.xml -nohistory"
  project.exec(cmd, "$buildDir/${project.name}")
}


Macht das bitte nicht nur in der Kürze sondern auch in so kurzer Zeit und so, daß man es immer wieder lesen kann, mit anderen Werkzeugen. Für mich ist Gradle nun weiterhin die erste Wahl in allen Projekten, in denen ich die Wahl habe.

Samstag, 22. Dezember 2012

Das war nicht gut

Neulich habe ich hier mal einen Workaround mit Gradle für das Classpath-Problem mit dem YUI-Compressor beschrieben.
Dieser Workaround funktioniert solange sehr gut, wie man in den Web-Archiv-Artefakten alle Java-Bibliotheken mitschleppt und dann beim Kopieren daraus auswählen und umbenennen kann.
Genau das habe ich gestern mit der Version 0.7 von Tangram aber endlich eingedämmt, da man jede Menge Code und damit Daten ständig von A nach B schiebt. Man kann eigentlich all die JAR-Dateien einfach weglassen. Sie kommen automatisch durch die Abhängigkeiten der restlichen Codes wieder rein. Alle JARs? Ein kleines, problematisches Archiv leider nicht: der YUI-Compressor. - Also habe ich die Mimik mal wieder umgebaut.
Dazu basteln wir uns eine weitere Configuration, wie das für die Google App Engine schon für das enhancen der JDO-Klassen notwendig war:

configurations {  .
  .
  .
  yui
}

Mit dieser Konfiguration belegen wir nur den YUI-Compressor:

dependencies {
  .
  .
  .
 providedCompile "com.yahoo.platform.yui:yuicompressor:$yui_version"
 yui "com.yahoo.platform.yui:yuicompressor:$yui_version"
  .
  .
  .
}

Er wird also aus den Abhängigkeiten entfernt und nur in der Konfiguration genutzt. Und die Elemente dieser Konfiguration können wir nun wieder beim Kopieren umbenennen, wen man das abschließende into('') etwas tuned.

war.doFirst {
  .
  .
  .
  for (f in configurations.yui.files) {
    copy {
      from f.absolutePath
      into "$buildDir/target/WEB-INF/lib"
      rename 'yui', 'aaa-yui'
    }
  }
 
  into ('') {
    from "$buildDir/target"
    exclude 'WEB-INF/lib/tangram-**'
  }
}
Und damit wäre dieser Bereich auch wieder heil und kann wieder als Vorlage für entsprechende Fälle mit dem YUI-Compressor und Gradle dienen.

Freitag, 21. Dezember 2012

Artefakte mit viel Liebe

Es liegt ganz sicherlich an mir, daß ich zwar nun für fast alles, was man so zu Entwicklung und Betrieb braucht, dienste in der "Cloud" gefunden habe, aber das Bereitstellen von Bibliotheken via Maven Repository noch nicht hingebekommen habe.
Aber im Grunde - so von der lesenden Seite - sind das ja nur Verzeichnisstrukturen mit einigen festgtelegten Dateien darin, die man per HTTP zugreifen kann. - Und das solle ist: Alle Daten dazu habe ich ja schon lokal auf der Platte, wenn ich meinen Build - bei mir ja immer mit Gradle - abgeschlossen habe.
Bis ich also verstanden habe, wie ich meine Projekte in den Prozeß in die Zentralen Maven Repositories einkippe, habe ich eher mal eben heute für die Zeit bis dahin ein Ad-hoc Maven Object Repository mit dem Tangram - in Version für die Google App Engine - geschrieben und es deshalb amor genannt.
Was mußte man dafür tun? Es gibt nur ein Datenmodell: Das Artefakt, wie es im Repository zugänglich gemacht werden soll. Der Rest ist URL-Format und eine kleine Listing-Seite - und damit sind das nur noch Dinge, die man dynamisch im Tangram mal schnell hinzufügen kann. Der Code findet sich auf github - mit Datenbank Abzug diesmal.
Die Beispiel-Site ist nun das Repository auf dem ich das aktuelle Tangram 0.7 zur Verfügung stelle: http://my-amor.appspot.com/

Donnerstag, 20. Dezember 2012

Neuer Frühling im Winter


Eventuell können wir die Diskussion ein wenig aufschieben, ob es nicht eigentlich kalendarisch noch Herbst wäre, aber es liegt nun einmal genug Schnee und trotzdem ist das Spring Framework in Version 3.2 herausgegeben worden.
Diesen Text könnte ich glatt auch einfach auf meine TODO Liste schreiben. Ein nettes, aber nicht aufregendes Update für das Spring Framework. Es ist ein extrem Umfangreiches, nicht immer komplett intuitiv und einfach zu benutzendes Framework, aber es tut einfach an so viele Stellen das, was man - mindestens im Web-Kontext - so braucht und wird von so vielen Menschen benutzt, daß ich für Tangram und fast alle kommerziellen Projekte irgendwo Spring im Einsatz habe.
Also sollte Tangram auch mal so schnell wie möglich den neuen Unterbau nutzen - wenn das mit Spring Security für den RDBMS-Bereich vereinbar ist. - Dort stehen wir im Moment noch auf dem "Development Release"  M1. Die Sanduhr tickt aber schon.
So richtig schön wird es, wenn man dann noch sieht, daß auch das Spring Framework in Sachen Source-Code-Verwaltung und Bau genau da angekommen ist, wo Tangram ganz klein und in der Ecke auch liegt: github und dem Build Tool gradle. Die Mavenfinsternis scheint langsam zu beginnen, wenn man auch in diesen Dimensionen sich auf das neuere Tool stützen kann.
Jetzt warte ich eigentlich nur noch darauf, daß die Google App Engine sich auf die Servlet Version 3 einschießt, damit man auch die qualitativ neuen Features dort gut nutzen kann.

Montag, 3. Dezember 2012

Singing the YUI Blues

Im Web gehört es ja zum guten Ton, spätetens seit Google die PageSpeed Tools herausgebracht hat, zusätzlich zur Kompression des HTTP-Inhaltes CSS und JavaScript zu "minifizieren" (ich sage mal lieber minimieren).

Es gehört sogar nicht nur zum guten Ton sondern bringt meßbar bessere Reaktionen in den Browsern und vermindert für Betreiber größerer Websites signifikant die Traffic-Menge. Das alte Argument, daß nachbearbeiten wäre zu CPU-belastend, gilt heute wirklich nicht mehr, wie ich an einer der größten deutschen (europäischen?) Website erfahren und messen konnte. Hier hat man eher Probleme, es nicht zu tun, da irgendwann auch GBit-Bündel von Netzanschlüssen am Kapazitätsende ist - nicht jedoch die CPUs.

Im Tangram sind natürlich alle statischen Dateien im Bereich CSS und JavaScript bereits beim Zusammenbau minifiziert und beim Ausliefern sollte der JSP/Servlet Container komprimieren - oder ein HTTP-Server davor. In der Google App Enginge erledigt das die Infrastruktur von Google auf irgendeiner Ebene für uns ebenfalls automatisch mit. (Und wenn auch die es im großen tun, ist es wohl wirklich kein Lastproblem mehr...) Für Inhalte aus dem Repository ist eine Lösung enthalten, die das CSS oder JavaScript zur Laufzeit minimiert.

Obwohl man das Minimieren dadurch mit dem Komprimieren des gesamten HTTP-Inhaltes verwechseln kann, wird es auch gerne "Kompression" genannt und ist so der Namensgeber des YUICompressors. Dieser ist - s.o. - lange im Tangram integriert, aber nie ohne Probleme, die wir hier endlich einmal nicht elegant aber funktionierend lösen müssen.

Der YUICompressor bietet sehr gute Ergebnisse, ist eigentlich einfach zu benutzen und sehr weit verbreitet. Damit wäre alle in Ordnung und erledigt, da er auch eine einfach zu benutzende Programmierschnittstelle hat, die im Tangram sowohl im Buildsystem als auch in der späteren Ausspielung zur Laufzeit genutzt wird.

Der erste unangenehme Punkt ist, daß aktuelle Versionen des YUICompressors (mindestens für mich) bisher nicht in öffentlichen Maven-Artefakt-Repositories zu finden sind und wir so mit einer nicht ganz neuen Version unterwegs sind.

Der zweite Punkt tut mehr weh: Im JAR-Archiv des YUICompressors befinden sich Klassen, die einige Klasse aus der eingesetzten JavaScript-Bibliothek - einer ebenfalls älteren Fassung von Rhino - überschreiben sollen. Das ist so natürlich eine recht dreckige Lösung, und sie funktioniert auch normalerweise nicht. Das liegt ganz einfach daran, daß im classpath ein JAR mit Namen yui... selten vor einem JAR mit Namen js... zu finden ist. Eine relativ saubere Behandlung der dreckigen Lösung wäre das umschreiben der Archive beim Zusammenbau des Tangram.

Da aber Tangram eher die möglichkeiten bestehender Software nutzen als zu viel Räder neu erfinden soll, Greifen wir lieber in den Werkzeugkasten und sehen, was unser Werkzeugbestand leisten kann.

In diesem Fall ist Gradle - endlich einmal wieder - unser Freund und bietet Möglichkeiten zur Umbenennung beim Zusammenbau. Da alle Zielsysteme den classpath alphabetisch sortiert zusammestellen, reicht es für uns aus, den YUICompressor einfach ein wenig nach vorne zu schieben. Damit sind dieselben Klassennamen immernoch zweimal im classpath vertreten, aber es wird - Erfahrung im Tangram und großen Systemen (s.o.) - nur die funktionierende Version genutzt.

Damit alle Abhängigkeiten im Buildsystem sauber formuliert bleiben, kleben wir unser Pflaster nur in den Anwendungen auf, die mit Tangram gebaut sind - nicht im System selbst.

Im Detail sieht das so aus:

Wir müssen nun den Anwendungen die genaue Version bekannt machen, die bisher nur im System einfach enthalten war:

ext.yui_version="2.4.6"

Das entsprechen JAR lösen wir aus dem automatisch zusammegestellten classpath heraus:

dependencies {
  .
  .
  .
  // To exclude original YUICompressor jar from output war
  providedCompile "com.yahoo.platform.yui:yuicompressor:$yui_version"
  .
  .
  .
}

Jetzt sind wir sowohl den YUICompressor als auch seine Dependencies los und müssen diese nun "von Hand" wieder einfügen. Da tun wir im war.doFirst Zusatz, der in Tangram-Anwendungen "traditionell" schon angepaßt ist:

war.doFirst {
  // Strange way of overwriting things - it must be the first webapp dependency
  if (configurations.webapp.dependencies.size() > 0) {
    String archiveFileName = configurations.webapp.asPath
    int idx = archiveFileName.indexOf(';')
    if (idx >= 0) {
      archiveFileName = archiveFileName.substring(0, idx)
    } // if
    println "unzipping $archiveFileName"
    ant.unzip(src: archiveFileName, dest: "$buildDir/target") 
  } // if
 
  // Missing: YUICompressor - see base system
  // It's not really that important since we don't have any
  // static Stylesheets or JavaScripts are in the examples.
  copy {
    from 'src/main/webapp'
    into "$buildDir/target"
    include '**/**'
  }
 
  // ...and to again include those YUICompressor's dependencies with a different name

  copy {
    from "$buildDir/target"
    into "$buildDir/target"
    include '**/yui*'
    rename 'yui', 'aaa-yui'
  }
  into ('') {
    from "$buildDir/target"
    include 'WEB-INF/lib/aaa-*'
    include 'WEB-INF/lib/js-*'
  }
 
  into ('') {
    from "$buildDir/target"
    exclude 'WEB-INF/lib/**'
  }
}

Hier wird die WAR-Depencency ausgepackt, ggf. statische Ressourcen minifiziert und dann alles außer den Abhängigkeiten sauber verpackt. Und das "alle außer den Abhängigkeiten" müssen wir nun wieder relativieren, da wir ja den YUICompressor aus den Abhängigkeiten entfernt hatte. Also kommt er explizit als "aaa-yuicompressor" wieder rein zusammen mit der Rhino-Bibliothek. Viola - das war's.

Was ist an der Lösung so toll? Nun, in Maven-Szenarien ist mir nichts besseren Eingefallen, als den YUICompressor unter dem albernen Namen in ein lokalen, eigenes Repository einzufügen. Würde ich das hier tun, wäre Tangram nicht mehr mit öffentlichen Maven-Artefakt-Repositories zu bauen. - Nicht gerade ein gute Idee und eigentlich auch keine für nicht öffentliche Projekte.

Samstag, 19. Mai 2012

BuildHive

Wer sich heute noch selbst Computer in ein Rechenzentrum stellt, kann irgendwie nicht rechnen - oder es ist sein Kerngeschäft.
Ein Stelle, wo es bisher immer sehr bequem oder notwendig war, noch detailliert auf die Server zugreifen zu können, ist der Softwaretest und -bau. Aber mit dem neuen BuildHive Dienst für Projekte auf github ist auch hier ein weitere Schritt getan, dieses auch in die Wolke zu schieben.
Natürlich ist eine Voraussetzung dafür, daß man sich standardisierter oder verbreiteter Techniken bedient. Mit Gradle, Java, Maven-Repositories etc. ist das z.B. gegeben. Erfreulich: Auch meine persönlichen Extravarganzen in Sachen Ordnerstruktur sind mit dem Build-System beschrieben und damt kein Problem. Zeitaufwand, hier einen CI-Server zu bekommen? Keine 5 Minuten!
Andere Projekte sind da viel komplizierter und überhaupt, muß man da sehr individuell vorgehen? Ich habe viel Verständnis für Individualisten und persönliche Bedürfnisse, aber nach all den Jahren in diesem Gewerbe ist es für mich ein Indikator, daß man evtl. nicht seinen Grips in diese Stelle der Software setzen sollte sondern genau hier die ausgetretenen Pfade nehmen sollte, die einem Sicherheit beim Bauen, Testen und Betriebsübergang geben. Fünf Minuten werden es vermutlich nicht sein, aber in diesem Bereich sehr schematisch vorzugehen ist eher eine Hilfe als ein Zeichen fehlender Ideen, es besser zu machen.
Der erfreuliche Nebeneffekt - und genau dafür ist so etwas ja da: Diese Umgebung deckte wieder einmal ein paar kleinere Schwächen im Code auf und regte mich dazu an, noch einmal ein Versions-Update von Gradle durchzuführen.
Schon wieder? Ja, Gradle bewegt sich mit großen Schritten auf eine ehrliche Version 1.0 zu und ließ es sich - zum Glück - nicht nehmen, noch ein paar inkompatible aber notwendig Änderungen durchzuziehen.

Dienstag, 14. Februar 2012

Noch mehr funky Groovy - und gradle blues

Rendering war ja nun schon lange nicht mehr mein Problem mit den bequemen Velocity Templates im Repository. Einiges Sites laufes ja schon damit (z.B. themen-geburtstag.de). Die URL-Formate lassen ebenfalls dort definieren, wenn man möchte, was sicherlich ein wenig schwieriger im Coding ist.
Neben dem eigentlichen Datenmodell fehlt nun noch die Action im ganzen, die ich auch gerne in's Repository verlegen würde - zumindest wieder einmal als Option, da das Verhalten doch etwas ist, was man ähnlich oft tunen will, wie das Layout.
Bisher habe ich das mit einem eigenen Satz Controller sauber lösen können, aber eben nicht so agil oder dynamisch wie die Styles und Templates.
Zumindest ist es nicht mehr meine Sicht auf moderne Web-Entwicklung, daß man einen Satz an Funktionalität fest eingebaut hat und nur noch Views darüber legt.
Und da sich am Horizont weitere Projekte abzeichen, die eher Communities als reine Websites werden sollen, bestand wieder einmal Handlungsbedarf.
Also habe ich nun den lange vorhandenen Action-Parameter bei der Link-Generierung einmal zu konsistenterem Leben erweckt (und nicht nur in der simplen Editing-Komponente verwendet) und mit zwei Annotations lassen sich nun in den LinkScheme Instanzen Methoden als Actionen markieren, die direkt über URLs angesprochen werden. Parameter-Übergabe und Resultatseiten Ansteuerung inklusive. Kommentare nach Lektüren der aktuellen Codes bitte gerne an mich.
Leider war eine Entscheidung nicht gerade förderlich (evtl. hatte ich sie deshalb beim letzte Lesen auf gradle.org auch nicht getroffen): Das update auf gradles 1.0 Milestone 7 bremst hier gerade extrem. Gut war es nur, als ich offline im Zug saß :-) Da war diese Version schneller. Ansonsten quält mich diese Version mit ewiglangen Suchaktionen nach den Dependencies. Zum glück kommt gerade Milestone 8 um die Ecke: Downloaden benutzen! Damit fühlt sich gradle wieder so gut an wie vorher :-)

Montag, 5. Dezember 2011

Endlich in's CoMA gefallen

Habe eben gerade einmal die neuen Errungenschaften des Wochenendes nach github geschoben - gepuscht auf neudeutsch. Ein kleines Update auf den neuesten Meilenstein von Gradle habe ich auch gleich noch eingezogen, weil Gradle langsam immer schneller wird und ich damit keinen nennenswerten Migrationsaufwand hatte. - Sehr zu empfehlen!
Letzte Woche hatten einmal wieder ein paar Leute gefragt, ob man nicht auch direkt auf eine CoreMedia Datenbank zugreifen kann. Kann man - aber wer will das schon.
Also habe ich jetzt endlich meinen CoreMedia Adapter (CoMA) in seiner schlichtesten Ausprägung in tangram integriert.
Nein, ich bin nicht zu doof, es ausgefeilter zu machen, aber ich will weder das Copyright von CoreMedia verletzen noch deren System nachbauen. Ich muß auch erst noch am Textkovertierer für den Richtext arbeiten.
Gemäß der neuen Doktrin seit Oktober ist nun ein Beispiel natürlich anbei. Wobei man für die Sache das Original-Beispiel von CoreMedia benötigt. Da meine Teile ohnehin nur als Ergänzung zu verstehen sind, ist das ja aber wohl keine Einschränkung.

Sonntag, 30. Oktober 2011

Beispielhaft

Leider fehlt mir immernoch der Ansatz, wie ich, ohne viel Code zu schreiben, Daten zwischen verschiedenen Datenbanksystemen austauschen kann. Ich hatte da schon auf vorgefertigte Techniken wie den XML-Export gehofft, die mir aber mit den internen IDs der Datenbanken bzw. von JDO Probleme machen.
Also habe ich das erst einmal aufgesteckt und statt dessen einfach die gute alte hsqldb eingesetzt, für die ich die Daten sehr einfach mitliefern kann. Dafür gibt's nun im rdbms Beispiel ein wenig Inhalt mit Velocity Templates und Groovy Code.
Das auf Gradle basierende Buildsystem kommt nun langsam in einen brauchbaren Zustand. Den CoreMedia CMS Adapter halte ich noch zurück, bis ich dort ein Beispiel für den Einsatz liefern kann.
Also: Beispiele und System stehen auf github bereit