Windows-Authentifizierung zeigt ständig Anmelde-Bildschirm

Heute habe ich bei einem Kunden den Fall gehabt, dass trotz dass Windows-Anmeldung im IIS 7 konfiguriert war, und im korrekt angezeigten Browser-Anmelde-Dialog die richtigen Anmeldedaten eingegeben wurden, keine Anmeldung möglich war.

Vielmehr ist 3 mal der Anmelde-Dialog erschienen und danach kam die HTTP-Fehlermeldung 401.2 (oder war’s 401.1?).

Die Lösung wurde dann von einem wirklich kompetenten Administrator beim Kunden geliefert: Die Authentifizierungsprovider waren in der Reihenfolge „Negotiate“ und dann „NTLM“.

Als wir NTLM ganz nach oben geschoben haben, ging alles.

windows-authentication-not-working
windows-authentication-successfully-working

OMG!

Meine Erlebnisse mit den „Dynamic Cloud Servern“ von 1und1

Das hier ist eine Geschichte, geprägt von Freud und Leid. Weil ich sie hier aufschreibe, ist das Leid im Vordergrund. Primär schreibe ich sie zur Selbst-Therapie auf und dass ich mich in einigen Jahren noch auf was berufen kann, wenn mich meine Erinnerung bei der Provider-Wahl zu trügen versucht.

Wir haben zurzeit 3 Webserver, alle mit dem Betriebssystem Windows Server von Microsoft. Einen Server für Zeta Uploader (ein Tool um große Dateien online zu versenden), einen Server für verschiedene Kunden-Websites, und einen Server für die Produktwebsites zu Zeta Producer (ein Content-Management-System) und Zeta Test Management (ein Test-Management-System).

Bis vor 2 Wochen waren alle Server über Jahre hinweg bei 1und1 gehostet. Seit zwei Wochen sind nun 2 der drei Server bei Internet 24 gehostet.

Wir haben über ein Jahr lang gelitten, geschwitzt, geflucht, gebettelt, gehofft und immer wieder Kunden über Downtimes hinweg versucht zu tröste (ich glaube wir waren der beste Nutzer von Uptime Robot). Am Ende hat alles nichts genutzt und wir mussten umziehen.

Was lief alles schief

Die Idee der „Dynamic Cloud Server“ klingt verlockend: Virtualisierte Server (mit Xen) mit schöner Admin-Web-GUI bei der ich via Schieberegler diverse Einstellungen wie RAM, CPU und Festplatte so konfigurieren kann, dass ich sie an unsere Bedürfnisse anpassen kann.

In der Praxis war genau das Gegenteil der Fall: Die Server reagierten träge, langsam und mit häufigen Ausfällen. Meine starke Vermutung ist, dass die Host-Rechner (also die physikalischen Computer, auf denen die virtuellen Computer laufen) so maximal ausgelastet sind, dass ich mit den Schiebereglern zwar meine Wünsche angeben kann, jedoch diese nichts mit der tatsächlichen Ressourcen-Zuteilung in der Praxis zu tun haben. Doch selbst eine schlechte Performance hätte ich verkraftet, wären da nicht die ständigen/häufigen Ausfälle gewesen.

Unser Zeta-Uploader-Server ist die rühmliche Ausnahme: Der läuft weiterhin bei 1und1, und zwar wie die sprichwörtliche 1: Er ist nicht der schnellste, dafür ist er quasi immer erreichbar und quasi nie down. Dieser Server läuft, so wie ich es wohl verstanden habe, auf einer Xen-5.5-Umgebung. Keine Treiber-Issues, nix.

Unsere beiden anderen Server liefen auf einer Xen-6.x-Umgebung.

Und genau diese beiden Server liefen wie Dreck. Nämlich nach Lust und Laune des Hosters. Meistens am Wochenende waren sie nicht erreichbar, erst am Montag, nachdem wohl ein Techniker die Hosts neu gebootet/whatever damit gemacht hat. Das ganze lief regelmäßig, pünktlich zum Wochenende so ab:

  • Server antwortet nicht mehr.
  • Control-Center (Web-Oberfläche) erlaubt es nicht den Server aus- und wieder einzuschalten.
  • Hotline schafft es nicht, Server aus- und wieder einzuschalten.
  • 2nd-Level-Kollegen von Hotline schaffen es nicht, Server aus- und wieder einzuschalten.
  • Administratoren schaffen es dann (nur Werktags!) wohl irgendwie, den Server auszuschalten.
  • Ich entdecke zufällig, dass ich Server jetzt wieder hochfahren kann.
  • Ich fahre Server via Control-Center hoch.
  • Server startet natürlich nicht normal, sondern im Rescue-Modus.
  • Ich boote neu im normalen Modus.
  • Server booten in Zeitlupe.
  • Nach 40 Minuten(!) warten ist Server dann gebootet.
  • Server läuft immer noch in Zeitlupe.

Die ersten zig Male habe ich immer versucht, Fehler bei mir selbst zu finden und alles mögliche probiert, leider erfolglos. Irgendwann haben wir dann einen „Platin-Support“ für etwa 10 Euro pro Monat mit dazu gebucht. Effekt war, dass die Warteschleife umgangen wurden, die Supporter dahinter waren genau so höflich und kompetent wie beim Nicht-Platin-Support. Also höflich und hilfsbereit waren sie wirklich, kompetent im Sinne von, dass sie etwas bewirken konnten, waren sie leider nicht. Es hieß am Wochenende dann oft „das müssen wir an den 2nd-Level-Support eskalieren lassen“ (oder so ähnlich), mit dem Zusatz „das kann schon Montag werden, bis es wieder läuft“. Arrgh. Da saß ich immer wie auf Kohlen.

Schön (im sarkastischen Sinne) waren anschließend auch immer die belanglosen, wohl von Robotern erstellten E-Mail-Nachrichten:

Ihr Dynamic Cloud Server ist wieder wie gewohnt erreichbar.

Bitte entschuldigen Sie die entstandenen Unannehmlichkeiten.

Wir wünschen Ihnen weiterhin viel Spaß mit unseren Produkten.

Es hat viele Versuche gedauert, bis 1und1 überhaupt zugegeben hat, dass der Fehler bei ihnen liegt. Es hieß dann lapidar „Es lag eine Störung im Hostsystem vor“. (Ob so was hier [als Bild] wohl repräsentativ ist?)

Lösungsansätze

Irgendwann habe ich dann angefangen, mich weiter zu erkundigen und bin auf die Möglichkeit gestoßen, die Xen-Netzwerk-Treiber in meinem Gast-Windows zu aktualisieren. Da habe ich wirklich viel Zeit investiert und nach viel nachbohren dem 1und1-Support wenigstens die Xen-Versionsnummer entlocken können. Sogar einen Bekannten („Facebook-Freund“), der bei Citrix arbeitet (die Firma hinter Xen) habe ich mit den Fehlern und der Thematik konfrontiert und er hat mir einige Tipps geben können, die gefühlt geholfen haben. Objektiv waren jedoch immer wieder die Aussetzer der Server.

Ich habe meinen Bekannten auch gefragt, ob man mit Xen 6 überhaupt ein stabiles System hinbekommen kann, worauf er mir mit Screenshots von Uptime-Charts von 190+ Tagen geantwortet hat. Es geht also. Xen kann das, die Admins scheinen es hier wohl nicht hin zu bekommen. Warum auch immer, ich vermute einfach Kostendruck.

Eines Tages habe ich dann einen ehrlichen Supporter am Telefon gehabt, der mir am Ende sagte „Ach, wissen Sie, Xen und Windows, das ist auf Dauer einfach nicht stabil hin zubekommen“. Der Tenor war für mich „Damit müssen Sie leben, seien Sie froh, dass es überhaupt läuft“. WTF? Dass unser Xen-5.5-Server stabil läuft ist für mich ein Beweis, dass es durchaus funktionieren kann, ebenso wie die Aussagen meines Bekannten.

Lösung

Es musste also eine Lösung her, und die war eindeutig außerhalb von 1und1 zu sehen. Doch wie vorgehen, um nicht vom Regen in die Traufe zu kommen?

Jeder Hoster, den ich mir so angeschaut habe hatte Vor- und Nachteile. Mal zu groß, mal zu klein, mal keine Windows-Server und mal irgend was anderes seltsames. Mein geschätzter Kollege M. hat dann nach viel Recherche und einigen Telefonaten den Hoster „Internet 24“ in Dresden ausfindig gemacht. Der ist groß genug um saubere Technik und Kompetenz zu liefern und gleichzeitig persönlich genug um immer direkt einen kompetenten Techniker am Draht zu haben.

Seit ca. 2 Wochen laufen jetzt also unsere Server bei Internet 24. Noch zu kurz um eine aussagekräftige Bewertung abzugeben, doch bereits der Anfang war geprägt von lauter positiven Eindrücken, die auch nach dem Kauf weiter anhielten, bis heute.

Epilog

Web-Hosting ist halt wohl ein genau so kaputter Markt wie alle anderen von BWL-ern „optimierten“ Branchen auch. Damit muss ich halt wohl leben und immer wieder mal zum „Server-Nomaden“ werden, wenn ein Hoster zu groß wird und die Qualität nachlässt.

Wenn 1und1 unsern auf Xen 5.5 laufenden Server nach Xen 6.x upgradet gehe ich davon aus, dass ich den auch umziehen muss, weg von 1und1.

Die Server bei Internet 24 sind schöne Intel-Xeon-Rechner mit viel RAM und Hewlett-Packard-RAID; der Support von Internet 24 ist erstklassig und tatsächlich per E-Mail Tag und Nacht sehr schnell und (fast immer) sehr kompetent zu erreichen.

Ich bin richtig glücklich mit Internet 24 und hoffe, das bleibt auch so.

Update 2013-11-03

Ich habe mir mal die Pingdom-Alerts angeschaut und ausgewertet:

webserver-downtimes-1und1

(Hier als XLSX-Datei)

Es waren im Schnitt 10 Downtimes pro Monat. Pro Monat! Und das waren nur die, die Pingdom auch gemessen hat. Alles unter 5 Minuten ist da noch gar nicht dabei.

Update Mai 2018

Wir sind jetzt inzwischen fünf Jahre bei „Internet 24“. Das lief dort alles wesentlich stabiler als beim vorherigen Provider.

Unsere Hardware-Server sind inzwischen in die Jahre gekommen, und müssen irgendwann ausgetauscht werden.

Da „Internet 24“ inzwischen (mehrfach?) verkauft wurde, unsere Server inzwischen (mehrfach?) physikalisch in andere Rechenzentren umgezogen wurden, es ein paar wenige technische Peinlichkeiten gegeben hat (z. B. eine Firewall, die auf einmal wieder geschlossene Ports geöffnet hatte), und der Support sich in letzter Zeit auch von persönlichen Ansprechpartnern, hin zu unpersönlichen Web-Ticket-Masken hin geändert hat, haben wir uns entschieden, nochmals umzuziehen.

Die Wahl fiel dabei nach viel Nachdenken, Recherchieren, Ausprobieren und Testen auf Amazon Web Services, AWS.

Wir haben einen Kunden/Partner, der seit über 10 Jahren mit AWS sehr zufrieden ist, und uns die Thematik ausführlicher dargelegt hat. Inzwischen haben wir schon ein paar Linux- und Windows-Server bei AWS, und die Stabilität, Geschwindigkeit und Zuverlässigkeit sind atemberaubend.

Wenn man genau weiß, was man sich zusammen konfiguriert, sind nach unserer Erfahrung auch die Kosten sehr wettbewerbsfähig.

Gegen Mitte/Ende 2018 wollen wir alle Server zu AWS umgezogen haben.

Von Windows 7 aus via ODBC auf eine benannte SQL-Server-2000-Instanz zugreifen

Heute ist Legacy-Tag!

Issue

Für ein Projekt müssen wir von Microsoft Office Access 2010 auf einen SQL Server 2000 via ODBC zugreifen.

Die Herausforderung ist, dass der SQL Server 2000 als benannte Instanz („Named instance“) auf dem Server installiert ist (Windows 2008 R2). Also so was wie „MEINSERVER\SQL2000“, anstatt nur „MEINSERVER“.

Lokal auf dem Server kann ich wunderbar auf den SQL Server 2000 zugreifen, alleine via Netzwerk von einem PC mit Windows 7 ging es nicht. Es kam immer wieder die Meldung, dass keine Verbindung hergestellt werden konnte.

Lösung

Die Lösung war dann im Microsoft-Knowledge-Base-Artikel „How to connect to SQL Server by using an earlier version of SQL Server“ zu finden:

Vom Prinzip via cliconfg.exe-Programm auf dem Client einen Alias zu IP-Adresse und Port des SQL Server 2000 machen und dann über den Alias zugreifen.

Zitate aus der Lösung:

Server:

Determine the TCP/IP port number of the instance of SQL Server. To do this, use one of the following methods, depending on which version of SQL Server that you are running.

  1. On the server that is running SQL Server 2000, start the Server Network Utility. To do this, click Start, click All Programs, click Microsoft SQL Server, and then click Server Network Utility.
  2. Click the General tab, and then select the instance that you want from the Instances list.
  3. Click TCP/IP, and then click Properties. The TCP/IP port number for this instance is shown. Note this number so that you can use it later.

Client:

Configure the server alias on the client computer. To do this, use one of the following methods, depending on your version of SQL Server.

  1. Start the Client Network Utility. To do this, click Start, click Run, type cliconfg.exe, and then press Enter.
  2. On the General tab, verify that TCP/IP appears in the list under Enabled protocols by order.
  3. Click the Alias tab, and then click Add.
  4. Under Network libraries, select TCP/IP.
  5. In the Server name text box, type the IP address of the server that is running SQL Server 2000.
    Note The IP address that you type here is the one that is configured to use the TCP/IP port number.
  6. Click to clear the Dynamically determine port check box, and then type the port number of the instance of SQL Server 2000 in the Port number text box.
  7. Type a name in the Server alias text box, and then click OK.

Dann ging die ODBC-Verbindung („Verbindungstest“ war erfolgreich).

MySQL-Datenbanken auf einen anderen Server umziehen

Da ich jetzt festgestellt habe, dass es eine doofe Idee ist, MySQL-Datenbanken durch schlichtes XCOPY-Deployment auf einen neuen Server zu verschieben (InnoDB-Tabellen werden scheinbar nur teilweise verschoben), habe ich diesen Artikel hier gefunden, der beschreibt, wie es geht:

How to Back Up and Restore a MySQL Database

Prinzipiell ganz einfach, alles via MySQL-Befehlszeile:

  1. Auf Quellserver ein mysqldump.exe-Aufruf auf die zu verschiebende Datenbank.
  2. Das Ergebnis (SQL-Datei) via FTP auf den Zielserver kopieren
  3. Auf dem Zielserver via phpMyAdmin eine neue, leere Datenbank als Ziel anlegen
  4. Auf dem Zielserver via mysql.exe die kopierte SQL-Datei einlesen.

Fertig.

Creating an XtraReports report that works both with Microsoft SQL Server and VistaDB

Currently I am extending and enhancing the reporting system for our test management tool Zeta Test.

In this article I will describe the issues I discovered (and solved) when trying to design one single report that works with both Microsoft SQL Server and VistaDB.

Creating a report

I decided to go with XtraReports from DevExpress as the reporting system.

The basic design steps creating and deploying a report with XtraReports is similar to methods when working with e.g. Microsoft SQL Server Reporting Services:

  1. Create a new report in the designer of XtraReports.
  2. Specify connection string/settings (using the SqlClient namespace classes).
  3. Specify tables/SQL queries to use as the data source for the report.
  4. Design your report graphically.
  5. Save the report to a .REPX file.
  6. Pack the .REPX file into the deployment setup.
  7. Ship the setup to the customers.

Displaying a report

In Zeta Test I had to perform the following steps when displaying a report:

  1. Load the report.
  2. Replace the design-time connection string by the connection string to the currently used database of the currently loaded project in Zeta Test.
  3. Display the report in a print-preview form.

Two databases

Since a project in Zeta Test can either use Microsoft SQL Server or VistaDB as the back-end database, I wanted to design a single report so that it can be used with both back-end databases.

Inially I planned to simply design the reports with an OleDB connection to the SQL Server database and then later when displaying the report for a VistaDB database, simply switch the connection string to the VistaDB database.

Unfortunately there is no OleDB provider for VistaDB. Therefore this solution did not work. Another idea was to simply leave the SqlClient classes that were used when designing the report as they are, only changing the connection string. This did not work, either.

So I did come up with another solution that works quite well as of writing this article:

Rewriting the .REPX report

A .REPX report file is a C# source code file that gets compiled when loading the report with the XtraReport.LoadLayout method.

I decided to give the following „algorithm“ a try:

  1. Load a .REPX file that was designed with the SqlClient classes as a string into memory.
  2. Do some string replacements to replace all SqlClient classes by their VistaDB counterparts.
  3. Call XtraReport.LoadLayout on the replaced string.
  4. Display the report in a print-preview form.

Luckily, after some try and error I actually was able to get this working!

In the following chapter I outline some of the methods I created in order to hopefully give other developers some ideas to use in their own applications:

Code to rewrite a .REPX report

The following method is the main method that is being called in order to load and display a report. It is a member of the print-preview form:

public void Initialize(
    FileInfo reportFilePath,
    Pair<string, string>[] parameters)
{
    var report = new XtraReport();

    if (Host.Current.CurrentProject.Settings.IsVistaDBDatabase)
    {
        var rawContent = UnicodeString.ReadTextFile(reportFilePath);

        var replacedContent = replaceSqlClientForVistaDB(rawContent);

        using (var ms = new MemoryStream(
            Encoding.Default.GetBytes(replacedContent)))
        {
            report.LoadLayout(ms);
        }
    }
    else
    {
        // Assumes that the report was designed with SQL Server.
        // If not, one would have to replace the other way, i.e.
        // from VistaDB to SQL Server.
        // I am leaving this out until really needed.
        report.LoadLayout(reportFilePath.FullName);
    }

    // --
    // Change connection string.

    var da = report.DataAdapter as DbDataAdapter;

    if (da != null)
    {
        adjustConnectionString(da.SelectCommand);
        adjustConnectionString(da.InsertCommand);
        adjustConnectionString(da.DeleteCommand);
        adjustConnectionString(da.UpdateCommand);
    }

    // --
    // Adjust parameters.

    if (parameters != null && parameters.Length > 0)
    {
        foreach (var parameter in parameters)
        {
            if (containsParameter(report.Parameters, parameter.First))
            {
                report.Parameters[parameter.First].Value = parameter.Second;
            }
        }
    }

    // --
    // Bind the report's printing system to the print control.

    printControl.PrintingSystem = report.PrintingSystem;

    // Generate the report's print document.
    report.CreateDocument();
}

The actual method to replace the string is the following:

private static string replaceSqlClientForVistaDB(string content)
{
    if (string.IsNullOrEmpty(content))
    {
        return content;
    }
    else
    {
        var result = content;

        // Add reference.
        result =
            result.Replace(
                Environment.NewLine + @"///   </References>",
                string.Format(
                    Environment.NewLine + @"///     <Reference Path=""{0}"" />" +
                    Environment.NewLine + @"///   </References>",
                    typeof (VistaDB.IVistaDBValue).Assembly.Location
                    ));

        // Change class and namespace names.
        result =
            result.Replace(
                @"System.Data.SqlClient.Sql",
                @"VistaDB.Provider.VistaDB");

        // Comment out non-available VistaDB properties.
        result = Regex.Replace(
            result,
            @"(^.*?FireInfoMessageEventOnUserErrors.*?$)",
            @"//$1",
            RegexOptions.Multiline);

        return result;
    }
}

And the method to replace the connection string is defined as follows:

private static void adjustConnectionString(DbCommand command)
{
    if (command != null)
    {
        var conn = command.Connection;

        if (conn != null)
        {
            if (conn.State == ConnectionState.Open)
            {
                conn.Close();
            }

            conn.ConnectionString = getConnectionString();
        }
    }
}

Other methods are left out for simplicity. If you want to see them, simply drop me a note, e.g. by e-mail.

Epilog

In this short article I’ve shown you a way to modify an XtraReports report during runtime of your application to work with both a Microsoft SQL Server and a VistaDB database without the need to design separate reports.

As of writing this article the reports I tested work quite well, probably/possible I will have to do further adjustments when getting to more complex reports. I will update the article then.

I am looking forward for lot of feedback! 🙂

Fehlermeldung beim Aktualisieren einer SQL Server 2000-Tabelle äber einen Verbindungsserver

Beschreibung

In einem Szenario hatten wir 2 Microsoft SQL Server 2000 (Server A und B). der eine hat den anderen als Verbindungsserver („Linked server“) eingebunden.

Wenn nun von einer Datenbank auf Server B eine SELECT-SQL-Abfrage auf eine Tabelle in einer Datenbank auf Server A ausgeführt wurde, so hat dies korrekt funktioniert.

Wurde jedoch eine UPDATE-SQL-Abfrage ausgeführt, so kam eine Fehlermeldung.

Die auszuführende Abfrage war in etwa:

UPDATE [MyTable]
SET [Credit Limit Block]=2
WHERE [No.]=’000102628521′

(also eine Abfrage auf Spaltennamen mit Leerzeichen).

Als Fehlermeldung erschien dann:

Server: Nachr.-Nr. 8180, Schweregrad 16, Status 1, Zeile 1
Statement(s) could not be prepared.

Server: Nachr.-Nr. 170, Schweregrad 15, Status 1, Zeile 1
Line 1: Incorrect syntax near ‚Limit‘.

Ursache

Scheinbar hat der SQL Server 2000 bei bestimmten Konfigurationsparametern Probleme bei UPDATE-Abfragen über einen Verbindungsserver, wenn Feldern mit Leerzeichen beteiligt sind.

Dies waren die ursprünglichen Einstellungen des Verbindungsservers:

Verbindungsserver-Einstellungen die in meinem Fall die UPDATE-Anweisung fehlschlagen ließen
Fehlgeschlagen

Damit kam die Fehlermeldung.

Bei Google habe ich unter anderem folgende Zitate gefunden:

…If I was to remove the space from (my colum name) [f 1] and use [f1] it would work fine…

Und

I’ve seen this before, and I’m quite sure that I have reported it to
Microsoft. However, it does not seem have been fixed in SQL 2000. The
error is that when the query is submitted to the remote server, the
brackets are missing.

Lösung

Da ich ein anderes, fast gleichwertiges, System hatte, auf dem die UPDATE-Abfrage korrekt funktionierte, habe ich die Einstellungen des Verbindungsservers verglichen und die Option gefunden, die in meinem Fall die Lösung war:

Verbindungsserver-Einstellungen die in meinem Fall die UPDATE-Anweisung erfolgreich ausführen ließen
Erfolgreich

Ich habe also schlicht die Option „Remotesortierung verwenden“ deaktiviert. Danach funktionierte meine Abfrage.

Ob das in jedem Fall eine praktikable Lösung ist, weiß ich leider nicht, jedoch in meinem Fall war dies ausreichend.

Microsoft Indexing Service findet (scheinbar) nicht vorhandene Suchbegriffe

Beschreibung

Beim Suchen nach Suchbegriffen mit dem Indexing Service von Microsoft (auch „Index Server“ genannt) tritt das Phänomen auf, daß auch Dokumente gefunden werden die den Suchbegriff scheinbar gar nicht enthalten.

Beispielsweise wurde nach „preisliste“ oder „kombinationsbeispiel“ gesucht und eben solche scheinbar falschen Dokumente gefunden. Hingegen haben Suchanfragen nach Wörtern wie „haus“ oder „auto“ nur korrekte Dokumente geliefert.

Ursache

Ich vermute sehr, dass der Indexing Service bei zusammengesetzten Wörtern die Wörter aufteilt und sucht als wäre es eine Suche nach den einzelnen Wörtern (gibt es da in der Grammatik Regeln um dies automatisch durchzuführen?).

Also beispielsweise bei der Suche nach „preisliste“ führt der Indexing Service eine Suchanfrage aus nach Dokumenten die „preis“ oder „liste“ oder „preisliste“ als Suchbegriffe enthalten.

Deswegen werden scheinbar auch „falsche“ Dokumente gefunden.

Lösung

Als einzigen Workaround habe ich die sogenannten „Content Queries“ gefunden.

Wenn mit einem Suchbegriff nun statt beispielsweise nach „preisliste“ über die erweitere Syntax nach „{phrase} preisliste {/phrase}“ gesucht wird, so werden tatsächlich auch nur Dokumente gefunden die direkt den Begriff „preisliste“ enthalten.

Ich bin mir unsicher ob das ein sauberes Vorgehen ist oder eher ein „geht so, ist aber eigentlich für was anderes gedacht gewesen“. Aber es funktioniert in allen bisherigen Tests :-).

Ergänzung 2006-03-03: Im Artikel „Creating Search Pages with Index Server and .NET“ auf CodeProject schreibt Heath Stewart über die „CONTAINS“- und „FREETEXT“-Prädikate. Ggf. auch eine Alternative.