CosmoCode ist eine Internetagentur aus Berlin mit Schwerpunkt CMS, Wiki, Web2.0
Great software. Bright people. Happy customers!
Mail info@cosmocode.deTel +49 (30) 814504070
Palava ist ein von CosmoCode entwickeltes Werkzeug zur Anbindung von PHP Frontends an Java Backends.
Palava wurde im Jahr 2007 von CosmoCode entwickelt; die TheLabelFinder Plattform läuft beispielsweise darauf. Wir haben im letzten halben Jahr viele Erweiterungen und Code-Verbesserungen in das System gebracht - das Ergebnis ist das Palava2 Framework.
Palava arbeitet nicht auf Methodenebene, sondern auf Service Ebene. Dies bedeutet, dass komplette Business-Tasks in Java Backend-Jobs Jobs gekapselt und vom PHP Frontend aufgerufen werden. Daraus resultiert eine klare Trennung zwischen Frontend und Backend (Schnittstelle sind die Backend-Jobs); dies bedeutet:
Palava2 ist um einiges flexibler als die Vorgängerversion Palava und inzwischen auch weit mehr als ein Framework für Inter-Prozess-Kommunikation mit PHP.
Dieser Artikel soll anhand eines einfachen Beispiels veranschaulichen, wie die Kommunikation zwischen PHP und Java mittels Palava2 realisiert werden kann.
Es soll das Erstellen einer simplen, auf dem Palava2 Framework basierenden, Webapplikation demonstriert werden. Der Job der exemplarisch erstellt werden wird, soll eine Liste übergebener Strings in Großbuchstaben umwandeln und diese dann wieder an das Frontend (PHP) zurückgeben. Nicht sehr spannend, aber als Demo absolut geeignet.
Hier das Vorgehen
Folgende Tools werden benötigt
Hinweis: Bei den Java-Quellen wurden die imports entfernt, das steigert die Lesbarkeit.
Zunächst erstellen wir die benötigte Verzeichnisstruktur
mkdir -pv palava-demo/{bin,conf,src/main/java}
Palava2 steht als Maven-Projekt zur Verfügung. Hier kann man die pom.zip für das Demoprojekt downloaden. Diese wird direkt in das Hauptverzeichnis, also palava-demo, entpackt.
Über Konsole oder ein IDE-Plugin führen wir mit viel Geduld, da anfangs noch alle Abhängigkeiten gedownloaded werden müssen, folgendes aus um das Framework zu packen.
mvn clean package
Am Ende sollte BUILD SUCCESS erscheinen.
Folgendes identifiziert unser individuelles Projekt.
<name>Palava Demo</name> <groupId>de.cosmocode.demo</groupId> <artifactId>palava-demo</artifactId>
Im <build></build> Bereich kann angegeben werden, an welche Stelle die PHP-Bibliotheken von Palava, dazu gehören die core-Libraries und die individuellen Stubs welche wir im Folgenden erstellen werden, exportiert werden sollen. Bei unserer Demoapplikation werden diese nach /src/main/webapp/application/lib/palava/core/palava bzw. /src/main/webapp/application/lib/palava/stub exportiert. Ansonsten wird hier noch das Framework selber erstellt – dazu später mehr.
Die Komponenten der Grundapplikation werden über zwei Google-Guice-Module geladen. Das DemoApp Modul konfiguriert Komponenten für den Ablauf des Frameworks, das JsonModule die Komponenten für die Kommunikation mit PHP. Wer Palava2 aktiv einsetzt, kann anstelle der Standardimplementierungen jederzeit eigene Implementierungen laden. Es wäre prinzipiell auch möglich alles in einem Modul zu konfigurieren. Allerdings ist es meiner Meinung nach sinnvoll die einzelnen Bereiche zu trennen, das fördert die Übersichtlichkeit.
Erstellen wir also beide Module im demo.app-Paket.
package demo.app;
// imports...
public class DemoApp implements Module {
@Override
public void configure(Binder binder) {
// palava-core
binder.install(new LifecycleModule());
binder.install(new DefaultRegistryModule());
binder.install(new TypeConverterModule());
// palava-concurrent
binder.install(new DefaultThreadProviderModule());
binder.install(new BackgroundSchedulerModule());
// palava-ipc
binder.install(new IpcModule());
binder.install(new IpcEventModule());
binder.install(new ConnectionAwareUnitOfWorkScopeModule());
binder.install(new StoreIpcSessionModule());
binder.install(FileSystemStoreModule.annotatedWith(IpcSessionStore.class,
"session"));
binder.install(new DefaultIpcCallFilterChainFactoryModule());
binder.install(new LocalIpcCommandExecutorModule());
// palava dummy jmx(we don't need jmx but guice needs at least a dummy)
binder.install(new FakeMBeanServerModule());
// JsonModule
binder.install(new JsonModule());
}
}
package demo.app;
// imports ...
public final class JsonModule implements Module {
@Override
public void configure(Binder binder) {
// start the connectors
binder.install(new ExecutorModule(Boss.class, "boss"));
binder.install(new ExecutorModule(Worker.class, "worker"));
binder.install(new NettyServiceModule());
binder.install(new ChannelPipelineFactoryModule());
binder.install(new NioServerSocketChannelFactoryModule());
binder.install(new DefaultConnectionManagerModule());
binder.bind(ChannelPipeline.class).to(Key.get(ChannelPipeline.class, Json.class));
binder.install(new JsonFrameDecoderModule());
binder.install(new JsonNettyModule());
binder.install(new CustomProtocolModule());
}
}
Es müssen noch ein paar Parameter für die Laufzeit eingetragen werden. Das passiert, wie bei Java üblich, über eine Properties-Datei. Wir öffnen bzw. erstellen die Datei palava-demo/conf/application.properties mit folgendem Inhalt
# the Main class core.application = demo.app.DemoApp # session configuration ipc.session.expirationTime = 3 ipc.session.expirationTimeUnit = HOURS # executor configurations executors.named.boss.queueMode = SYNCHRONOUS executors.named.boss.queueCapacity = -1 executors.named.boss.keepAliveTime = 5 executors.named.boss.keepAliveTimeUnit = MINUTES executors.named.boss.minPoolSize = 2 executors.named.boss.maxPoolSize = 10000 executors.named.boss.shutdownTimeout = 1 executors.named.boss.shutdownTimeoutUnit = MINUTES executors.named.worker.queueMode = SYNCHRONOUS executors.named.worker.queueCapacity = -1 executors.named.worker.keepAliveTime = 5 executors.named.worker.keepAliveTimeUnit = MINUTES executors.named.worker.minPoolSize = 2 executors.named.worker.maxPoolSize = 30000 executors.named.worker.shutdownTimeout = 1 executors.named.worker.shutdownTimeoutUnit = MINUTES executors.named.background.minPoolSize = 2 executors.named.background.shutdownTimeout = 1 executors.named.background.shutdownTimeoutUnit = MINUTES # ipc netty.address=localhost:8081 # session storage session.store.filesystem.directory = file:tmp/palava-sessions
Aufgrund der hohen Flexibilität des Frameworks muss anfangs viel konfiguriert werden - das Palava Team ist jedoch bemüht die möglichen Parameter und Einstellungsmöglichkeiten auf docs.palava2.org gut zu dokumentieren.
Nun da wir im demo.app-Paket die Grundlage unserer Demoapplikation gelegt haben, können wir anfangen den Job zu entwickeln. Wie schon angedeutet soll ein Job erstellt werden, welcher Strings eines Arrays in Großbuchstaben umwandelt und dieses als Array zurückgibt.
Jobs kommen in das Paket demo.app.service. Für unseren Job erstellen wir also ein Paket demo.app.service.echo und erzeugen darin eine Klasse EchoUppercaseAll
package demo.app.service.echo;
// imports ...
@IpcCommand.Params({
@IpcCommand.Param(name = EchoUppercaseAll.P_INPUT, optional = false, type = "List",
description =
"List with strings")
}
)
@IpcCommand.Return(name = EchoUppercaseAll.RESULT, description = "return uppercased values")
public final class EchoUppercaseAll implements IpcCommand {
private static final Logger LOG = LoggerFactory.getLogger(EchoUppercaseAll.class);
static final String P_INPUT = "input";
static final String RESULT = "data";
@Override
public void execute(IpcCall call, Map<String, Object> result)
throws IpcCommandExecutionException {
final IpcArguments arguments = call.getArguments();
final List<String> data = Lists.newArrayList();
final UtilityList<Object> list = arguments.getList(P_INPUT);
for(Object item: list ) {
data.add(StringUtils.upperCase((String) item));
}
LOG.trace("echo uppercased values");
result.put(RESULT, data);
}
}
Wie man sehen kann wird der Job mit Hilfe von Annotationen parameterisiert und auch dokumentiert. Ein angenehmer Nebeneffekt hierbei ist, dass die Dokumentation natürlich auch in die generierten PHP Stubs integriert wird.
Nun sollte wieder alles gepackt werden, das Backend ist soweit fertig. Da die Abhängigkeiten des Frameworks jetzt lokal in ~/.m2 vorliegen, sollte das relativ schnell gehen.
mvn clean package
Im letzten Schritt soll der EchoUppercaseAll-Job auf PHP-Seite ausgeführt werden. Während des Packens des Frameworks wurden über ein Maven Plugin bereits Stub-Klassen für unsere Job erstellt, diese müssen also lediglich eingebunden werden.
Die Webapplikation befindet sich unter src/main/webapp/application. Wenn wir da einen Blick drauf werfen finden wir ein /lib-Verzeichnis. Dieses enthält ein Verzeichnis /palava welches die Core-Library und die Stubs für unsere Demoapplikation enthält, jeweils in eigenen Verzeichnissen.
Erstellen wir direkt im /application Verzeichnis zunächst eine PHP-Datei, ich nenne sie einfach mal demo.php.
Hier wird zunächst die Palava2-Core-Library eingebunden, die die Kommunikation mit dem Backend übernimmt. Danach kann unser EchoUppercaseAll-Job über den entsprechenden Stub aufgerufen werden.
<?php
define ('PALAVA', 'lib/palava/');
define ('PALAVA_CORE', PALAVA . 'core/palava/');
define ('PALAVA_STUB', PALAVA . 'stub/');
define ('PALAVA_PORT', 8081);
include_once(PALAVA_CORE . 'Palava.php');
/**
* Autoload Stubs
*
* @param String $classname Class name
*/
function __autoload($classname) {
@include_once PALAVA_STUB . $classname . '.php';
if (!class_exists($classname, false)) {
// avoid fatal error
eval('class ' . $classname . ' { ' .
' public function __construct() { ' .
' throw new Exception("Class ' . $classname . ' not found."); ' .
' } ' .
'} ');
}
}
// create palava instance
$palava = new Palava("localhost", PALAVA_PORT);
try {
$palava->connect();
// create application stub
$stub = new IPCStubs_demoapp($palava);
$params = array(
'foo',
'bar',
);
$result = $stub->demo->app->service->echo->echoUppercaseAll($params);
var_dump($result);
} catch(PalavaConnectionException $ex) {
echo 'connection to backend lost';
} catch(Exception $ex) {
echo $ex->getMessage();
}
?>
Jetzt sind wir eigentlich fertig. ein Aufruf von demo.php zeigt nun folgendes
connection to backend lost
Starten wir also das Framework. Dazu wechseln wir in das /target-Verzeichnis und entpacken zunächst die Applikation.
cd target tar xf palava-demo-2.2.0-bin.tar
Gestartet wird das Framework danach über
./palava-demo-2.2.0/bin/palava.sh start
Ein erneuter Aufruf unserer demo.php sollte nun folgendes zeigen
array
'data' =>
array
0 => string 'FOO' (length=3)
1 => string 'BAR' (length=3)
Das soll es erstmal gewesen sein. Gezeigt wurde ein einfaches Beispiel wie ein Business Job in Java entwickelt und von PHP Seite aus aufgerufen werden kann. Aktuelle Informationen und einen Übersicht über weitere Palava2 Module kann unter Palava2.org gefunden werden.
Über CosmoCode
Abonnieren