Entwickeln Sie mühelos konfigurierbare Softwareanwendungen

Die Entwicklung leicht konfigurierbarer Software ist im heutigen Geschäftsumfeld von größter Bedeutung. Softwareanwendungen werden nicht mehr nur anhand der Menge an Geschäftslogik beurteilt, die sie einschließen. Sie werden auch danach beurteilt, wie einfach sie zu warten sind. Die Möglichkeit, das Softwareverhalten über die Konfiguration zu ändern, ist ein wichtiger Aspekt dieses Wartungszyklus.

Während die Java-Sprache eine Reihe von Funktionen wie Eigenschaftendateien und Ressourcenpakete zur Unterstützung der Konfiguration bereitstellt, fehlen diesen die Funktionen, die für die heutigen dynamischen Geschäftsumgebungen erforderlich sind. Viele Java-Standards, -Tools und -Container verwenden bereits erweiterte und benutzerdefinierte XML-Konfigurationsformate.

Das Obix Framework ist ein Open Source Framework, das die gängigen Mittel und Formate zum Speichern von Konfigurationsdaten in XML und zum Zugreifen auf diese Daten über einfache Java-Objekte bereitstellt. Es ermöglicht die Modularisierung von Konfigurationsdaten, indem Konfigurationsdateien importiert und ineinander eingeschlossen werden und Konfigurationsinformationen in "Modulen" organisiert werden.

Darüber hinaus unterstützt es "heiße" Konfigurationsänderungen - durch automatische Erkennung und automatisches Neuladen von Änderungen an Konfigurationsdaten - und bietet auch Unterstützung für die Java Naming and Directory Interface API (JNDI). Darüber hinaus kann es auf vielfältige Weise in Java-Anwendungen integriert werden, unter anderem über Java Management Extensions (JMX) und Java Platform, Enterprise Edition-Listener, für die keine Codierung erforderlich ist, sowie einfache Java-Klassen, die direkt aufgerufen werden können. Schließlich bietet das Framework eine benutzerfreundliche Plug-In-API, mit der Entwickler es erweitern können, um initialisierungsbezogene Aufgaben auszuführen. Diese API wurde vom Obix-Team verwendet, um Initialisierungsdienstprogramme für andere Open Source-Frameworks wie log4j, Hibernate und Commons DBCP (Datenbankverbindungspools) von Apache bereitzustellen.

In diesem Tutorial beschreibe ich ein hypothetisches Szenario, für das konfigurierbare Software erforderlich ist und für das wir mit Obix Skelettanwendungen erstellen. Das erste Beispiel kommt einem Proof-of-Concept im "Hello World" -Stil am nächsten, während das zweite und dritte diese Anwendung erweitern, um weniger triviale Aspekte der Konfiguration aufzuzeigen.

Bitte beachten Sie, dass alle Codebeispiele in diesem Artikel als Archiv verpackt sind, das über den in Ressourcen bereitgestellten Link heruntergeladen werden kann.

Problemszenario

Die Bewertung von finanziellen Vermögenswerten wie Aktien oder Optionen umfasst manchmal die tausendfache Simulation des Preises des Vermögenswerts und die Ermittlung des Durchschnitts dieser Werte - in der Annahme, dass der Durchschnitt eine bestmögliche Schätzung des "wahren" zukünftigen Werts des Vermögenswerts liefert. Solche Simulationen erfordern typischerweise statistische Eingaben in Form des aktuellen Preises des Vermögenswerts (der Vermögenswerte), des Durchschnittspreises über einen bestimmten Zeitraum sowie der Abweichung vom Durchschnitt.

Nehmen wir an, wir erstellen eine Anwendung zur Bewertung solcher Instrumente. Daher muss diese Anwendung die statistischen Eingaben über einen Webdienst herunterladen, und die Details wie URL und Authentifizierungsinformationen für die Verbindung zu diesem Dienst werden in einem Konfigurationsdokument gespeichert. Es genügt zu sagen, dass die Anzahl der Simulationen, die für eine bestimmte Bewertungsanforderung durchgeführt werden sollen, ebenfalls flexibel sein sollte und als solche über die Konfiguration festgelegt wird.

Beispiel 1: Eine grundlegende Konfigurationsdatei

In diesem Beispiel erstellen wir eine grundlegende Konfigurationsdatei, example1-config.xml, für unsere Anwendung, die die Details für die Verbindung mit dem Webdienst enthält, der die statistischen Eingaben für den Bewertungsprozess bereitstellt. In dieser Konfigurationsdatei wird auch die Anzahl der Simulationen gespeichert, die für eine Bewertungsanforderung ausgeführt werden sollen. Diese Datei (sowie die Konfigurationsdateien für die anderen Beispiele) befindet sich im Konfigurationsverzeichnis des herunterladbaren Archivs, das diesem Lernprogramm zugeordnet ist. Der Inhalt der Konfigurationsdatei wird wie folgt aufgelistet:

//www.some-exchange.com/marketdata

trading_app_dbo

nopassword

10000

Wenn wir die Datei genauer untersuchen, stellen Sie fest, dass sie mit dem Stammknoten beginnt . Dies markiert den Beginn eines Obix-Konfigurationsdokuments. Es gibt vier Knoten, die jeweils einen Konfigurationseintrag enthalten. Die ersten drei enthalten die URL, die Benutzer-ID und das Kennwort für die Verbindung zum Eingabedienst. Der letzte Eintrag enthält die Anzahl der Simulationen, die für jede Bewertungsanforderung durchgeführt werden müssen. Beachten Sie, dass jeder Eintrag einen eindeutigen Schlüssel hat, wie durch das entryKeyAttribut angegeben, und dass der Wert in jedem Eintrag von einem Knoten gekapselt wird .

Als Nächstes erstellen wir das Grundgerüst unserer Bewertungsanwendung und zeigen vor allem, wie das Konfigurationsdokument zur Laufzeit gelesen wird. Die interessierende Klasse wird aufgerufen Example1.javaund befindet sich im Ordner src des herunterladbaren Archivs, das diesem Lernprogramm zugeordnet ist. Die Klassendefinition lautet wie folgt:

import org.obix.configuration.Configuration; import org.obix.configuration.ConfigurationAdapter; import org.obix.configuration.ConfigurationAdapterFactory;

public class Example1 { public static void main(String[] args) { ConfigurationAdapterFactory adapterFactory = ConfigurationAdapterFactory.newAdapterFactory();

ConfigurationAdapter adapter = adapterFactory.create(null);

adapter.adaptConfiguration(Configuration.getConfiguration(), "config/example1-config.xml"); printMarketDataInfo(); }

private static void printMarketDataInfo() { Configuration globalConfig = Configuration.getConfiguration();

System.out.println("Data Service URL :\t\t" + globalConfig.getValue("market.data.service.url"));

System.out.println("Data Service User-ID :\t\t" + globalConfig.getValue("market.data.service.uid"));

System.out.println("Data Service Password :\t\t" + globalConfig.getValue("market.data.service.password"));

System.out.println("Simulation Count :\t\t" + globalConfig.getValue("number.of.valuation.simulations")); } }

Um dieses und die folgenden Beispiele auszuführen, müssen Sie die Obix Framework-Binärdateien an einen Speicherort herunterladen, auf den Sie über Ihren Klassenpfad zugreifen können. Ihr Klassenpfad muss auf die Obix-Bibliothek obix-framework.jar verweisen , die sich im lib-Ordner des Stammverzeichnisses des Frameworks befindet. Sie benötigen außerdem die folgenden Open Source-Bibliotheken von Drittanbietern : dom.jar , jaxen-full.jar , sax.jar , saxpath.jar und xercesImpl.jar , die sich im Ordner lib / ThirdParty im Stammverzeichnis des Frameworks befinden Verzeichnis.

Das Ausführen dieser Klasse sollte das folgende Ergebnis liefern:

Data Service URL : //www.some-exchange.com/marketdata Data Service User-ID : trading_app_dbo Data Service Password : nopassword Simulation Count : 10000 

Um diese Klasse zu zerlegen, beginnen wir mit der Hauptmethode. In der ersten Zeile dieser Methode wird eine Instanz der Klasse erstellt org.obix.configuration.ConfigurationAdapterFactory, die für die Erstellung eines Konfigurationsadapters (einer Instanz der Klasse org.obix.configuration.ConfigurationAdapter) verantwortlich ist. Der Adapter ist wiederum dafür verantwortlich, ein Konfigurationsdokument von einem bestimmten Speicherort (angegeben als Dateipfad oder URL) tatsächlich zu lesen.

Der folgende Codeextrakt liest den Inhalt unserer Konfigurationsdatei in die globale / statische Konfigurationsinstanz, indem er die Adaptermethode aufruft adaptConfiguration()und einen Verweis auf die globale Instanz - wie aus dem Aufruf erhalten Configuration.getConfiguration()- und den Pfad zu unserer Konfigurationsdatei config / example1 übergibt -config.xml:

adapter.adaptConfiguration(Configuration.getConfiguration(), "config/example1-config.xml"); 

Note that it is possible to create a new configuration instance to store our configuration data, rather than use the static (global) instance, but for the sake of simplicity (and brevity), we use the static instance for this example.

Next, we briefly examine the method printMarketDataInfo(), which simply reads the configuration entries (i.e., the XML nodes) and prints their values (i.e., their child nodes). Notice that each entry's value is obtained by calling the method getValue (...) on the associated Configuration instance, passing in the name/key of the entry—as specified for the entry node's entryKey attribute. As an aside, note that an entry can have multiple values, which will be demonstrated later in this tutorial.

Example 2: Modularizing configuration data

Applications of this nature will typically generate a report detailing a request's results in some sort of format. Our hypothetical application is no different; it is capable of producing valuation reports in a variety of formats. In addition, the reporting formats used in a given application run are dictated by a configuration entry, and all generated reports are emailed to a list of recipients within our organization—where the recipients are also specified in the configuration set.

Logically, reporting is a distinct piece of functionality—when compared to valuation—even though both are related; so it would be quite reasonable to encapsulate our "reporting" configuration data. This not only provides a cleaner separation of the configuration data, but also makes it simpler for a novice to visualize the delineation of functionality within the application.

We encapsulate the reporting configuration for this example by creating a configuration module for reporting, which is a child of our root module. We modify the configuration file from the last example by appending the node shown below to its list of nodes; the resulting file is called example2-config.xml and can be found in the config directory of the source archive.

.................... .................... ................... [email protected]

spreadsheet text-file pdf

Two things immediately stand out in this configuration file: the first, of course, is our module definition , followed by the module's second entry node . We begin with the module definition. An Obix configuration document can contain any number of submodules. Barring two elements—not discussed in this tutorial—modules support the same node set as the root module. In other words, modules have entries and can contain other modules; hence, modules can effectively be used to replicate a tree structure.

Recall that in the last example, I mentioned that a configuration entry can have multiple values. This functionality is demonstrated by the configuration entry for holding reporting formats, i.e., . As you can see, this differs from other entries in that it has three values—specifying the three formats in which reports should be generated.

We now examine the Java code for reading the entries in our reporting configuration module. We modify the Java source for the previous example by adding the following method; the modified source file (class) is renamed Example2.java, and can be found in the src folder of the archive associated with this tutorial:

private static void printReportingConfig() { Configuration globalConfig = Configuration.getConfiguration();

Configuration reportingConig = globalConfig.getModule("reporting.parameters");

System.out.println("Reports Destination :\t\t" + reportingConig.getValue("reports.destination.email"));

System.out.println("Reporting Formats :\t\t" + reportingConig.getValues("report_formats")); }

On executing this class, it should produce the output:

Data Service URL : //www.some-exchange.com/marketdata Data Service User-ID : trading_app_dbo Data Service Password : nopassword Simulation Count : 10000

Reporting Config Parameters= Reports Destination : [email protected] Reporting Formats : [spreadsheet, text-file, pdf]

Wenn wir die zusätzliche Methode im Detail untersuchen, stellen wir fest, dass sie zuerst einen Verweis auf die globale ConfigurationInstanz erhält . Anschließend wird ein Verweis auf das Konfigurationsmodul abgerufen, das die Berichtskonfigurationsinformationen enthält. Die Methode erreicht diese Aufgaben, indem sie die Methode getModule(...)auf dem übergeordneten Modul aufruft und die ID des zu empfangenden Moduls übergibt. Beachten Sie, dass diese Syntax in dem Sinne generisch ist, dass das Abrufen des untergeordneten Elements eines Moduls - auch wenn es nicht das Root-Modul ist - durch Aufrufen getModule(...)des angegebenen Moduls erreicht wird.