Beispiel: Zugriff auf Stateless Session Bean aus Java-Anwendung
Inhalt:
Anlegen des Projekts
Code des Application Client
Konfiguration (Variante 1)
Konfiguration (Variante 2)
Welche Variante?
Ausführen des Clients
Verbinden mit Username/Passwort
Troubleshooting
Import
Für JBoss 7.1: Beispiel für eine "ganz normale" Java-Anwendung, die auf eine Stateless Session Bean zugreift.
In diesem Beispiel werden zwei Varianten gezeigt, wie der Client sich die EJB "holen" kann.
Variante 1: Lookup über JNDI. Das Beispiel basiert auf der Doku von
https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI
Variante 2: Lookup über das "Remote Naming Project", siehe
https://docs.jboss.org/author/display/AS71/Remote+EJB+invocations+via+JNDI+-+EJB+client+API+or+remote-naming+project
Beide Varianten unterscheiden sich nur in den Konfigurationen.
Hier gibt es das gepackte Eclipse-Projekt (in Variante 1 - JNDI-Lookup) zum Download (die Importanleitung
findet man am Ende dieses Dokuments): StatelessStandaloneClient.zip
Vorraussetzungen: das Projekt basiert darauf, dass die EAR-Datei aus dem Stateless-Beispiel in Eclipse vorhanden ist
und außerdem auf dem lokalen JBoss deployed wurde.
Anlegen des Projekts
Es wird ein "Java Project" erzeugt:
Im ersten Schritt des Assistenten geben wir dem Projekt einen Namen (hier: "StatelessStandaloneClient")
Im Rest des Assistenten bleibt alles beim Default
Nachbearbeitung: Build Path
Wir müssen den Build Path anpassen.
Schritt 1: Das EJB-Projekt "StatelessEJB" wird bekannt gemacht. Dazu in die "Properties" des Projekts gehen.
Im Bereich "Java Build Path" gehen wir auf den Karteireiter "Projects" und klicken auf "Add":
Wir wählen das Projekt "StatelessEJB" aus:
Anmerkung: eigentlich benötigen wir nur die Klassen "GeometricModelRemote" sowie die in den Interfacemethoden deklarierte Exception "InvalidParameterException".
Schritt 2: eine JBoss-Library wird zugefügt, die die benötigten EJB-Klassen enthält.
Wieder in den "Java Build Path" gehen, diesmal auf den Karteireiter "Libraries". Hier auf "Add External Jars" klicken:
Die Datei "%JBOSS_HOME%\bin\client\jboss-client.jar" auswählen. Das Ergebnis sieht so aus:
Code des Application Client
Der volle Code des Client sieht so aus:
public class GeometricModelApplicationClient
{
public static void main(String[] args)
{
try
{
GeometricModelRemote geometricModel;
//InitialContext befüllt sich komplett aus "jndi.properties"
InitialContext context = new InitialContext();
//Lookup durchführen:
geometricModel = (GeometricModelRemote) context.lookup("ejb:/Stateless/StatelessEJB//GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote");
//EJB aufrufen:
double dblVolume = geometricModel.computeCuboidVolume(10, 5, 7);
double dblSurface = geometricModel.computeCuboidSurface(10, 5, 7);
System.out.println("Calculated volume: " + dblVolume + ", surface: " + dblSurface);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
Anmerkung: in dieser Variante des Client wird der EJB-Lookup über den "JBoss EJB Client ausgeführt, siehe eingangs verlinkte Seite.
In diesem Fall muss der Lookup auf "ejb:/Stateless/..." ausgeführt werden.
Für Variante 2 des Client (Lookup über "Remote naming project") entfällt "ejb:", aber sonst sind keine Änderungen am Client nötig.
Anmerkung 2: würde man den Lookup auf eine Stateful Session Bean durchführen, muss an den Lookup-String "?stateful
" angehängt
werden. Grund ist, dass die EJB-Client-API einen Hinweis darauf braucht, dass eine Stateful Session Bean gesucht wird und deshalb
interne Optimierungen, die nur für Stateless Beans möglich sind, nicht durchgeführt werden dürfen.
Konfiguration (Variante 1)
In dieser Variante des Client wird der EJB-Lookup über den "JBoss EJB client" durchgeführt.
"jndi.properties"
Es wird eine Datei "jndi.properties" im Verzeichnis "src" des Projekts angelegt. Beim Erzeugen des "InitialContext"
(in der Variante ohne Konstruktorparameter) wird diese Datei eingelesen.
Sie hat diesen Inhalt:
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
#JBoss 7:
java.naming.provider.url=remote://localhost:4447
#WildFly 8:
#java.naming.provider.url=http-remoting://localhost:8080
In dieser Datei wird für das JNDI-Framework (das ein Teil von JavaEE ist und von den Implementieren der Anwendungsserver
um eine konkrete Implementierung erweitert) angegeben, welche JBoss-Klassen die JNDI-Implementierung enthalten. Außerdem
ist hier die Adresse des Zielservers angegeben. Der Präfix "remote://" (für JBoss 7) bzw "http-remoting" (für WildFly 8)
muss sein, er gibt das Protokoll an.
Anmerkung: in den Beispielen werden immer Username und Passwort übergeben, beim Zugriff auf einen lokal laufenden JBoss ist das allerdings nicht
nötig. Siehe dazu der Abschnitt Verbinden mit Username/Passwort
Alternativ könnte man diese Datei weglassen und die Konfiguration komplett im Code machen:
Properties jndiProps = new Properties();
jndiProps.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
jndiProps.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
jndiProps.put(Context.PROVIDER_URL,"remote://localhost:4447");
// create a context passing these properties
InitialContext context = new InitialContext(jndiProps);
"jboss-ejb-client.properties"
Eine Datei namens "jboss-ejb-client.properties" wird ebenfalls im Verzeichnis "src" des Projekts angelegt.
Sie hat diesen Inhalt:
endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
#Port für JBoss 7:
remote.connection.default.port = 4447
#Port für WildFly 8:
#remote.connection.default.port = 8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
#remote.connection.default.username=theuser
#remote.connection.default.password=thepassword
Zu den einzelnen Werten:
- "endpoint.name": Wert ist egal, vermutlich dient er dazu, auf Serverseite den Client zu identifizieren.
- "remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED": siehe Webseite, allerdings erklärt sie nicht wirklich etwas...
- "remote.connections": es ist möglich, mehrere Verbindungs-Konfigurationen in der Datei anzulegen. Der Wert ist eine kommaseparierte Liste
von solchen Konfigurationsprofilen. "default" ist hier der Name einer solchen Verbindungs-Konfiguration.
- Pro eben deklarierter Verbindung werden jetzt deren Optionen angegeben, jeweils beginnend mit "remote.connection.", gefolgt von dem
Namen der Verbindung. "host" und "port" sind selbsterklärend (aber Achtung: bei WildFly 8 läuft jeglicher Zugriff über Port 8080),
und die letzte Option ist wiederum auch auf der Webseite nicht erklärt :-(.
- Auch hier kann man Benutzername/Passwort angeben, was für lokalen Zugriff nicht nötig ist - siehe Abschnitt "Verbinden mit Username/Passwort.
Im Project Explorer sollte es so aussehen:
Konfiguration (Variante 2)
In dieser Variante des Client wird der EJB-Lookup unter Verwendung des "jboss-remote-naming"-Projekt durchgeführt.
"jndi.properties"
Es wird eine Datei "jndi.properties" im Verzeichnis "src" des Projekts angelegt. Beim Erzeugen des "InitialContext"
(in der Variante ohne Konstruktorparameter) wird diese Datei eingelesen.
Sie hat diesen Inhalt:
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
#JBoss 7:
java.naming.provider.url=remote://localhost:4447
#WildFly 8:
#java.naming.provider.url=http-remoting://localhost:8080
jboss.naming.client.ejb.context=true
Einziger Unterschied zum vorherigen Beispiel ist hier die Zeile "jboss.naming.client.ejb.context=true
": diese gibt an, dass
"jboss-remote-naming" verwendet werden soll.
Die Datei "jboss-ejb-client.properties" ist hier nicht nötig.
Anmerkung zum JNDI-Namen:
Der Lookup geht auf eine EJB, die auf Server-Seite unter dem Namen "java:jboss/exported/" ins JNDI eingebunden wurde.
Genau solche Einträge finden wir in der Server-Konsole:
21:39:27,496 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for
session bean named GeometricModelBean in deployment unit subdeployment "StatelessEJB.jar" of deployment "Stateless.ear" are as follows:
java:global/Stateless/StatelessEJB/GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote
java:app/StatelessEJB/GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote
java:module/GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote
java:jboss/exported/Stateless/StatelessEJB/GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote
java:global/Stateless/StatelessEJB/GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelLocal
java:app/StatelessEJB/GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelLocal
java:module/GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelLocal
Beim Lookup wird automatisch vor den Lookup-String "java:jboss/exported/" vorangestellt, so dass folgendes funktioniert:
geometricModel = (GeometricModelRemote) context.lookup("Stateless/StatelessEJB//GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote");
Welche Variante?
Gemäß https://docs.jboss.org/author/display/AS71/Remote+EJB+invocations+via+JNDI+-+EJB+client+API+or+remote-naming+project,
Abschnitt "Why use the EJB client API approach then?" ist es empfehlenswert, den Ansatz der "EJB client API" zu verwenden. Dieser
hat mehr Möglichkeiten, die Kommunikation mit dem Server für den Fall "EJB wird im JNDI gesucht" zu optimieren.
Ausführen des Clients
Egale welche der beiden Konfigurationsvarianten man wählt, das Ausführen ist identisch: Einfach Rechtsklick auf "GeometricModelApplicationClient"
und "Run" wählen. Wenn man alles richtig gemacht hat, kommt diese Ausgabe (scheinbar in beiden Varianten ebenfalls identisch):
Jan 31, 2013 10:21:18 PM org.xnio.Xnio <clinit>
INFO: XNIO Version 3.0.7.GA
Jan 31, 2013 10:21:18 PM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.0.7.GA
Jan 31, 2013 10:21:18 PM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 3.2.13.GA
Jan 31, 2013 10:21:18 PM org.jboss.ejb.client.EJBClient <clinit>
INFO: JBoss EJB Client version 1.0.15.Final
Jan 31, 2013 10:21:19 PM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
INFO: EJBCLIENT000017: Received server version 1 and marshalling strategies [river]
Jan 31, 2013 10:21:19 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@e8b9d7, receiver=Remoting connection EJB receiver [connection=Remoting connection <10e4a33>,channel=jboss.ejb,nodename=turbotante]} on channel Channel ID c75492c5 (outbound) of Remoting connection 0056e684 to localhost/127.0.0.1:4447
Calculated volume: 350.0, surface: 310.0
Am Ende stehen die aus der EJB abgerufenen Werte.
Verbinden mit Username/Passwort
Wenn der WildFly-Server lokal läuft, dann kann der Client sich ohne Angabe von User/Passwort anmelden - siehe
https://community.jboss.org/wiki/AS710Beta1-SecurityEnabledByDefault,
Abschnitt "Local Clients".
Man kann die Eingabe eines Passworts auch bei lokalem Zugriff scheinbar "erzwingen": in "standalone.xml" sucht man folgenden Abschnitt:
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local"/>
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
</security-realms>
Im Security Realm "ApplicationRealm" entfernt man die Zeile <local default-user="$local" allowed-users="*"/>
und startet den Server neu.
Die Doku dazu: https://docs.jboss.org/author/display/WFLY8/Security+Realms
und https://docs.jboss.org/author/display/WFLY8/Detailed+Configuration
Jetzt erhält man eine Exception (Fehlermeldung bei Verwendung von Variante 1 - JBoss EJB client):
ERROR: JBREM000200: Remote connection failed: javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed
javax.naming.NamingException: Failed to connect to any server. Servers tried: [http-remoting://localhost:8080]
at org.jboss.naming.remote.client.HaRemoteNamingStore.failOverSequence(HaRemoteNamingStore.java:213)
at org.jboss.naming.remote.client.HaRemoteNamingStore.namingStore(HaRemoteNamingStore.java:144)
at org.jboss.naming.remote.client.HaRemoteNamingStore.namingOperation(HaRemoteNamingStore.java:125)
at org.jboss.naming.remote.client.HaRemoteNamingStore.lookup(HaRemoteNamingStore.java:241)
at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:79)
at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:83)
at javax.naming.InitialContext.lookup(Unknown Source)
at de.fhw.komponentenarchitekturen.knauf.stateless.standaloneclient.GeometricModelApplicationClient.main(GeometricModelApplicationClient.java:33)
Also fügt man über das Script "add-users.bat" einen Benutzer hinzu, siehe auch "Zugriff auf Web-Console" im Abschnitt "Allgemein"
Wichtig ist hier allerdings, dass man einen Benutzer im Realm "ApplicationRealm" zugefügt, nicht im "ManagementRealm".
Jetzt trägt man den User in die entsprechenden Konfigurationsdateien des Client ein:
Variante 1 ("JBoss EJB client")
Logindaten müssen in "jboss-ejb-client.properties" eingetragen werden.
endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
#Port für JBoss 7:
remote.connection.default.port = 4447
#Port für WildFly 8:
#remote.connection.default.port = 8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=theuser
remote.connection.default.password=thepassword
Ein Eintrag in "jndi.properties" ist hier nicht nötig.
Variante 2 ("jboss-remote-naming")
Logindaten müssen in "jndi.properties" eingetragen werden.
jboss.naming.client.ejb.context=true
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
#JBoss 7:
java.naming.provider.url=remote:/localhost:4447
#WildFly 8:
#java.naming.provider.url=http-remoting://localhost:8080
java.naming.security.principal=theuser
java.naming.security.credentials=thepassword
Troubleshooting
Bei Problemen hilft es eventuell, Log4J einzubinden.
Dazu muss man dem "Build Path" die Datei "%JBOSS_HOME%\modules\org\apache\log4j\main\log4j-1.2.16.jar" zufügen.
Außerdem legt man im Verzeichnis "src" eine Datei "log4j.properties" an mit diesem Inhalt:
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%t][%c] - <%m>%n
Durch das Loglevel "DEBUG" sieht man jetzt ein paar mehr Ausgaben. Das Level "INFO" entspricht wohl der Ausgabe ohne diese Datei.
Ein paar Fehlersituationen, die mir unterkamen:
Diese Meldung kommt beim Lookup über JNDI ("ejb:"-Präfix), wenn die Anwendung nicht auf dem Server deployed ist oder der Lookup-Name falsch ist
(hier ein Beispiel für "nicht deployed"):
INFO: EJBCLIENT000015: Initial module availability report for Remoting connection EJB receiver [connection=Remoting connection <3af4b3>,channel=jboss.ejb,nodename=turbotante] wasn't received during the receiver context association
java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:Stateless, moduleName:StatelessEJB, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@168cef6
at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:693)
at org.jboss.ejb.client.ReceiverInterceptor.handleInvocation(ReceiverInterceptor.java:116)
at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:183)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:177)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:161)
at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:124)
at sun.proxy.$Proxy0.computeCuboidVolume(Unknown Source)
at de.fhw.komponentenarchitekturen.knauf.stateless.standaloneclient.GeometricModelApplicationClient.main(GeometricModelApplicationClient.java:38)
Und dieser Fehler kommt beim Lookup über "jboss-remote-naming", wenn der Name falsch ist:
javax.naming.NameNotFoundException: aStateless/StatelessEJB//GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote -- service jboss.naming.context.java.jboss.exported.aStateless.StatelessEJB."GeometricModelBean!de.fhw.komponentenarchitekturen.knauf.stateless.GeometricModelRemote"
at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:103)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:197)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:174)
at org.jboss.naming.remote.protocol.v1.Protocol$1.handleServerMessage(Protocol.java:127)
at org.jboss.naming.remote.protocol.v1.RemoteNamingServerV1$MessageReciever$1.run(RemoteNamingServerV1.java:73)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Falscher Server-Name in "jboss-ejb-client.properties":
WARN: Could not register a EJB receiver for connection to alocalhost:4447
java.nio.channels.UnresolvedAddressException
at sun.nio.ch.Net.checkAddress(Unknown Source)
at sun.nio.ch.SocketChannelImpl.connect(Unknown Source)
at org.xnio.nio.NioXnioWorker.connectTcpStream(NioXnioWorker.java:310)
at org.xnio.XnioWorker.connectStream(XnioWorker.java:184)
at org.jboss.remoting3.remote.RemoteConnectionProvider.connect(RemoteConnectionProvider.java:194)
at org.jboss.remoting3.EndpointImpl.doConnect(EndpointImpl.java:296)
at org.jboss.remoting3.EndpointImpl.connect(EndpointImpl.java:386)
at org.jboss.ejb.client.remoting.NetworkUtil.connect(NetworkUtil.java:151)
at org.jboss.ejb.client.remoting.NetworkUtil.connect(NetworkUtil.java:132)
at org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector.setupEJBReceivers(ConfigBasedEJBClientContextSelector.java:146)
at org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector.<init>(ConfigBasedEJBClientContextSelector.java:105)
at org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector.<init>(ConfigBasedEJBClientContextSelector.java:73)
at org.jboss.ejb.client.EJBClientContext.(EJBClientContext.java:81)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:156)
at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:124)
at sun.proxy.$Proxy0.computeCuboidVolume(Unknown Source)
at de.fhw.komponentenarchitekturen.knauf.stateless.standaloneclient.GeometricModelApplicationClient.main(GeometricModelApplicationClient.java:38)
In der Variante "JNDI" ist dies gefolgt von dieser Exception (in der Variante "jboss-remote-naming" ist das die einzige Exception):
javax.naming.NamingException: Failed to connect to any server. Servers tried: [remote://blocalhost:4447]
at org.jboss.naming.remote.client.HaRemoteNamingStore.failOverSequence(HaRemoteNamingStore.java:200)
at org.jboss.naming.remote.client.HaRemoteNamingStore.namingStore(HaRemoteNamingStore.java:131)
at org.jboss.naming.remote.client.HaRemoteNamingStore.namingOperation(HaRemoteNamingStore.java:112)
at org.jboss.naming.remote.client.HaRemoteNamingStore.lookup(HaRemoteNamingStore.java:223)
at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:79)
at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:83)
at javax.naming.InitialContext.lookup(Unknown Source)
at de.fhw.komponentenarchitekturen.knauf.stateless.standaloneclient.GeometricModelApplicationClient.main(GeometricModelApplicationClient.java:33)
Import
Um das Beispielprojekt in Eclipse zu importieren, sind folgende Schritte nötig:
1) Verlinkte zip-Datei in den Workspace entpacken (so dass ein Unterverzeichnis "StatelessStandaloneClient" entsteht).
2) In Eclipse: Menü "File" -> "Import" wählen. Dort die Option "Existing Projects into Workspace" wählen:
Im nächsten Schritt wählt man als "Root directory" den Workspace selbst aus. Jetzt sollten alle vorhandenen Projekte auftauchen, und
außerdem das Projekt "StatelessStandaloneClient". Dieses wird abgehakt.
3) Jetzt muss man vermutlich den "Build Path" (bzw. den Pfad zu "jboss-client.jar") anpassen, siehe Abschnitt Anlegen des Projekts
Stand 11.10.2017
Historie:
05.02.2013: erstellt
02.04.2013: Feinschliff, Unterschied der Zugriffsvarianten verdeutlicht
29.09.2013: Anpassung an WildFly 8, Abschnitt "Verbinden mit Username/Passwort"
11.10.2017: Link auf Beispiel für WildFly 11