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

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.

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, 17. August 2014

Tangram Release 0.9

Over the last year, Tangram has changed very much and does not look the same in all of its modules and options.

Persistence Options

The Tangram Dynamic Web Application Framework has been extended to support the Java Persistence API (JPA) and EBean as persistence layers in addition to the already available Java Data Objects solution. This greatly extends the number of options for platforms to use with Tangram.
The Tangram examples reflect this with the JDO/DataNucleus, JDO/Google App Engine, EBean, and JPA example where the later is switchable with tested API implementations for OpenJPA, EclipseLink, DataNucleus, and Hibernate.
The simple and more or less generic JDO editor now is a generic editor for all the storage solutions and built as a separate module. Except for Google App Engine this module can be left out if there is another solution available to get the content into the repository.

Dynamic Model Extenions

In addition to the Groovy implemented classes stored in the repository, which are used to extend the view and controller part of the application, you can now create JDO annotated classes which are immetiately usable as model classes in the repository. This option can only be presented for the JDO implementation.
To make the building of Tangram itself, any Tangram application, or even any application using byte-code transformation for JDO, JPA, and Ebean easier, a gradle plugin has been introduced. The examples and over blog entries illustrate the usage of this plugin.

IDE Integration

To be able to synchronize codes stored in the repository a separate FTP module has be introduced. Most IDE due to their strong PHP past support FTP better than any other protocol. CSS, JavaScript, Groovy codes can be imported and exported.

Generic Import and Export

Additionally and apart from the FTP module Tangram now contains a generic importer and exporter for the whole content stored in the repository. The XML representation can be used to transfer content between application using JPA, JDO, or EBean. IDs cannot be preserved.

Framework Independency

For the whole Tangram code the Springframework now is only an option to do the setup of the application by Spring's Dependency Injection IoC-Container and for the controller and view parts of the application.
Along this way it was easier to present a custom DI component, Dinistiq, than to use one of the available solutions. Tests have been done with Google Guice, TinyDI, and JSR-330. As of Tangram 0.9 none of these are more than experiments. Work on a Tangram JavaEE integration is also in an experimental stage and would require a major refactoring of some parts of Tangram, see other entries in this blog on the JavaEE topic.
It should now be fairly easy to do more experiments with other IoC/DI frameworks and containers or to integrate other web frameworks starting from the new plain servlet based solution.

Options

Tangram 0.9 has been tested, verified, or is even in production use on the following platforms: Google App Engine, run@CloudBees, OpenShift, and Standalone with Apache Tomcat using RDBMS or MongoDB.
The option matrix now looks like this:
Persistence: JDO, JPA, EBean
IoC: Springframework, Dinistiq
Security: Spring Security, Apache Shiro
Hosting: On Premise, OpenShift, Cloudbees, Google App Engine
Storage: RDBMS, No-SQL e.g. MongoDB, Files

Code Reduction

Tangram started as an idea to plug existing components together to form a web application with as little glue code as possible. Some of the ideas like object oriented templating though had to be coded explicitly. After this starting point the Tangram codes started growing and the 0.9 release got an additional design goal to stop this. Still some of the codes in Tangram duplicate functionality which would otherwise externally be available but avoids a depedency just to call one method. And there are still codes duplicates within Tangram where the different options e.g. for persistence might get a stronger common code base. In these areas it is still not clear if they will differentiate or grow together.

Outlook

The experiences with CapeDwarf showed the way into a Java EE module as an alternative to dinistiq, the Springframework, and the many tests with e.g. Google Guice. The not that encouraging experiences with the EBean ORM might lead to discontinued support for that module. Dinistiq will become more Java EE / CDI compatible and CDI without the full Java EE stack also seems to be an interesting option. Work on the generic import and export functions using XStream needs to be extended and easier integration of OpenID or OAuth outside of the Google App Engine is needed for many applications. Google App Engine with JPA is still on the list but not desperately needed.

Freitag, 25. Juli 2014

150 Lines just for one Collection

Some 15 years ago I left the Java Enterprise Edition train and am now pushed by things like CapeDwarf on JEE Servers to the evaluation of running Tangram somehow in a JEE environment. I read the promise, that things have become a lot easier and that JEE has adopted the annotation and convention based style I now feel familiar.
I came across the JSR330 annotations from the javax.injection package which were introduced in the JEE world. JSR330 Annotations are recognized by several Dependency Injection frameworks like Guice, Springframework, my homegrown dinistiq, and obviously the JEE Containers.

Step 1: Get rid of Spring

This is were I started to make Tangram less depending on the Springframework directly or provide interfaces to be implemented in a spring and a non-spring way. For the dependency injection part it was mostly as easy as to migrate from

    @Autowired
    private ClassRepository classRepository;


to

    @Inject
    private ClassRepository classRepository;

I lost the required = false option at that time but was able to live with that given the advantages of portability.

Step 2: Find Alternatives

The spring parts of the application where re-implemented using a plain servlet based solution, and springsecurity was replaced by Apache Shiro for those scenarios.
After having migrated to the new annotations most of my Components where portable across containers. So I don't have to sell my soul to a special DI framework for very many cases, which helps choosing the right infrastructure for every project. But there are several details which still remain problematic, most notably the missing required = false option and the missing collection of instances to be able to automatically let the whole set of instances be injected.

Step 3: Annoying Details

The annoying collection problem looks like this:

@Autowired(required = false)
private Collection<ControllerHook> controllerHooks = new HashSet<ControllerHook>();


Later I migrated this to the JSR-330 Annotations - which also works with spring.

@Inject
private Collection<ControllerHook> controllerHooks = new HashSet<ControllerHook>();


I made this work with dinistiq as well.
With Guice I learned, that it didn't like to collect the instances for me and provide me with the collection out of the box.
I'm expecting here, that any instance implementing ControllerHook from the application context gets added to a collection which then is injected into the consuming component. Spring does it, dinistiq does it, Guice can be convinced to do it by the additional Multibinder:
 
Multibinder<ControllerHook> multibinder = 
                            Multibinder.newSetBinder(binder(), ControllerHook.class);
multibinder.addBinding().to(UniqueUrlHook.class);
multibinder.addBinding().to(ProtectionHook.class);

Step 4: Show Stopper

Inspired by the CapeDwarf project, promising to be able to deploy applications developed for the Google App Engines and their APIs on a JEE infrastructure, I tried to deploy GAE based Application to such an Infrastructure based on a JBoss AS7 or Wildfly 8 Server using CapeDwarf Versions 1 or 2 respectively.
This approach doesn't work for Tangram since the used DI solution within the application - be it dinistiq or the Springframework - interferes with the JEE Container's CDI implementation. The Application Server starts to interpret the annotations intended for the other DI framework. This would instantiate the application components twice and right at the moment it also fails on some of those components.

Step 5: JEE Wordiness

So quite naturally I now went over to directly migrate Tangram to the JEE world. But also JEE has the same problem as plain Guice has.

@Inject
private Collection<ControllerHook> controllerHooks = new HashSet<ControllerHook>();

This time the solution really is ugly. It seems I have to implement a different consuming component and thus instead of the two lines, a separate ControllerHookProvider is needed

public interface ControllerHookProvider {

    Collection<ControllerHook> getControllerHooks();

} // ControllerHookProvider

Of course the generic implementation looks like the good old two lines which did the whole job for me so far.
 
public class GenericControllerHookProvider implements ControllerHookProvider {
    @Inject
    private Collection<ControllerHook> controllerHooks;


    public Collection<ControllerHook> getControllerHooks() {
        return controllerHooks;
    } // getControllerHooks()

} // GenericControllerHookProvider

For JEE an alternative implementation has to be chosen, which made the interface necessary in the first place, and it is surprisingly wordy for that common and simple scenario.

@Named("controllerHooksProvider")
@Singleton
public class JeeControllerHooksProvider implements ControllerHookProvider {

    private Collection<ControllerHook> controllerHooks = new HashSet<>();

    @Inject
    public void setControllerHooks(@Any Instance<ControllerHook> hooks) {
        for (ControllerHook hook : hooks) {
            controllerHooks.add(hook);
        } // for
    } // setControllerHooks()

    public Collection<ControllerHook> getControllerHooks() {
        return controllerHooks;
    } // getControllerHooks()

} // JeeControllerHooksProvider

This really didn't invite me to go deep into JEE again. Additionally it is missing any of the configuration file based bean definitions avoiding much of the interfacing of classes where in fact just two injected values make the difference. With the Springframework configuration files and auto scanning of beans go hand in hand. JEE intentionally left out this part.

Donnerstag, 24. Juli 2014

Fast on the first Request

It seems to be common sense with so many developers, that "expensive" things that only happen once are not that bad. And when those things happen at the wrong point in time that it is a good idea to move them to the "application startup".
So most of us are not very surprised and consider it no problem, if this startup takes some time.

Welcome to the Cloud

At a first sight, deploying my applications in the cloud is exactly the same as it used to be. I follow accepted standards, use common frameworks and respect many best practices. So any runtime platform supporting this should do the job. Cloud in that case means, that many more aspects of deployment and operations get automated - including that starting and stopping of additional instances, load balancing and so on.
But wait... When does the platform get the idea to start new instances?

Let the Customer wait?

It's just load. And load means, that Customers issued HTTP Requests and are awaiting responses in time. From research we know that in time means something around 3s. But when a user request leads to the start of a new instance to handle the load it's quite too late to do all the expensive stuff. Application startup is not the right point in time anymore.
Additionally you can learn, that all those nice precomputations simply re-calculate values the instances from yesterday or the other instance on the other machine already learned. And of course it still is a good idea to have all those values at hand - meaning to "cache" them. But very many of the values which are not coded or configured into the application deployment stay the same as long as the deployment environment doesn't change. Or they stay the same unless the application itself changes them and thus is able to tell when really to re-calculate derived values in the caches.

Examples for all this are:
  • Database derived value caches like query results with calculations based on the result, where just this application writes to the database. Those values might be needed at startup but stay constant until the application itself changes the database contents.
  • Classpath scanning to auto-discover software components. The developers and deployers wont want to collect the lists manually or at build time but the components don't change after the deployment. And definitely not on every startup.
  • Webtemplates and other codes which need to be fetched and prepared for use (e.g. get compiled). Those codes get prepared on every startup of the application while they change not that frequently and especially not on application startup. Obviously they add a big amount to the the response time since those codes need to be executed for the generation of the response.

Just use a Cache

What? Oh, well not that Cache. This Cache. This cache doesn't need to be as fast as the in memory caches already available and I didn't want to re-invent the wheel. They just need to have some values at hand some other instance already calculated and they have to be changed when these values change - which is - as already pointed out - not as frequent as other runtime values.
The values in the cache still are volatile and can be re-calculated by the application at any time. But they should be persisted for some time to be available at startup and reduce the time for the first request in web applications.
The jsr107 cache implementation in the Google App Engine is an example of exactly this approach. I wrapped this cache as a PersistentRestartCache in Tangram and cut reponse times for the first request to a third (or half as the worst case scenario) from around 30s to 11s. For all other platforms available for the Tangram dynamic webapplication framework I presented a simple (maybe too simple) file based implementation which does most of the job as well.
Still this leaves out the classpath scanning of the Springframework or dinistiq. In my environment the Springframework uses between 3s and 7s and dinistiq around 4s to do the basic application setup based on classpath scanning and additional configuration files. So half of the time the startup deals with the framework code and not the applicaiton startup itself. And this value only was achieved by nearly brutal reduction of the portion of the classpath to be scanned creating a "components" package where all autoscanned components reside for the Springframework and dinistiq.

Resume

Caches are a good Idea. Applications with a dynamic deployment are a good idea. Thinking of the application startup as a point in time where long calculations might take place is not that much of a good idea (at least anymore) where instances should be automatically braught up depending on load and not human decisions.
So simply bring together caches with persistence and knowledge when those caches can be invalidated. As an example I did this for the tangram web application framework and had quite some success on the Google App Engine, run@cloudbees, and OpenShift cloud platforms for the Java world.
The last but also important point: Optimizing the application startup in general is worth the time nowadays.

Donnerstag, 17. Juli 2014

Not that Groovy

Ist es eigentlich schon allgemein akzeptiert, daß Web Anwendungen nicht mehr davon ausgehen dürfen, daß es für sie eine entspannte Startphase gibt? Daß sie auch beim ersten Request für den Endbenutzer ausreichend schnell antworten sollten? Es klingt immer noch häufig so, als wären "teure" Dinge, die nur einmal in der Anwendung passieren kein so großes Problem und das man dieses "teure" einfach auf die Startphase verschiebt.
Alles was ich hier schreibe, ist nur für Projekte relevant, in denen ein Deployment nennenswerte Kosten verursacht oder es aus anderen Gründen keine gute Idee ist, ständig die gesamte Software installieren. Mir war diese Stabilität mit großen Systemen auf Basis CoreMedia immer ein so großer Vorteil, daß ich das auch mit Tangram im kleinen nachgezeichnet habe (bzw. dort sogar für die großen Projekte vorgezeichnet habe). Dennoch muß man auf wechselnde Anforderungen genauso dynamisch reagieren, wie auf die Anfragen der Kunden an das System.

Messungen mit Groovy-Code in der Datenbank

Um die Software einfacher und schneller an Anforderungen anzupassen, habe ich mich ja vor Jahren auf Groovy-Code in einem irgendwie gearteten Repository festgelegt. Wir kennen das von Stylesheets, JavaScripts, Templates (z.B. Freemarker, Apache Velocity).
Damals habe ich für einen großen deutschen Telekommunikationskonzern eine Reihe von Messungen durchgeführt, die deutlich belegt haben, daß der Zugriff auf eine fertig kompilierte Groovy-Klasse - oder gar eine vorher abgelegte Instanz davon - keine Nachteile gegenüber dem Java-Code des statischen Programmteiles hat.
Für den einzelnen Request einer Webanwendung ist es also vollkommen egal, ob der Code nun deployed wurde oder in der Datenbank liegt.
In der Folge hat die typische Tangram Webanwendung kaum noch Anteile an Java-Code (siehe z.B. amor oder dragon-map usw). Es wird alles über Templates mit Apache Velocity für die Darstellungsschicht und Groovy-Code für die Geschäftslogik oberhalb der Datespeicherung bis hin zur URL-Struktur erledigt. (Mit JDO kann sogar ein Teil des Datenmodells so in den dynamisch veränderbaren Teil verlagert werden.)
Die Effizienz bei der Ausführung wird durch einfaches Kompilieren und Instanziieren beim Speichern der Codes erreicht.
Und hier genau schlummert ein Problem, das sich mir nun richtig stellt, wo einiges an "dynamischen" Codes (also solchen in der Datenbank, die potenziell häufiger geändert werden) zusammengekommen ist:
Im Gegensatz zu den Aufrufen der Anwendung und dem direkten Benutzen der Instanzen erhalten wir beim Start des Systems einen deutlichen Performance-Nachteil, da hier nun einiges an Datenbankabfragen und Kompiliervorgängen sowie Instanziierungen zusammenkommt. In der Summe ist das deutlich aufwendiger als die entsprechenden Vorgänge mit statischem Java-Code.
Warum interessiert der Start aber nun gerade wieder? In den meisten Szenarien kommt so etwas doch nicht gerade häufig vor.

Willkommen in der Wolke

Für einen selbstbetreuten "on premise" Servlet/JSP Container mag das stimmen, aber gerade für die "billigen" Modelle in der Wolke und z.B. auch die Google App Engine, OpenShift oder run@cloudbees stimmt das überhaupt nicht, da hier zum Skalieren und bei Nichtbenutzung der Trick gerade darin besteht, Instanzen der Anwendungen herunterzufahren oder neue hinzuzustellen.
Bei Instanzen auf der Google App Engine, bei denen keine sonstigen Zusicherungen eingestellt sind, führt das zu einem netten Hinauf- und Herunterfahren wie ein Jojo. Bis Version 0.7 beinhalten alle Tangram Anwendungen einen Cron-Job, der genau das verhindern sollte, indem die Anwendung sich selbst aufgerufen hat. Vor einiger Zeit habe ich mich dem Problem nun endlich gestellt und versucht, die Startup-Sequenz einmal zu tunen. In der Ausgangslage sind wir bei einem Erst-Start im Bereich von gerne 30s abgelaufene Zeit.

Spring Tuning

Das ist nicht das erste Mal: Bereits früher hat das Springframework gezeigt, daß es den Komfort für den Entwickler mit "Kosten" beim Start belegt. Der Scan der Klassen nach Annotationen und Komponenten sowie deren vollautomatisches Zusammensetzen dauert auf einem mittelprächtigen Rechner zwei bis drei Sekunden, in der App-Engine jedoch gerne bis zu acht Sekunden, die ein Kunde am Browser warten müßte. Das konnte damals durch das Eingrenzen beim Scan reduziert werden:

    <context:component-scan base-package="org.tangram" />

Die Angabe des Base-Package ist hier wichtig und grenzt den Bereich der Klassen, die geprüft werden, nachhaltig ein, was einige Sekunden bringt. In Zukünftigen Versionen sollten hier noch weitere Einschränkungen greifen, weil das "base package" org.tangram immer größer wird und über eine Anzahl von Jars verteilt ist. Ab Version 0.8 müssen daher all Komponenten, die gerne gescannt werden möchten "org.tangram.components" mit Vornamen - also Package-Namen - heißen.

    <context:component-scan base-package="org.tangram.components" />

Dazu wurden einige Klassen in den Paketen verschoben und es bringt wieder einmal ein paar Bruchteile von Sekunden ein. Lohnend, aber es durchbricht die fachlich Sortierung der Klassen ein wenig. (Aus org.tangram.jdo wird org.tangram.components.jdo - man findet sich also schon zurecht)

Caching

Moment! Wieso Caching? Das Füllen der Caches - soweit zu dem Zeitpunkt hilfreich oder notwendig  - ist doch gerade das, was den Start u.a. so langsam macht. Aber die Inhalte der Caches werden dann bei Start genau dieselben sein, wie beim letzten Herunterfahren - oder dieselben, die schon eine andere Instanz berechnet hat. Es wäre also ganz nett, wenn man einfach auf diese Wert zurückgreifen könnte.
Das tut Tangram letztlich auch bereits seit langer Zeit in der Google App Engine: Der Scan nach Klassen, die zur Persistenz-Schicht gehören, fällt in die gleiche Rubrik wie der Scan des Spring-Framework und die Daten, die sich die BeanFactory hier merken muß, werden in der Google App Engine im Memory Cache über die JSR107 Schnittstelle gespeichert.
Diese Schnittstelle soll uns nun helfen, noch mehr Vorgänge nur so häufig auszuführen, wie es notwendig ist, unabhängig davon, ob es wir von einem Start-Request sprechen oder mitten in der Arbeit sind. Das macht die Webanwendungen Wolken-tauglicher als sie es bisher sind.
Die Benutzung dieses Caches ist denkbar einfach:

try {
  CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
  jsrCache = cacheFactory.createCache(Collections.emptyMap());
} catch (CacheException ce) {
  log.error("()", ce);
} // try

Und danach ist es mit put() und get() getan, wobei man natürlich immer gewahr sein muß, daß einmal nichts im Cache ist. - Und einige kompliziertere Sache - obwohl java.lang.Serializable markiert - kann man zwar hineinstopfen, findet sie in der Admin-Oberfläche, bekommt sie aber nicht mehr heraus.
Das gilt leider insbesondere für per JDO persistierbare Objekte und kompilierte Java Klassen.

Tunen wo es wehtut

Langsam ist bei einem leeren Cache das beziehen aller Codes, die für die Website notwendig sind, aus dem Datastore. Hier geht es schon bei wenigen Codes eher um Sekunden als die oben zitierten Bruchteile. Also enthält Tangram ab Version 0.8 einen Simplen, persistenten Query-Cache, der genau wie der transiente Cache die IDs der Objekte der Ergebnislisten den Queries erfaßt und ablegt. Dieser Cache bringt der gesamten Anwendung bei ersten Aufrufen einer Instanz eine deutlichen Schub bei abfragebasierten Anwendungen.
Mit diesen ID-Listen muß man sich leider immer noch an den Datastore wenden, um die Objekte zu beziehen, da diese Objekte ja nicht in den Cache gelegt werden konnten.
Diesen Schritt aber wiederum kann man sich bei den Codes gut sparen, in dem man sie beim Lesen in reine transiente Objekte umwandelt (hey, es sind Codes!) und sie dann im persistenten Startup Cache ablegegen zu können.
Jetzt ist eine der längsten Phasen beim Starten das Kompilieren der Groovy-Klassen. Hier den Binärcode zu cachen hat sich bisher als nicht umsetzbarer Ansatz erwiesen. Aber evtl. kommt das ja auch noch. So ist der limitierende Faktor beim Startup auf die Anzahl der Groovy-Codes und den Compiler gesetzt. Mehr ging bisher nicht, aber es hat den Startup in der Zeit gedrittelt. - Und die Ansätze daraus sind verallgemeinerbar für Anwendungen in der Wolke.

Dienstag, 15. Juli 2014

Google App Annoyance - aka Engine

Together with the missing support for the Servlet 3.0 specification in Google App Engine (and, yes, there are still too many situations were this specification level ist not available) we are reading from Google for their App Engine that classpath scanning is an issue on that platform and that this is one of the reasons that kept them from supporting this version of the servlet specification.
What I didn't read from Google is, that they noticed that the Servlet 3.0 version is (arguably) one of the most important steps since Java came to the web. There is no major framework in the Java world anymore not doing any classpath scanning right now. The mentioned Springframework I'm using is just one example.
After some five years of work with the Google App Engine and Java as the only language in use, I changed my mind and don't consider the App Engine as one of the first choices in cloud platforms for the Java world anymore.
And this is really just because of these two small issues.
Like very many others I'm using the Springframework - with component scan and thus with classpath scanning. Any first request to an instance take ages. But "first requests" are common in the cloud, where instances need to be shut down and brought up depending on load. And the cloud is what Google App Engine is about, isn't it?
I invested quite some effort to learn how to be fast on the first request while still using the Springframework and even developed my own stripped down, minimalistic Dependency Injection environment for the application setup (dinistiq) to only have the features I'm using at hand. But this all didn't help to make the end user experience satisfying. Things feel slow.
So in the end this all gave the push for Tangram to support that many new platforms, use the CI Features and Repositories at cloudbees, enjoy the command line access of OpenShift. This brought options of different frameworks and I learned much about cloud deployment and operation scenarios. So in that respect we should be thankful for the Google App Engine weakness.
But it still is the source of some level of complexity and number of artifacts flying around in what I call my dynamic web application framework Tangram. Also this currently renders Google App Engine the second best cloud platform for Java while still having great web based monitoring tools.

Montag, 14. Juli 2014

Ein weiterer Tangram Nutzer

Die Ponton GmbH aus Hamburg hat nun seit einiger Zeit auch eine Webanwendung mit Tangram im produktiven Betrieb. Dabei habe ich sie natürlich selbst in diese Richtung geschubst, um schnell Ergebnisse vorweisen zu können, aber weder war ich der einzige Entwickler noch hab es ausreichende Gegenwehr.
Auf dieser Basis wird anscheinend das Projekt auch mit neuen Anforderungen weiterentwickelt.
Als eher konservatives Layout wird hier noch das Springframework genutzt und auf JPA als Persistenzschicht gesetzt. URL-Formate als Groovy-Codes in der Datenbank und dort auch jede Menge Busineslogik waren aber gerade der Gewinner bei der laufenden Anpassung von Kleinigkeiten. Nun ist nicht mehr jedesmal ein Deployment erforderlich wie bei der Vorgängerlösung (in PHP).
Aber insbesondere danke, daß ich das laut sagen darf.

Samstag, 5. April 2014

Why use JSR330 Dependency Injection annotations

I rather apologise to introduce another Dependency Injection Container for the Java world - dinistiq - a very minimalistic approach to the topic. It turned out to be easier to implement another one, than to use others listed here. Limited in features, easy to use, and still more configurable than other options I could think of. After some months of use, I now can invite other users to take a look at it and try it in their own projects.
Also this text gives you a "why" on the use of the JSR 330 annotations for Dependency Injection. It simply makes your code even more reusable in case your development or deployment environment changes.
Since tangram is much more about glueing together proven existing software components and frameworks than writing code, I felt the need to check if the existing code base was really fully dependent on the Spring Framework.
Despite the fact that spring more or less in many ways does what I need, it sometimes feels a bit bloated and does too much magic I don't understand in detail (which I still had to learn when debugging things). So I tried to isolate the spring code during the tangram 0.9 work and present at least a second solution for all the things I did with spring so far.
For tangram spring does three things
  • Dependency Injection to plug the whole application together
  • support a decent view layer with JSP and Apache Velocity views
  • A concise way to map http requests to code - controller classes or methods
So I took a look at other view frameworks like Vaadin, GWT, Apache Wicket, Play, Struts, JSF/JEE, Stripes. Right at the moment I think Vaading, GWT, Wicket, and Play are no really good fit for tangram, Struts in my eyes is a fading technology, and only JSF/JEE is an obvious option. With Java Server Faces I only had unsatisfying project experiences and the rest of JEE goes for plain Servlet. So tangram had to be provided with a plain Servlet way of doing the view layer.
Since the modularity of tangram was achieved by the Spring way of plugging components together with Dependency Injection, the first thing to do was, to mark the generic components in a spring independent way and to look at the other options for the Dependency Injection part. Only then it would be possible to replace the spring view layer with a Servlet view layer during the startup and wire-up of the application.
So the list of relevant DI frameworks gets shortened to those supporting the generic Dependency Injection annotations from JSR330 which are intended for JEE and can e.g. also be used with Google Guice and the Spring Framework alike.
From the reading Google Guice seemed to be a good alternative for the proof of concept phase, but it took me that much work to get something to run with it (not everything can be plugged together programmatically in my case), that I came out faster with my own Dependency Injection Container. Rather minimalistic and only suited for the setup of components.
Its advantage over Guice is that it's smaller and easier configurable with properties files. Weeks later I discovered TinyDI as another option. While this container seems to be a lot cleverer about the search of annotated classes it seems to lack the needed option of extending the configuration aspects from the annotations with properties files - defaults and overridden values and references.
So right at the moment I still don't have a running tangram application but all of the tangram framework now can be used with dinistiq. This example shows that now over 90% of the classes of tangram are free of direct dependencies to the Spring Framework while still taking advantage of its features and runtime environment. The code definitely got cleaner and more reusable.

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.

Sonntag, 29. September 2013

Tangram 0.8 Release - Mehr Dynamik bitte

Es wurde langsam Zeit für ein neues Tag auf dem Tangram Repository. Die Änderungen gegenüber der letzten Version sind doch umfangreich und viele Anwendungen, die Tangram nutzen - und dringend Version 0.8 brauchen, sollten wieder eine Stabile Basis bekommen.
Die aktuelle Fassung auch von den Beispielen gibt es als Code wie immer bei github und die Dependencies bezieht man aus dem amor.

Was gibt's neues?

Version 0.8 beschäftigt sich viel mit Dynamik. Dazu bedurfte es eines Erheblichen Tunings - insbesondere auf der Google App Engine - allgemein für die Nutzung in der Cloud. Aber etwas anderes kommt unter der Überschrift Dynamik ja auch nicht in Frage.
Zu den umfangreicheren Caching Techniken ebenfalls mit Zielgebiet Cloud werde ich hier noch einen separaten Beitrag veröffentlichen.
Mit Version 0.8 ist die Programmierung von Tangram-Anwendungen nun endgültig in die Datenbank gewandert und wird dort mit Apache Velocity und Groovy umgesetzt, wobei einige Schwächen und Fehler bereinigt wurden.
Dabei sind nun neben den bekannten URLs auch Benutzeraktionen auf der Weboberfläche mit (in den sogenannten Shims) Groovy umsetzbar.
Das dynamische Zusammenstellen der Inhalte auf der Site ist nun ausreichend performant, in der API vollständig und ebenfalls mit Groovy handhabbar.
Um das alles benutzbar zu halten wurde der Editor - eigentlich nur ein Stiefkind in Tangram - in wichtigen Details verbessert und in den Abläufen einfacher gestaltet.
Technisch wurde die RDBMS Umsetzung vom Proof of Concept neben der Google App Engine zu der Hauptumsetzung und das System um die Anbindung MongoDB erweitert. Dabei wurde der JDO-Layer von den historischen Altlasten früher Google App Engine Zeiten befreit und auf API-Level 3.0 gehoben.
Natürlich sind alle genutzen Komponenten auf aktuelle Versionen umgestellt und auch das Buildsystem mit Gradle - jetzt in Version 1.8 - konsistent weiterentwickelt.

Neuer Standard-Startpunkt

Wer nun eine neue Web-Idee ausprobieren möchte und kein Geld in die Hand nehmen will, nimmt nicht mehr die Tangram und die Google App Engine und den Google Apps sondern Tangram und Cloudbees - ggf. mit MongoDB als Backend, an dieser Stelle sogar mit integrieten git-SCM und Continous-Integration-Server über Jenkins.
Auch hier gilt: Wenn die Idee fliegt, besteht ein professionelles Angebot für eine kommerzielle Nutzung der Plattform.
Ersatz für die Google Apps, die wir in der Vergangenheit für den Rest des Auftrittes wie Mails, Calender, Dokumentablage etc. genutzt haben, findet man bei zoho.

Mittwoch, 18. September 2013

NATURINSPIRIERT.org mit Tangram

Die Meldung kommt eigentlich etwas spät, aber es gibt einmal wieder eine neue Site, die mit Tangram realisiert wurde. Das kleine Foto-Blog naturinspiriert.org ist mit dem Tangram Framework auf der Google App Engine realisiert worden.
Das ganze ist insofern technisch dennoch nicht "yet another" und "nur klein", da hier zum ersten Mal live die dynamsichen Content-Zusammenstellungen genutzt werden. Die einzelnen Rubriken der Site befüllen sich mit den passenden Artikeln von selbst. Alle anderen Projekte, die diese Feautures von Tangram 0.8 nutzen sind leider noch nicht online oder können hier nicht öffentlich erwähnt werden.
Was bei der Umsetzung aufstieß sind allerdings die Probleme mit der Google App Engine: Man darf sich nicht mehr blenden lassen von veralteten Hinweisen und Links: Ohne kostenpflichtigen Google Apps Zugang bekommt man keine eigene Domain mehr für Google App Engine Anwendungen geschaltet. Punkt. Die erwähnten Links gibt es nicht mehr. Wir haben sie in keinem Szenario aus Google App Engine Anwendungen und Google Accounts nachstellen können.Wir werden also nun weiter an den alternativen Arbeiten.
Wenn's Geld kosten darf, ist Google Apps sicherlich eine gute Lösung und Google Appengine eine sehr attraktive Plattoform. Wenn man allerdings noch nicht weiß, ob eine Lösung Geld verdient, ist es nach erfolgreichen Jahren seit 2013 nur noch zweite Wahl.

Montag, 8. Juli 2013

Mehr Auswahl für die Plattform - Tangram auf Cloudbees

Wie es aussieht, läuft Tangram sehr einfach auch auf der Infrastruktur von Cloudbees. Im Gegensatz zu der Google App Engine benötigt man bei dieser Variante keine spezielle Anpassung, es müssen nur die vorhandenen Konfigurationsparameter passend eingestellt werden:

1. Anlegen einer neuen Anwendung

Entweder über die Web-Schnittstelle oder die Kommandozeile legt man eine neue Anwendung an.

2. Erstellen einer Datenbank

Unter Cloudbees kann man MySQL Datenbanken anlegen und benutzen.
Eine solche Datenbank könnte man natürlich nun direkt in die Tangram-Anwendung hineinkonfigurieren.  Dann könnte man den folgenden Schritt überspringen und direkt die Anwendung mit Schritt 4 deployen. Server, Port, Schemaname und Benutzername sind ja aus dem GUI bekannt und können einfach in die jdoconfig.xml übernommen werden. Wir gehen hier jedoch über einen Zwischenschritt vor.

3. Verbinden der Datenbank als Datasource

Die Verbindung der Datenbank mit einem Namen, der über JNDI erreichbar ist, kann nur über die Kommandozeile erledigt werden:

examples\rdbms-example>bees app:bind -db <DBNAME> -a <APPID> -as tangramdb

Im Tangram rdbms example lautet die jdoconfig.xml dann

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

   <persistence-manager-factory name="transactions-optional">
       <property name="javax.jdo.PersistenceManagerFactoryClass"
           value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
       <property name="datanucleus.ConnectionFactoryName" 

                 value="java:comp/env/jdbc/tangramdb" />
       <property name="datanucleus.autoCreateSchema" value="true" />
       <property name="datanucleus.validateTables" value="true" />
       <property name="datanucleus.manageRelationships" value="true" />
       <property name="datanucleus.validateConstraints" value="true" />
   </persistence-manager-factory>

</jdoconfig>


Statt der "normalen" Verbindungsparameter findet sich hier also nur eine Zeile mit dem Eigenschaftsnamen "datanucleus.ConnectionFactotyName". Die anderen Parameter entsprechen den Einstellungen, die im Zusammenhang mit Tangram immer verwendet werden.

4. Deployen

Entweder benutzt man wieder das GUI und deployed das Web-Archiv unter build\libs\rdbms-example.war oder erledigt dies über die Kommandozeile (BEES_HOME muß gesetzt sein und das Cloudbees SDK im Pfad erreichbar):

examples\rdbms-example>bees app:deploy build\libs\rdbms-example.war  
    -a <account>/<appid>

5. Benutzen

Das war's dann auch:

http://<appid>.<account.>.cloudbees.net/s/list?cms.editor.class.name=org.tangram.rdbms.solution.RootTopic

führt einen nun auf nach dem Login an den einfachen Tangram Editor - und es kann losgehen, mit dem Anlagen von Codes und (im Beispiel) Topics und anderen Objekten,

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/

Freitag, 14. Dezember 2012

My App Engine

Der Schock am Wochenende, was sich bei Google mit dem Gesamtszenario aus Google App Engine und Google Apps, um eigenen Domains der App-Engine Anwendung zuordnen zu können, so verändert, ist ja wieder immer heilsam. Panik ist sicherlich übertrieben und die spontane Sicht etwas pointiert, aber es ist nicht der erste Schritt, bei dem Google Einschnitte im Bereich App Engine und Google Apps vornimmt. Und so war es wieder einmal Zeit, sich die Optionen anzusehen. 
Mit Tangram steht man nicht gerade festgelegt da und kann die Anwendungen normalerweise direkt auf einen Servlet/JSP Container umstellen und sich eine Datenbank suche, die von DataNucleus' JDO Implementierung unterstützt wird.
Das ist natürlich mit einer Umstellung der Codes verbunden. Außerdem hat sich ja gerade die Frage nach der Datenmigration als einer der verschlechterten Punkte in der Google App Engine herausgestellt. Es ist also eine gute Option für Tangram aber eventuell eine schlechte für ein bereits damit bestehenden Projekt.
Beim Stöbern im Netz stößt man als zusätzliche Option mitterweile aber auch auf zwei Projekte, die es sich zum Ziel gesetzt haben, eine zur Google App Engine kompatible Infrastruktur auf anderen Systemen als denen von Google zum Laufen zu bekommen.
AppScale und TyphoonAE wollen sich dabei wie ein "Plug-In-Replacement" anfühlen, sodaß die Anwendungen ohne Änderungen laufen können. Dafür muß man dann eben mit unterschiedlichen Mitteln - entweder entsprechenden Computern oder Cloud-Infrstrukturen - die App Engine Infrastruktur nachstellen - also statt an der Anwendung gewaltig and er Umbgebung arbeiten.
Um es gleich ganz deutlich zu sagen: Für keine der beiden Umgebungen sind alle APIs umgesetzt und keine ist in einem Zustand, daß man über einen produktiven Einsatz wirklich nachdenken sollte. Es ist nur jetzt an der Zeit, sich prototypisch anzusehen, ob man mit dem Weg etwas anfangen kann, und ihn mitzugehen oder nicht.
Die wesentliche Warnung an dieser Stelle ist aber eine, die bleiben wird: Die APIs und Lösungen der Google App Engine müssen bei beiden Lösungen nachprogrammiert werden. Und wenn sich bei Google etwas ändert, das man in der Anwendung nachführen muß (trotz der recht stabilen APIs ist das natürlich schon vorgekommen in den letzten Jahren), muß man bei den beiden Emulationen darauf warten, daß es dort auch entsprechend umgesetzt wird - oder es selbst tun. Es sind ja Open Source Lösungen und man kann die Erweiterungen auch wieder einbringen. Der Zeitliche Versatz bleibt so oder so und es wird immer ein Hinterherprogrammieren bleiben.
Von den beiden Lösungen kommt man mit der "Kleinen" TyphooeAE schneller an ein lauffähiges System aus dem bereitgestellten Code heraus. Hier wird ein Pyhthon-basiertes Build System bereitgestellt und man kann from Scratch bauen.
Die AppScale Lösung ist größer und zielt in jeder Hinsicht auf die größeren Installationen. Zum Ansehen gibt es hier dafür allerdings fertige VMs für Xen und KVM. Dumm nur, daß ich mich gerade wegen der Nutzung unter Windows und Linux für die Oracle Virtual Box entscheiden mußte.

Dienstag, 11. Dezember 2012

On the Fly: URL Formate dynamisch anpassen

Nachdem nun einmal Groovy Codes jederzeit als Klassendefinitionen in das Tangram System eingefügt werden können, bietet es sich an
Mit Groovy kann man in Tangram neben den Ergänzungen der "Bean Schicht" - des Models - durch Shims sehr einfach URL-Formate schreiben und dynamisch jederzeit anpassen.
Dazu wird wieder Code mit MimeType "application/x-grooy" geschrieben. Die Annotation ist diesmal aber "org.tangram.view.link.LinkScheme" oder gleich der Klassenname der zu erstellenden Klasse.
Diese Klasse muß das Interface org.tangram.view.link.LinkScheme implementieren, d.h. Instanzen müssen sowohl URLs interpretieren können als auch aus Objekten URLs generieren können.
Das Tangram-System bietet dabei neben den URLs zur reinen Ansicht der Inhalte sogenannte Action-URLs, mit denen Aktionen - also Controller-Elemente - angesprochen werden sollten. Am Ende so einer Aktion sollte ein Redirekt auf eine reinen Ansichts-URL erfolgen. (Sonst würde bei "Reload" durch den Benutzer die Aktion erneut ausgeführt)

package org.tangram.solution.web.links;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.tangram.Constants;
import org.tangram.content.Content;
import org.tangram.content.BeanFactory;
import org.tangram.controller.DefaultController;
import org.tangram.view.Utils;
import org.tangram.view.TargetDescriptor;
import org.tangram.view.link.Link;
import org.tangram.view.link.LinkScheme;

import org.tangram.solution.web.RootTopic;
import org.tangram.solution.web.Topic;
import org.tangram.solution.web.ImageData;

public class DefaultLinkScheme implements LinkScheme {
 
  private static Log log = LogFactory.getLog(DefaultLinkScheme.class);

  private BeanFactory beanFactory;
 
 
  public void setBeanFactory(BeanFactory factory) {
    beanFactory = factory;
  } // setBeanFactory()
 
 
  public void setDefaultController(DefaultController defaultController) {
    // Automagically set default view
    defaultController.getCustomLinkViews().add(Constants.DEFAULT_VIEW);
  } // setDefaultController ()
 
 
  public Link createLink(HttpServletRequest request, 

                         HttpServletResponse response, Object bean, 
                         String action, String view) {   
      Link result = null;
      if ((action==null)&&(view==null)) {
          if (bean instanceof RootTopic) {
              result = new Link();
              result.setUrl("/");
          } else {
              if ((bean instanceof Topic)||(bean instanceof ImageData)) {
                  String title = "-";
                  String keywords = "-";
                  if (bean instanceof Topic) {
                      try {
                          title = Utils.urlize(((Topic)bean).getTitle());
                          keywords = Utils.urlize(((Topic)bean).getKeywords());
                      } catch (UnsupportedEncodingException uee) {
                          log.error("createLink()", uee);
                      } // try
                  } // if
                  String url = "/"+keywords+"/"+title+

                                    "/"+((Content)bean).getId();
                  result = new Link();
                  result.setUrl(url);
              } // if
          } // if
      } // if
      return result;   
  } // createLink()

 
  private TargetDescriptor rootDescriptor = null;;
 
 
  public TargetDescriptor parseLink(String url, HttpServletResponse response) {
    TargetDescriptor result = null;
    if (url.equals("/")) {
        try {
            if (rootDescriptor==null) {
                List<RootTopic> rs = 

                   beanFactory.listBeans(RootTopic.class, null);
                RootTopic root = null;
                if (rs.size()==1) {
                    root = rs.get(0);
                } else {
                    response.sendError(HttpServletResponse.SC_NOT_FOUND, 

                                "Have "+rs.size()+" RootTopics in data store");
              } // if
                rootDescriptor = new TargetDescriptor(root, null, null);
            } // if
            result = rootDescriptor;
        } catch (Exception e) {
            result = new TargetDescriptor(e, null, null);
        } // try/catch
      } else {
          String id = null;
          Object bean  = null;
          String[] elements = url.split("/");
          for (String element : elements) {
            if (element.indexOf(":") > 0) {
              id = element;
              bean = beanFactory.getBean(element);
            } // if
          } // for
          if (bean != null) {
              result = new TargetDescriptor(bean, null, null);
          } else {
              throw new RuntimeException("no content with id "+id+

                                         " in repository.");
          } // if
      } // if
      return result;
  } // parseLink()
   
} // DefaultLinkScheme

Das Beispiel hier zeigt zum einen das Erzeugen von URLs mit createLink() und das Verarbeiten mit parseLink(). Es ist im Grunde schon gar nicht so simpel sondern erfüllt eine ganze Reihe von Anforderunen:
  • Es sind sprechende URLs umgesetzt, die den Title der Objekte benutzen
  • Die Startseite wird mit / aufgerufen
  • Aktionen laufen auf den DefaultController aus dem System