Eine TreeView durchsuchen und den gefundenen Pfad aufklappen
Da ich heute wieder eine Mail mit der Frage bekommen habe wie man ein Item in einer TreeView finden kann und dieses dann anzeigen kann, dachte ich ich poste es gleich hier falls es noch jemand braucht.
Das Ganze funktioniert sehr einfach mit nur einer kleinen rekursiven Funktion:
''' <summary>
''' Diese Funktion durchläuft den ganzen Treenode bis ein Eintrag gefunden wird der dem Suchbegriff
''' gleicht. Danach wird der Pfad der zu diesem TreeNode führt (zur Visualisierung) aufgeklappt
''' </summary>
''' <param name="tn">Der zu durchsuchende Treenode
''' <param name="name">Der Suchbegriff
Public Sub findNode(ByVal tn As TreeNode, ByVal name As String)
For Each tnn As TreeNode In tn.Nodes
If tnn.Text = name Then
Dim t As TreeNode = tnn
For i As Integer = tnn.Level To i = 0 Step -1
Try
t = t.Parent
t.Expand()
Catch ex As Exception
End Try
Next
tnn.Parent.Expand()
Exit Sub
End If
findNode(tnn, name)
Next
End Sub
Hier laufen wir alle Nodes der TreeView durch bis wir auf einen Node stoßen der unserem Suchbegriff entspricht. Ab hier durchlaufen wir den ganzen Weg rückwärts und klappen alle übergeordneten Nodes auf, sodass wir unseren gesuchten Node zu Gesicht bekommen.
Diese Funktion kann man auch ganz gut als Extension Method umschreiben, sodass diese bequemer aufgerufen werden kann, aber das ist dann natürlich jedem selbst überlassen.
Hier noch ein Demoprojekt zum testen: Download [VS 2010]
Nice2Know: Attribut-basierende Routen in ASP.NET MVC
Für Routen ist in ASP.NET MVC für gewöhnlich die Global.asax zuständig, so werden hier alle Routen definiert und gemappt. Doch wenn man viele verschiedene Routen nutzen will wird es schnell voll in der eigentlich sehr übersichtlichen Klasse.
Da wäre es doch eigentlich cool wenn wir die Routen direct in unseren Controllern definieren könnten. Genau das können wir mit der Erweiterung AttributeRouting machen.
Dank NuGet ist die Installation in wenigen Sekunden abgeschlossen und wir können unsere Actions mit Routing-Attributen dekorieren.
Die Attribute sind hierbei sehr einfach gestaltet und bieten uns alles was wir benötigen.
public class DemoController : Controller
{
[GET("Ist/Das/Cool/Oder/Was")]
public ActionResult Start()
{
return View();
}
}
Nun muss man nur noch die Attribut-basierten Routen registrieren:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapAttributeRoutes(); //--- das wars!
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Start",
id = UrlParameter.Optional } // Parameter defaults
);
}
Und fertig! Nun können wir die Seite starten und unsere Route testen:
Die Routen die wir definieren können, beschränken sich nicht darauf, so können diese auch problemlos parametrisiert werden, inklusive Defaultparameter:
public class DemoController : Controller
{
[GET("Artikel/{artikelid}/{version?}")]
[RouteDefault("artikelid", "120")]
public ActionResult Start(int artikelid, string version)
{
ViewBag.ArtikelId = artikelid;
ViewBag.Version = version;
return View();
}
}
Hier wird der Parameter artikelid mit einem Defaultwert ausgestattet und der zweite optional gemacht. Das Ergebnis kann sich sehen lasen:
Hier hört das Framework nicht auf, so könnt ihr auch Regular Expressions in die Routen integrieren und auch mehrere Routen an eine Action heften. Die Möglichkeiten sind grenzenlos
Für weitere Infos schaut einfach auf der Projekthomepage vorbei, es lohnt sich!
Nice2Know: Errorlogging mit ELMAH
Errorlogging? Ist das nicht dieses Zeugs das die ganzen Java Programmierer und Linux Fuzies nutzen werdet ihr euch jetzt denken? Stimmt, aber auch in der .NET Welt ist es sehr nützlich ein Log zu führen und hier gibt es einige Tools für uns Entwickler die für uns diese unschöne Arbeit deutlich ersparen.
Das Tool dass ich euch heute vorstellen möchte ist ELMAH, was für Error Logging Modules and Handlers steht.
Was kann ELMAH? Hier ein Ausschnitt von der Hompage:
- Logging of nearly all unhandled exceptions.
- A web page to remotely view the entire log of recoded exceptions.
- A web page to remotely view the full details of any one logged exception, including colored stack traces.
- In many cases, you can review the original yellow screen of death that ASP.NET generated for a given exception, even with customErrorsmode turned off.
- An e-mail notification of each error at the time it occurs.
- An RSS feed of the last 15 errors from the log
Zusammengefasst gesagt schnappt ELMAH sich jede Exception die ihr nicht abgefangen habt und loggt diese für euch im Speicher oder in einer Datenbank und fasst diese auch noch schön zusammen auf einer Übersichtswebseite, in einem RSS Feed oder schickt euch diese direkt per Mail zu.
So könnt ihr jeder Zeit nach der Exception schauen was schiefgelaufen ist und euch den kompletten Stacktrace anschauen und (fast) jeden Yellow Screen of Death nachstellen.
Was mir an diesem Tool besonders gefällt ist die sehr einfach Integration in ein Projekt. Alles was ihr machen müsst ist es dafür zu sorgen dass die DLL in eurem bin-Verzeichnis und einen Handler in eure web.config einzutragen. Nun werden alle eure unbehandelten Exceptions geloggt.
Und es geht sogar noch einfacher, wenn ihr NuGet nutzt werden euch sogar diese Schritte abgenommen, einfach NuGet anwerfen, nach ELMAH suchen und installieren - fertig.

Das Schöne an NuGet ist, dass es nicht nur die Bibliothek herunterlädt sondern auch alle nötigen Einträge für uns anpasst, so werdet ihr nun auch ein paar neue Zeilen in eurer web.config entdecken:
Diese Arbeit blieb uns zum Glück erspart
Da nun eigentlich schon alles konfiguriert ist können wir fröhlich ein paar Fehler erzeugen:
Ich habe hier also eine URL aufgerufen für die kein Controller und keine View bestimmt werden konnte. Dass wir hier so “viele” Informationen angezeigt bekommen liegt daran, dass wir die Seite lokal aufrufen, würden wir das ganze von einem anderem System machen würden uns noch weniger Informationen zur Verfügung stehen.
Nun wollen wir mal gucken, was ELMAH für uns aufgezeichnet hat, das können wir machen, in dem wir diese Seite aufrufen: /elmah.axd
Alle aufgetretenen Exceptions für uns übersichtlich dargestellt. Wenn Bedarf besteht kann man natürlich auch tiefer bohren und die Details aufklappen:
Hier hat man nun den vollen Stacktrace und viele weitere Informationen wie z.B. die Servervariablen. Eine sehr coole Sache wie ich finde, da ihr nun auch von unterwegs eure Fehler untersuchen könnt.
Falls ihr nicht wollt, dass jeder eure Exceptions sehen kann ist dies natürlich auch möglich.
UPDATE: Ich habe hier die normale Version von ELMAH genutzt, für ein MVC Projekt würde ich aber ELMAH.MVC empfehlen (Ebenfalls auf NuGet verfügbar). Dieses integriert sich besser in eine MVC Umgebung da es einen AdminController anlegt und somit über /Admin/Elmah erreichbar ist.
ELMAH gibt es übrigens schon eine halbe Ewigkeit aber ich (Langschläfer) bin erst vor ein paar Wochen drüber gestolpert. Wie schaut es bei euch aus? Nutzt ihr ELMAH?
Lokale Wetterdaten anzeigen mit der HTML5 Geolocation API und ASP.NET MVC
Wenn man heutzutage hipp sein will mit seiner Webseite sollte man irgendwelche coolen Features von HTML5 nutzen. Deswegen möchte ich euch in diesem kleinem Tutorial zeigen, wie ihr auf eurer Seite dem aktuellen Besucher sein lokales Wetter anzeigen könnt.
Um dieses Ziel zu verwirklichen benötigen wir folgende Daten bzw. Dienste:
- Die physische Position des Besuchers
- Einen Dienst der uns zu der Position des Besuchers die Wetterdaten liefert
Der erste Punkt ist schnell abgehackt, denn hier werden wir eins der tollen neuen HTML5 Features namens Geolocation API benutzen. Diese API liefert uns, vorausgesetzt der Browser unterstützt es, Koordinaten des aktuellen Besuchers der Webseite. Alle aktuellen Browser, auch der IE ab Version 9 unterstützt diese API.
Das Ganze läuft so ab, dass wir als Webseite den Browser nach den Koordinaten fragen, aber dieser uns diese nicht sofort herausrückt, sondern erst den Benutzer fragt ob er einverstanden ist, das sieht je nach Browser unterschiedlich aus, hier ein Beispiel aus dem FireFox 6:

Wenn der Benutzer hier auf die “Zugriff erlauben” Schaltfläche klickt bekommen wir ein Objekt mit einem Haufen an Informationen rund um die Position des Benutzers. Für uns ist an dieser Stelle aber nur die Adresse des Nutzers von Bedeutung.
Moment mal, woher weiß der Browser so ich bin?
Tja, das ist eine gute Frage, auf magische Weise weiß der Browser wo wir uns aufhalten, auch wenn wir grad mit einem Desktopcomputer auf der Seite sind, der kein GPS Chip verbaut hat (im Vergleich zum Handy – wo das ja klar sein sollte) und das auch noch _ziemlich_ genau!
Im ersten Augenblick könnte man denken der Browser macht es über die IP Adresse, aber nein die IP ist _Viel_ zu ungenau im Vergleich mit den echten Koordinaten.
So sendet der Browser (Firefox) nach eurer Erlaubnis folgende Daten an Google unter dieser Adresse: https://www.google.com/loc/json
"mac_address": "01-23-45-67-89-ab", "signal_strength": 8, "age": 0, "SSID": "MyAccessPoint"
Als Google mit ihren StreetView Autos durch die Gegend gefahren ist haben die nämlich auch die Namen und die genauen Positionen von den WLAN Netzwerken gespeichert, an denen sie vorbeigefahren sind. An Hand dieser Informationen versucht Google euch nun zu orten.
Wie komme ich nun an diese Informationen?
Glücklicherweise gestaltet sich dies für uns sehr einfach:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(success, error);
} else {
alert("Not Supported!");
}
Wenn der Browser diese API kennt werden die statischen Klassen Navigator und geolocation aufgerufen und hier die Methode getCurrentPosition aufgerufen, diese bekommt als Parameter einen Verweis auf die Methode die ausgeführt werden soll. Wenn das Ganze fehlschlägt (Kein Browsersupport) soll die display_error-Methode aufgerufen werden.
Die Funktion success bekommt einen Parameter mit, der alle relevanten Informationen enthält:
<pre>
function success(position) {
alert(position.coords.latitude);
alert(position.coords.longitude);
alert(position.address.city);
}
Für uns ist für das weitere Vorgehen aber nur der Name der Stadt wichtig.
Auslesen der Wetterinformationen
Es gibt diverse offene APIs die uns die Wetterdaten zu einer angegebenen Stadt liefern können, ich habe mich an dieser Stelle für die von Google entschieden, da diese offen ist und keine Registrierung voraussetzt.
Die API ist unter folgender URL zu erreichen:
http://www.google.com/ig/api?weather=[LOCATION]&hl=[LANGUAGE]
Hier müsst ihr lediglich die gewünschte Stadt und die gewünschte Sprache einsetzen. Als Antwort bekommt ihr eine XML, die die Wetterdaten enthält, und das nicht nur für jetzt, sondern für die gesamte nächste Woche, inklusive ausgeschriebener Formulierungen wie “Teils sonnig” oder “Meistens bewölkt”.
Und wenn euch das noch nicht genug ist, bietet Google auch gleich das passende Icon zu dem Wetter, dass ebenfalls aus dem XML.Status ausgelesen werden kann. Insgesamt gibt es 22 verschiedene Wetterzustände zu denen es jeweils auch ein passendes Icon gibt.
Moment, wie komme ich nun an diese Daten?
Es besteht nun nur noch ein kleines Problem. Die ganze Abfrage erfolgt beim Client, deswegen kennt nur der Client die Position des Benutzers. Wir müssen nun aber (im Hintergrund => Ajax) einen Request an den Google Dienst absenden um an die Wetterdaten zu kommen.
Leider können wir das nicht direkt aus dem Client machen, da die Browser es verbieten, Requests an andere Domains abzusenden als an die von der das Script geladen wurde. Das ganze schimpft sich “Same Origin Policy” und soll den Benutzer unter Anderem vor Cross-Site-Scripting-Attaken schützen.
Das bedeutet, dass wir den Umweg über den Server gehen müssen und uns auf dem Server eine kleine API anlegen müssen die unserem Script auf Anfrage die passenden Daten liefert.
Dies kann man z.B so implementieren:
private const string GOOGLE_WEATHER_API_URL =
"http://www.google.com/ig/api?weather={0}&amp;amp;amp;amp;amp;amp;hl=de";
public WeatherResult GetWeahterData(string city) {
string api_url = string.Format(GOOGLE_WEATHER_API_URL, city);
try {
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(api_url);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
StreamReader reader = new StreamReader(res.GetResponseStream());
string response = reader.ReadToEnd();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(response);
string temp = xmlDoc.SelectSingleNode("xml_api_reply/weather/current_conditions/temp_c").Attributes["data"].Value;
string icon = xmlDoc.SelectSingleNode("xml_api_reply/weather/current_conditions/icon").Attributes["data"].Value;
WeatherResult wRes = new WeatherResult(temp, city);
wRes.Icon = icon;
return wRes;
} catch (Exception ex) {
return null;
}
}
Für den Response habe ich mir eine kleine Klasse gebastelt, die ich zu Json serialisiere und als Antwort zurückgebe:
public class WeatherResult {
public string Temperature { get; set; }
public string City { get; set; }
public string Icon { get; set; }
public WeatherResult(string temp, string city) {
Temperature = temp;
City = city;
}
}
Das Ganze wird von dem ApiController aufgerufen:
public class ApiController : Controller
{
//
// GET: /Api/GetWeather
public JsonResult GetWeather(string id)
{
WeatherResult res = new Tools.Weather.WeatherHelper().GetWeahterData(id);
return Json(res, JsonRequestBehavior.AllowGet);
}
}
Was wir nun noch machen müssen ist unsere neu geschaffene API über einen Ajax Request aufzurufen. Hier habe ich jQuery benutzt um das Ganze zu vereinfachen:
function get_address(position) {
var full_url = "/Api/GetWeather/" + position.address.city;
$.ajax({
url: full_url,
success: get_weather,
error: display_error
});
}
Die get_weather-Funktion auf die ich hier im Erfolgsfall verweise ist nun dafür verantwortlich die vom Server erhaltenen Daten anzuzeigen, und dank dem zu Json serialisiertem WeatherResult-Objekt kann dies auch bequem erfolgen. Hier habe ich ebenfalls jQuery genutzt um mir die benötigten Referenzen auf die DOM-Objekte zu besorgen. Ein mögliches Ergebnis des Ganzen könnte ihr hier rechts sehen.
function get_weather(address) {
$(".weather_info").html(address.Temperature == "" ? "N/A" : address.Temperature + "°C");
$(".location_info").html(address.City);
}
Sonst noch was?
Im Grunde wars das auch schon. Aber eine Kleinigkeit ist mir noch aufgefallen, und zwar kann es vorkommen, dass ihr keine Adressdaten von der Geolocation API bekommt, sondern nur Koordinaten. (Falls Google diese Daten nicht gelistet hat oder ähnliches).
In diesem Fall müsst ihr ein Reverselookup machen und die Koordinaten in eine Adresse umwandeln, da die Google Wetter API nur Ortsnamen, aber keine Koordinaten annimmt.
Glücklicherweise stellt und Google auch dafür eine API zur Verfügung, diese könnt ihr unter folgender URL erreichen:
http://maps.google.com/maps/api/geocode/json?latlng=[KOORDINATEN]sensor=true
Hier könnt ihr sogar als Parameter angeben, ob ihr XML oder Json als Antwort haben wollt. Diese könnt ihr z.B. so nutzen:
private const string GOOGLE_MAPS_REVERSE_LOOKUP_URL =
"http://maps.google.com/maps/api/geocode/json?latlng={0}&amp;amp;amp;amp;amp;amp;sensor=true";
public static string GetCityFromCoords(string coords) {
string api_url = String.Format(GOOGLE_MAPS_REVERSE_LOOKUP_URL, coords);
try {
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(api_url);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
StreamReader reader = new StreamReader(res.GetResponseStream());
string response = reader.ReadToEnd();
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
dynamic obj = serializer.Deserialize(response, typeof(object));
string city = obj.results[0].address_components[3].long_name;
return city;
} catch (Exception ex) {
return "";
}
}
Hier habe ich mir ein dynamic-Object generieren lassen aus dem übergebenen Json. Dazu habe ich diese Klasse hier genutzt.
Falls dieser Fall eintreffen sollte, braucht ihr übrigens nicht mit genauen Koordinaten bzw. Adressdaten zu rechnen, aber für unseren Zweck reicht es allemal.
Probleme mit Plesk Admin und MySQL
Es gibt Tage da zickt Plesk herum und man könnte verzweifeln weil plötzlich die Adminoberfläche, die gestern noch funktionierte, nun nicht mehr laden will und Plesk sich mit der Fehlermeldung verabschiedet:
Error: Test connection to the database server has failed because of network problems:
Failed to connect to database: Access denied for user 'admin'@'localhost' (using password: YES)
Lösung:
Zum Glück lässt sich das Problem ganz einfach aus der Welt schaffen, dazu muss man das Admin Passwort für MySQL neu setzen.
- Loggt euch auf dem Server über RemoteDesktop (oder SSH) ein
- Öffnet die my.ini aus eurem Plesk Verzeichnis ( %plesk_dir%databases\mysql\data )
- fügt den Parameter "add the parameter skip-grant-tables=1" hinzu und speichert.
- Startet den MySQL Service neu
- Verbindet euch zu der MySQL Datenbank ( "%plesk_dir%\mysql\bin\mysql.exe" mysql )
- Führt folgenden Befehl aus:
- mysql> update user set password=password('euer_passwort') where user='admin'; Wobei ihr hier euer Admin-Passwort eintragen solltet dass Plesk nutzt um sich anzumelden
- Entfernt den Parameter aus der my.ini den ihr im Schritt 3. hinzugefügt habt
- Startet dem MySQL Dienst erneu neu
Das Problem sollte sich nun erledigt haben.
Anonyme Threaduebergreifende Eventhandler
Jeder von uns uns hatte schon mit Events und den dazugehörigen Handlern zu tun. Jeder Button auf einem Formular hat z.B. einen eigenen Eventhandler, der irgendwo in der Form.Designer.cs definiert und registriert wird. Dieser sieht dann meist ca so aus:
//Registrierung
this.button1.Click += new System.EventHandler(this.button1_Click);
//Verweis / Nutzung
private void button1_Click(object sender, EventArgs e) { }
So wird jedes Mal wenn des Click-Event der Button-Instanz ausgelöst wird die registrierte Methode button1_Click Ausgeführt. Eigentlich ein schönes und einfaches System, aber wenn man eigene Klassen hat und teilweise viele Instanzen davon bildet ist es unschön, da dabei sehr viele Methoden in der Klasse entstehen und somit die Übersicht mit der Zeit verloren geht.
Deswegen gibt es für uns die Möglichkeit so genannte anonyme Methoden als EventHandler zu verwenden. Diese benötigen keine Deklaration und haben keine Referenz, somit gehen diese auch verloren, sobald diese zu Ende gelaufen sind. Benutzen kann man diese so:
this.button1.Click += delegate(object sender, EventArgs e) {
//hier steht mein Code
};
Wie man sieht ist der Code um einiges übersichtlicher geworden und kürzer geworden. Wir übergeben nun eine anonyme Methode als Handler.
Was das ganze noch etwas interessanter macht, ist dass es noch mehr Ersparnis und noch mehr Übersicht bei Threadübergreifenden EventHandlern gibt. Schauen wir uns mal an wie es normalerweise ausschaut:
//Registrierung des EventHandler zum anderen Thread
man.NewInformationUpdate += new NewInformationUpdateArrived(update_received);
//Handlermethode
public void update_received(object sender, TestInformationUpdateEventArgs ev) {
//Aufruf zu dem eigenen Thread deligieren
this.BeginInvoke(new delegateupdate(update), new object[] { ev });
}
//Definition der delegaten Methode
public delegate void delegateupdate(TestInformationUpdateEventArgs ev);
//Eigentliche Methode zum Verarbeiten des Events
private void update(TestInformationUpdateEventArgs msg) {
//...
}
Wir haben nun drei Methoden definiert, nur um ein Event zu behandeln, dies ist von Nöten, da wir hier Events verarbeiten, die aus einem anderen Thread kommen.
Hier können wir ebenfalls viel einsparen wenn wir mit delegates und anonymen Methoden arbeiten:
fd1.CompletionInformation += delegate(object se, CompletionInformationArgs ci) {
this.BeginInvoke(new EventHandler(delegate(object obj, EventArgs ea) {
CompletionInformationArgs cea = (CompletionInformationArgs)ea;
//Unser verabeitungs Code hier
}), new object[] { se, ci });
};
Hier haben wir wieder aus drei Methoden eine gemacht, naja eine richtige zumindest
und zwei anonyme. Ich finde diese Variante deutlich lesbarer und übersichtlicher, da alle wichtigen Information an einer Stelle sind.
Doch Vorsicht, diese Methode hat einen gravierenden Nachteil, da wir hier anonyme Methoden benutzen haben wir keine Referenz darauf und können somit das registrierte Event nie wieder un-registrieren. Mit anderen Worten, man muss abwegen wann man diese Abkürzung gehen kann und wann nicht.
Meiner Erfahrung nach wird der Fall überwiegen wo man diesen Weg leider nicht gehen kann, aber dann kann man versuchen ein Zwischenweg zu finden:
var handler = new EventHandler(delegate(object o, EventArgs e)
{
//handler Logik
};
Subject.NewEvent += handler;
//Diesen können wir später dann auch entfernen
Subject.NewEvent -= handler;
Auf diese Weise haben wir immernoch eine Ersparnis und behalten die volle Kontrolle über unsere Handler.
Als kleine (unschöne) Alternative können wir auch anonyme Handler loswerden, indem wir alle Handler entfernen:
public event EventHandler SomeEvent;
public void ClearEventHandlers() {
Delegate[] delegates = SomeEvent.GetInvocationList();
foreach (Delegate delegate in delegates) {
SomeEvent -= (EventHandler) delegate;
}
}
Java: SubjectDN eines X509Zertifikates zu OpenSSL kompatibel machen
Java stellt uns zum angenehmen Arbeiten mit X509-Zertifikaten die Klasse java.security.cert.X509Certificate zur Verfügung. Hier können wir uns sehr einfach über die methode .getSubjectDN().getName() den Distinguished Name des Zertifikats besorgen.
Wenn man in der Javawelt bleibt ist das auch alles ok und funktioniert Problemlos. Will man aber andere Programme wie zum Beispiel OpenSSL anbinden, bekommt man hier ein Problem, denn OpenSSL liefert einen anderen SubjectDN String als unsere Java Klasse.
Wobei mit anders meine ich, dass die Reihenfolge der ausgegebenen Parameter anders ist, hier ein Beispiel:
Ausgabe von OpenSSL (über die X509_NAME_oneline Methode):
/C=DE/ST=NRW/L=Bielefeld/O=ORG/OU=AB1/CN=130.122.146.121
Wogegen die Java-Klasse folgenden String liefert (.getSubjectDN().getName()) :
CN=130.122.146.121, OU=AB1, O=ORG, L=Bielefeld, ST=NRW, C=DE
Die Reihenfolge scheint genau umgekehrt zu sein und auch die Trenner sind anders, und wenn man den String nun als Auth-String benutzen will bekommt man ein Problem.
Leider bietet uns Java hier keine Möglichkeit den String anders zu sortieren, man kann zwar über die Methode getSubjectX500Principal.getName(String format) einen RFC als Parameter mitgeben, welcher die Sortierung etwas anpasst, aber teilweise noch komischere Ergebnisse liefert.
Hier bleibt einem nun leider nichts anderes übrig, als selbst einzugreifen und die Sortierung selbst vorzunehmen. Dazu habe ich eine kleine Klasse geschrieben die das für euch erledigt. Diese hat zwei Methoden, eine die einen OpenSSL Kompatiblen String ausgibt und eine bei der ihr die Reihenfolge der Parameter selbst bestimmen könnt:
/**
* Teilt die Informationen aus dem X509Certificate.SubjectDN in die
* einzelnen Bestandteile auf und macht diese einzeln verfügbar
* @author sebastian gross blog.bigbasti.com
*/
public class CertificateDataSeperator {
private String _C = "";
private String _ST = "";
private String _L = "";
private String _O = "";
private String _OU = "";
private String _CN = "";
private String _EMAILADRESS = "";
/**
* Erstellt eine neue instanz
* @param cert Das Zwetifikat aus dem die Daten gelesen werden sollen
* @param seperator Die Zeichenfolge ander die parameter
* getrennt werden sollen, bei Java ist Standard ", "
*/
public CertificateDataSeperator(X509Certificate cert, String seperator){
String [] subjectDNData;
subjectDNData = cert.getSubjectDN().getName().split(seperator);
seperateData(subjectDNData);
}
/**
* Duchläuft das Array und ordnet die Informationen
* @param data Array mit dem SubjectDN String
*/
private void seperateData(String [] data){
for(String s : data){
if((s.toUpperCase()).startsWith("C=")){
_C = s;
}else if((s.toUpperCase()).startsWith("ST=")){
_ST = s;
}else if((s.toUpperCase()).startsWith("L=")){
_L = s;
}else if((s.toUpperCase()).startsWith("O=")){
_O = s;
}else if((s.toUpperCase()).startsWith("OU=")){
_OU = s;
}else if((s.toUpperCase()).startsWith("CN=")){
_CN = s;
}else if((s.toUpperCase()).startsWith("EMAILADRESS=")){
_EMAILADRESS = s;
}
}
}
/**
* Generiert einen angepassten SubjectDN String
* @param params Die gewünschten Werte in der gewünschten Reihenfolge als Array
* @param seperator Die Zeichenfolge, die zum Trennen der Parameter genutzt werden soll
* @return Einen angepassten SubjectDN String
*/
public String getNewDNString(String [] params, String seperator){
StringBuilder sb = new StringBuilder();
for(String s : params){
if((s.toUpperCase()).equals("C")){
sb.append(_C);
}else if((s.toUpperCase()).equals("ST")){
sb.append(_ST);
}else if((s.toUpperCase()).equals("L")){
sb.append(_L);
}else if((s.toUpperCase()).equals("O")){
sb.append(_O);
}else if((s.toUpperCase()).equals("OU")){
sb.append(_OU);
}else if((s.toUpperCase()).equals("CN")){
sb.append(_CN);
}else if((s.toUpperCase()).equals("EMAILADRESS")){
sb.append(_EMAILADRESS);
}
if(!s.equals(params[params.length-1])){
sb.append(seperator);
}
}
return sb.toString();
}
/**
* Erzeugt einen DN String in dem Format, wie ihn auch die
* X509_NAME_oneline Methode von OpenSSL generiert, dies ist
* auch das Format, welches von dem Exim Email Server genutzt wird.
* Das Forman ist wiefolgt: /C=$/ST=$/L=$/O=$/OU=$/CN=$
* @return OpenSSL
*/
public String getOpenSSLCompatibleDNString(){
StringBuilder sb = new StringBuilder();
sb.append("/").append(_C).append("/").append(_ST).append("/").append(_L).append("/")
.append(_O).append("/").append(_OU).append("/").append(_CN);
//EMAILADRESS Ist ein optionales Feld und könnte nicht gesetzt sein
if(_EMAILADRESS != null){
sb.append("/").append(_EMAILADRESS);
}
return sb.toString();
}
public String getC() {
return _C;
}
public String getCN() {
return _CN;
}
public String getEMAILADRESS() {
return _EMAILADRESS;
}
public String getL() {
return _L;
}
public String getO() {
return _O;
}
public String getOU() {
return _OU;
}
public String getST() {
return _ST;
}
}
Mit dieser kleinen Klasse ist es sehr einfach einen OpenSSL kompatiblen SubjectDN zu erhalten:
X509Certificate myCert = session.getClientCertificate(); CertificateDataSeperator sep = new CertificateDataSeperator(myCert, ", "); String dn = sep.getOpenSSLCompatibleDNString();
Wenn man selbst die Reihenfolge der parameter bestimmen will kann man das z.B. so machen:
X509Certificate myCert = session.getClientCertificate();
String [] myParams = new String[]{"O", "CN", "L"};
CertificateDataSeperator sep = new CertificateDataSeperator(myCert, ", ");
String dn = sep.getNewDNString(myParams, "|");
Nun soll erst O, dann CN und zum Schluss L ausgegeben, jeweils von einem | (pipe) getrennt.
Das Ganze lässt sich natürlich je nach verwendeten optionalen SSL Parametern noch beliebig erweitern, und ist eine einfache und schnelle Methode um den SubjectDN neu zu sortieren.
Twitter for iPhone: “Quick-” bzw. “Trends”-Bar entfernen
Falls ihr ein iPhone besitzt und die Twitter for iPhone App nutzt werdet ihr seit dem letzten Update etwas sehr sehr nerviges festgestellt haben, nämlich die neue halbdurchsichtige Leiste über der Timeline.
Diese so genannte Trends-Bar kann man leider nicht über die Einstellungen der App entfernen. Was noch dazu kommt, ist dass das was diese Leiste anzeigt (für mich) überhaupt keinen Sinn ergibt und einfach nur stört!
An dieser Stelle gibt eine gute Nachricht für alle die ihr iPhone gejailbraked haben, denn die können im Cydia Store die Erweiterung namens "Twizzler" installieren und müssen diese nutzlose Leiste nie wieder sehen!
Falls ihr euer iPhone nicht gejailbraked habt, bleibt euch nur die Wahl eine ältere Version der Twitter App zu installieren oder zu einem anderen Twitter Client zu wechseln!
.NET WebServices anlegen und konsumieren
Letzte Woche haben uns angeschaut, wie man unter Java einen WebService anlegen kann und diesen dann auch verwenden (konsumieren), heute gucken wir wie das Ganze unter .NET funktioniert und legen den selben Service auch hier mal an.
HINWEIS: Ich verwende hier Visual Studio 2010 Ultimate, soweit ich weiß können die C#/VB.NET Express Versionen keine WebServices anlegen, dazu solltet ihr den Visual Web Developer Express herunterladen.
Klickt also auf Datei>Neu> Projekt und Wählt "Web" bzw. "Internet" unter C#. Damit euch das WebService Projekt überhaupt vorgeschlagen wird müsst ihr die verwendete Frameworkversion auf 2.0 stellen (Siehe Bild), wählt dann ASP.NET Webdienstanwendung. Benennt das Projekt nun "USerDB" (genauso wie im Java Beispiel) und klickt auf OK.
Wenn das Projekt angelegt ist, solltet ihr ungefähr folgendes sehen:
Im Grunde haben wir hier bereits einen funktionierenden WebService den wir auch schon ausführen können. Ähnlich wie beim Glassfish unter Java bekommen wir dann eine Testseite angezeigt auf der alle Methoden des Dienstes sichtbar sind und getestet werden können:
Bevor wir hier aber weiter machen implementieren wir erst einmal unsere Methode aus dem Java Beispiel:
Wenn wir den Service erneut ausführen und auf der Testseite unsere Funktion wählen bekommen wir ein ähnliches Bild zu sehen wie unter Java:
Auch hier wird anschließend ein SOAP Request für uns erzeugt und an den Service gesendet, dieser wird dann inklusive dem SOAP Response auf der Ergebnisseite angezeigt.
Damit haben wir den WebService bereits fertiggestellt, machen wir uns nun also daran diesen zu konsumieren.
Fügen wir als erstes ein neues Konsolen Projekt zu unserer Projektmappe hinzu (Bild 6). Nachdem das Projekt angelegt ist fügen wir einen neuen Webverweis hinzu (Bild 7). In dem Dialog der nun auftaucht kann man die Quelle für die Schnittstellenbeschreibung (WSDL) wählen. Man kann hier direkt eine Referenz auf das Projekt in der Projektmappe anlegen. Wir wollen aber direkt die URL der WSDL in der Obere Textfeld angeben.
An die WSDL kommt man übrigens sehr einfach, indem man die Testerseite des WebServices aufruft und dort auf den Link namens "Dienstbeschreibung" klickt, diese URL müssen wir hier angeben. Das Tool analysiert die WSDL und zeigt uns eine Übersicht über die Operationen dieses Dienstes an (Bild 8), hier tragen wir noch den gewünschten Namen ein (bei mir "UserDBService") und klicken auf Verweis hinzufügen.
An dieser Stelle hat die IDE für uns den harten Teil schon erledigt und alle nötigen Klassen und Methoden generiert, die nötig sind um unseren WebService zu nutzen. Implementieren wir also unsere Funktion aus dem letzten Artikel:
Die interessanten Stellen habe ich hier markiert, so sollte man nicht vergessen eine Referenz auf unseren WebService zu setzen bevor man loslegt.
Im Vergleich zu Java bekommen wir hier sogar noch mehr fürs Geld, denn in den generierten Klasse finden wir zu unseren Methoden auch schon asynchrone Aufrufe inklusive der Completed-Events:
Und da die Klasse von System.Web.Services.Protocols.SoapHttpClientProtocol erbt, bekommen wir auch alle anderen wichtigen Properties und Funktionen die man in einem web-bezogenen Projekt benötigen könnte
Die generierten Klassen und auch die WSDL findet ihr übrigens im Projektverzeichnis in dem Ordner "Web References".
Führen wir nun unser Programm aus, erwartet uns das Selbe wie in unserem Java Beispiel:
Natürlich muss der Dienst nicht unbedingt in .NET sein, so könntet ihr genauso gut die WSDL aus unserem Java Beispiel verwenden um einen .NET Consumer für einen Java WebService zu erstellen .
Java: Webservice anlegen und konsumieren
Aus gegebenen Anlass gibt es heute mal ein kleines Tutorial wie man in Java ein WebService anlegt und diesen dann benutzt. Das ganze wird mit NetBeans 9.6.1 als IDE und einem Glassfish als Application Server realisiert.
Erstmal kurz: was ist ein WebService?
Ein WebService unter Java ist (vergleichbar mit RMI) eine Art Dienst, der sowohl von anderen Anwendungen als auch von Webseiten genutzt werden kann. Anders als eine gewöhnliche Webseite besitzt ein WebService kein eigenes Benutzerinterface sondern stellt nur in einer Schnittstellenbeschreibung seine Dienste nach außen zur Verfügung.
Diese Schnittstellenbeschreibungen werden in WSDL Dateien (Web Service Description Language) gespeichert, diese sind XML Dokumente, die genau beschreiben, welche Methoden der WebService bietet und wie diese angesprochen werden. Glücklicherweise müssen wir diese WSDL Dateien nicht selbst erstellen sondern können uns diese generieren lassen, aber dazu gleich mehr.
Kommunikation mit dem WebService
Unser WebService wird dann später über das SOAP Protokoll (Simple Object Access Protocol) kommunizieren, dieses ist ebenfalls XML basierend und auch hier müssen wir nicht selbst Hand anlegen sondern lassen alles für uns generieren
In diesem Beispiel werden wir einen einfachen WebService erstellen und eine gewöhnliche App, die dann mit dem WebService kommunizieren wird. Wir werden hier die NetBeans IDE 6.9.1 benutzen, diese wird uns hierbei viel Arbeit abnehmen, sodass der unangenehme Teil der Arbeit klein gehalten wird
Downloads - falls nicht bereits vorhanden
NetBeans - Download
Glassfish - Download
JDK - Download
Legen wir mal los
Als Erstes sollten wir damit starten den WebService zu erstellen. Dazu legen wir ein neues Web Application Projekt an und nennes es "CheckUser" die Restlichen Dialoge könnt ihr unverändert lassen und schließlich auf Fertigstellen klicken.
Nun sollte das Projekt angelegt sein und ihr solltet den Quelltext der index.jsp vor euch haben. Diese Datei benötigen wir nicht da wir einen WebService erstellen wollen, deswegen könnt ihr diese Löschen, wenn ihr mögt.
Nun müssen wir einen neuen WebService anlegen, das machen wir, indem wir auf unser Projekt rechtsklicken und über "New -> WebService" wählen. Nennt den Service "UserDB" und packt es in das "com.bigbasti.db.user.service" Package rein. Zum Schluss macht ihr noch den Haken bei "Implement Web Service as Stateless Session Bean" rein und klickt auf Fertig.
Was ist nun passiert? NetBeans hat für uns nun einen neuen Ordner im Projekt namens "Web Services" angelegt und auch eine neue Enterprise Bean die so heißt wie unser Service ist im Projekt aufgetaucht. NetBeans kümmert sich größtenteils selbst um diese automatisch generierten Beans so sollten wir erst einmal die Finger davon lassen.
Nun solltet ihr die UserDB.java vor euch aufgeklappt sehen, diese Klasse ist mit der Annotation Stateless und WebService markiert. Da ein WebService mindestens eine Methode (Operation besitzen muss wird euch wahrscheinlich ein Fehler angezeigt. Das wollen wir nun ändern und fügen dem Service eine neue Operation hinzu.
Macht dazu einen Rechtsklick innerhalb der UserDB Klasse und klickt auf "Insert Code..." wählt hier nun "Add Web Service Operation...". In dem nun angezeigten Fenster solltet ihr eure Operation "checkUserName" nennen und den Rückgabetyp auf Boolean stellen. Desweiteren fügt ihr einen Parameter hinzu und tauft ihn "userName".
Bestätigt das Fenster mit "OK" und ihr solltet folgendes zu sehen bekommen:
Diese neue Methode ist mit "WebMethod" annotiert und weist so darauf hin, dass auf diese von außen zugegriffen werden darf. Alle Parameter sind ebenfalls annotiert sodass diese zugreifbar sind. Diese Annotation ist wichtig, damit wir später WSDLs und Requests generieren können. Beim Arbeiten mit dieser Methode könnt ihr diese aber für euch ausblenden
Diese erzeugte Methode funktioniert genauso wie jede andere Java Methode, somit könnt ihr mit ihr wie gewohnt arbeiten. Diese Methode soll für uns prüfen, ob ein Benutzername bereits von jemandem verwendet wird. Da wir das simel halten wollen nutzen wir eine LinkedList als Datenbank
So können wir eine einfache Prüffunktion implementieren:
Nun haben wir einen WebService mit einer Operation. Diese wollen wir nun mal testen. Dazu verwenden wir zunächst die Test-Tools vom Glassfish Server. Dazu müssen wir unsere Web Application zunächst Deployen (Veröffentlichen), das machen wir ganz einfach indem wir das Projekt rechts-klicken und dann "Deploy" wählen. Nun wird der Glassfish Server gestartet, die Anwendung kompiliert und auf dem Server veröffentlicht.
Wenn dieser Vorgang erfolgreich war könnt ihr im "Services"-Tab unter "Servers->GlassFish Server 3" euere Web App sehen (Siehe Bild 11). Nun wollen wir mal gucken ob unsere Operation funktioniert, dazu macht ihr einen Rechtsklick auf dem "GlassFish Server 3" und wählt View Admin Console (Bild 12).
Nun könnt ihr euch einen Kaffee holen...
Wenn die Admin Console irgendwann offen ist, und es kann dauern, klickt ihr auf "Anwendungen" und wählt eure "CheckUser" App. In dem "Allgemein"-Tab seht ihr nun unten eine Tabelle mit allen Komponenten eurer App, sucht hier unseren Service "UserDB" und klickt auf "Endpunkt anzeigen".
Hier seht ihr nun alle wichtigen Informationen zu eurem Web Service, für uns ist im Moment die Tester URL wichtig (Roter Pfeil) Diese öffnen wir mal in einem neuen Tab.
Was wir hier sehen ist die Testseite für unseren Dienst. Hier hat der Glassfish für jede von uns erstellte Operation (momentan haben wir nur eine) eine Testmethode erstellt. Diese Testmethode macht nichts anderes, als eine SOAP Nachricht zu generieren und an unseren Dienst zu senden und uns die Antwort von diesem anzuzeigen.
Probieren wir das Ganze mal aus, probiert einfach mal ein paar Namen durch und verwendet auch ab und zu einen der von uns Reservierten Namen, ihr werdet feststellen, dass unsere Methode super funktioniert.
Dabei zeigt das Test-Tool auch an welche SOAP Requests es generiert und welche Antworten unser Service gibt, so wird folgender SOAP Aufruf getätigt um zu prüfen, ob der Name vorhanden ist:
Hier finden wir alle unsere Werte aus der Klasse wieder. Der Name der Operation die wir mit diesem Request aufrufen wollen (Rot), diese Operation benötigt einen Parameter namens "userName" den wir hier angeben (Blau) und der Wert für diesen Parameter (Orange). Das Ganze ist dann noch in einen SOAP Envelope verpackt und wird so zum Server geschickt.
Dieser führt die gewünschte Operation aus und gibt uns eine Antwort:
Die Response ist weitestgehend genauso aufgebaut die der Request. Der Knoten hat nun noch "Response" an den Namen der Methode/Operation angehängt bekommen (Rot) und auch das Ergebnis der Operation ist angegeben (Blau).
Das Ergebnis können wir nun mit Hilfe eines Parsers auslesen, aber dazu gleich mehr. Nach dem Test wissen wir nun, dass unser dienst läuft und auch dass die Operation zuverlässig arbeitet, Zeit unsere "Consumer"-Applikation zu bauen.
Web Service benutzen (konsumieren)
Nun haben wir also einen Web Service laufen, und natürlich wollen wir diesen nun auch nutzen (konsumieren ist das Fachwort), aus einer App mit der sich Benutzer irgendwo registrieren können. Diese App muss natürlich irgendwie prüfen, ob der gewünschte Benutzername schon vorhanden (bzw. bereits benutzt) ist, das wird die App über unseren Web Service UserDB erledigen.
Legen wir zunächst ein neues Projekt in NetBeans an, diesmal eine normale Java Application, die wir "RegisterUser" nennen.
Nun müssen wir unserer frischen Applikation beibringen mit einem WebService zusammen zuarbeiten. Das macht man über so genannte Schnittstellen, denn diese beschreiben genau was möglich ist und wie man da dran kommt. Schnittstellen haben hier das Format WSDL, und wenn ihr gut aufgepasst habt habt ihr sicher schon gesehen dass der GlassFish für unser Service eine WSDL erstellt hat.
Geht die Schritte in den Bildern 13,14 und 15 durch (falls ihr den Tab schon geschlossen habt), sodass ihr wieder die URLs des Services angezeigt bekommt. Der Blaue Pfeil im Bild 15 zeigt euch die URL der WSDL, diesen link klickt ihr bitte an, sodass ihr die XML Datei zu Gesicht bekommt.
Auf den Inhalt werde ich hier nicht weiter eingehen, das könnt ihr euch ja bei Wiki durchlesen
Die URL zu der WSDL sieht bei mir wie folgt aus:
http://localhost:8080/UserDBService/UserDB?wsdl
Diese könnt ihr mal in die Zwischenablage kopieren, da wir diese gleich benötigen um einen Web Service Client zu erstellen. Das machen wir mit einem Rechtsklick auf das Projekt und wählen "New->Web Service Client". Hier wählt ihr als Bezugsort für die WSDL "URL" aus und fügt die URL ein. Als Paketnamen habe ich "com.bigbasti.user.register" gewählt.
NetBeans lädt nun die WSDL herunter und erzeugt daraus für euch die nötigen Klassen für den Zugriff auf diese Schnittstelle, deswegen sind einige neue Dateien bei euch im Projektordner aufgetaucht (Bild 24). Der etwas ausgegraute Ordner "Generated Sources" enthält hierbei die wichtigen Klassen und Methoden für das Erzeugen von SOAP Requests und für das Lesen von Responses. Diese Dateien solltet ihr auch möglichst nicht anfassen.
Nun sind wir komplett gerüstet um unseren Web Service zu nutzen. Der Code der dafür geschrieben werden muss ist auch nicht weltbewegend:
Als Erstes sind die imports wichtig, damit wir Zugriff auf die generierten Klassen haben. Mit denen können wir nun (Blaue Pfeile) die nötigen Instanzen der Service Objekte anlegen. Ist das erst geschafft können wir mit unserem UserDB Objekt, das den Service repräsentiert bequem arbeiten und die Service Operationen aufrufen, als ob es eine gewöhnliche kleine Klasse wäre (Oranger Pfeil).
Nachdem wir unsere Operation aufgerufen haben bekommen wir eine Antwort ob der Name schon benutzt wird oder nicht. So könnte es aussehen wenn man das obige Programm startet:
Wie man sieht funktioniert das Ganze sehr gut und es hat gerade mal ein paar Minuten gedauert diesen Service zu erstellen. Ihr könnt natürlich selber weiter herumspielen und weitere komplexere Operationen hinzufügen, die mit echten Datenbanken kommunizieren.












































