Boolean in Datenbanken

Gemäß SQL-Standard SQL:1999 ist ein Boolean-Typ in Datenbanken vorgesehen, aber dieses Feature ist optional und auch im Jahr 2012 von vielen vorhandenen Datenbanken nicht implementiert. PostgreSQL ist wohl eine Ausnahme.

Nun ist dieser Boolean-Typ sehr trivial nachzubilden. Ich habe zum Beispiel die folgenden Varianten gesehen:

  • VARCHAR(1) oder VARCHAR2(1) oder CHAR(1) mit ‚0‘ und ‚1‘
  • VARCHAR(1) oder VARCHAR2(1) oder CHAR(1) mit ‚N‘ und ‚Y‘
  • VARCHAR(1) oder VARCHAR2(1) oder CHAR(1) mit ‚T‘ und ‚F‘
  • INTEGER oder NUMBER(1,0) mit 0 und 1

Oder so absurde Varianten wie:

  • VARCHAR(5) oder VARCHAR2(5) mit ‚true‘ und ‚false‘

Gerade die Tatsache, daß hier nicht eine Lösung sich durchgesetzt hat, ist schon ein Nachteil, denn in größeren Applikationen hat man dann oft noch verschiedene Varianten gleichzeitig. In SQL muß man immer noch die Extra-Runde drehen und den Boolean-Wert konvertieren, vor allem in den WHERE-Bedingungen.

Letztlich kann man mit dieser Einschränkung leben, solange man sich auf eine Variante einigt. Vielleicht kann mir auch jemand sagen, welches die „richtige“ Lösung ist, auf die sich die meisten DBAs geeinigt haben.

Share Button

Apps oder HTML5

English

Die Idee mit den Apps für Mobiltelefone ist nicht sehr neu. Schon recht einfache Telefone konnten so etwas und man entwickelte die Apps oft mit Java ME, einem verkleinerten Java. Das ist vielleicht nicht die beste Lösung, aber hatte immerhin den Vorteil, daß man eine Entwicklung für eine Vielzahl von Mobiltelefonen machen konnte und nur beim Testen dann alle möglichen Geräte durchprobieren mußte.
Dann kam bei den Nokia-Smartphones Qt mit C++ als Basis für die Entwicklung von Apps auf. Das versprach immer noch geräteunabhängig zu sein, weil Qt OpenSource ist und auf verschiedene gängige Desktop-Betriebssysteme sowie Symbian und Maemo/Meego portiert worden ist. Wirklich verbreitet waren die Qt-Apps auf Symbian und Maemo/Meego. Heute wird Qt von Digia entwickelt und soll in Zukunft unter anderem auch für Android verfügbar sein.
Nun kamen mit der Verbreitung von Apple-iphone und Android neue Varianten auf, deren Apps man mit Objective-C bzw. Java für Dalvik schreiben soll. Microsoft versucht auch noch ein Mobiltelefon-OS zu verbreiten, das dessen Apps wieder anders, vielleicht mit C#, entwickelt werden sollen.
So stellt sich für App-Entwickler heute das Problem, daß man Apps mit einer bestimmten Funktionalität wirklich ca. 6-8 Mal komplett neu entwickeln muß, wenn man einen großen Teil der Anwender erreichen will. Für sehr wichtige Apps mag das schon eine vertretbare Investition sein, aber es stellt sich doch schnell die Frage, ob der Aufwand vertretbar ist. Außerdem wissen wir nicht genau, welche Systeme außer Android in ein paar Jahren verbreitet sein werden oder zumindest relevante Nischen besetzen. Vielleicht wird sich bei neuen Systemen zumindest eine Android-Kompatibilität durch eine angepaßte Dalvik-Umgebung für diese Systeme etablieren, aber sicher ist auch das nicht. Mit Web-Applikationen ist man aber schon ab dem ersten Tag auf dem neuen Smartphone, von dem man heute noch nichts weiß. Den Browser wird niemand weglassen wollen.

Die Idee, mit den Prozenten aus dem Verkauf von Apps über die jeweiligen App-Stores Geld zu verdienen war sicher vor ein paar Jahren mal Gold wert. Heute gibt es aber für viele Systeme eine riesige Vielfalt von Apps und wenn da noch eine dazustellt, ist es schon ungewöhnlich, noch auf so hohe Download-Zahlen zu kommen, daß die paar Cent pro Download alleine schon den Aufwand finanzieren könnten.
Bis vor gar nicht so langer Zeit hatten die Apps noch eine Existenzberechtigung alleine schon wegen der Möglichkeiten Client-seitig, also auf dem Mobiltelefon, bessere interaktive Funktionalität aufzubauen als dies mit HTML und einer Webapplikation möglich wäre.
Das hat sich aber inzwischen sehr relativiert. Mit HTML5, JavaScript, Ajax, Websockets und einigen anderen Neuerungen der klassischen Web-Applikations-Technologie kann man heute praktisch alles machen, was diese Apps konnten. Und man muß es nur einmal entwickeln. Ich gehe daher davon aus, daß sich diese Apps nur für einige wenige spezielle Anwendungen halten werden, die so bedeutend sind, daß die Mehrfachentwicklung nicht weh tut und die mehr Interaktion brauchen als mit Web-Applikationen üblich ist. Es wird immer schwieriger, solche Fälle zu finden. Was einem spontan einfällt:

  • Anwender soll für Verwendung der Funktionalität bezahlen. Das gibt es im Internet an viele Stellen. Die Bezahlmechanismen sind noch nicht optimal, aber weit verbreitet und etabliert, wenn es um Einkaufen im Web geht. Sogenannte „Paywalls“ für Bereiche einer Webseite oder -applikation, die nur zahlenden Kunden zugänglich sind, gibt es. Vorteil: Kein app-Store zieht seine Prozente ab.
  • Spiele sollten doch auch offline funktionieren, zum Beispiel im Ausland (solange Dataroaming teuer ist) oder im Tunnel. Das verspricht HTML5 mit dem lokalen Speicher („local storage“) aber zu lösen
  • Aussehen: Kann man mit HTML5 ziemlich beliebig schaffen.
  • Interaktivität: Mit JavaScript, Ajax und HTML5 kann man sehr gute Applikationen bauen.

Kurz gesagt, das Geschäft mit den App-Stores dürfte in ein paar Jahren zum Auslaufmodell werden oder als Nische für eine kleine Anzahl von Apps überleben.

Share Button

Oracle Bug with empty strings

Deutsch

Oracle databases seem to consider empty strings and null the same. This contradicts the SQL specification and is therefore a bug.

Unfortunately, it would be almost impossible for Oracle to fix this, because too many applications using Oracle for decades rely on this behavior.

It is therefore crucial to know about this bug when dealing with Oracle databases and to take it into account. As long as the database is accessed explicitly using SQL, SQL*Plus, JDBC, DBI, Pro*C etc., it is easier to deal with. But it is becoming tricky. when using frameworks like Hibernate, where weird errors might be observed. If you have an entity that is persisted, which has a string attribute, it should be legitimately to assign the empty string to it. Once this object travels through Hibernate and gets persisted, this empty string has become null instead, resulting in unexpected NullPointerExceptions. Or if the attribute has the NOT-NULL-constraint in the database, this will result in an Oracle error because of constraint violation. On top of this it might become a semantic issue, when empty strings and null are actually considered to have a different meaning.

An ugly, but practical way to deal with it is to change the getter and setter methods of the string attributes accordingly. Maybe some kind of „magic string“ can be defined, which will never ever occur as a value of this string attribute. The getters and setters use this magic string to internally represent the empty string. It is necessary to annotate the attribute, not the getter, for making this work with JPA or Hibernate.

Example in Java (without JPA annotations, synchronization, toString(), hashCode(), equals() etc. Would be similar for other languages)


public class MyEntity {
  private static String MAGIC_VALUE = "_._._«NULL»_._._";
  private String value;

  public String getValue() {
    if (MAGIC_VALUE.equals(value)) {
      return "";
    } else {
      return value;
    }
  }

  public void setValue(String newValue) {
    if (MAGIC_VALUE.equals(newValue)) {
      throw new IllegalArgumentException("value="+newValue+" not allowed");
    } else if ("".equals(newValue)) {
      this.value = MAGIC_VALUE;
    } else {
      this.value = newValue;
    }
  }
}

To make this work even with short strings, defined as VARCHAR2(1) or VARCHAR2(2) in the database, it is perhaps a good idea to use control characters or UTF-8 characters that are not going to be used otherwise. But it remains an unsatisfactory and ugly solution.

Does anyone have a better idea?

Are there any other databases that have the same issue?

Share Button

Fehler in Oracle-Datenbanken mit leeren Zeichenketten

English

Es scheint so, dass alle gängigen Oracle-Datenbanken leere Zeichenketten und null nicht unterscheiden. Das widerspricht der SQL-Spezifikation und ist damit ein Fehler.

Leider wäre es für Oracle nicht einmal einfach, den zu entfernen, weil zu viele Applikationen seit Jahrzehnten Oracle benutzen und sich auf dieses Verhalten eingestellt haben.

Entscheidend ist also erst einmal, diesen Fehler zu kennen und bei der Verwendung von Oracle-Datenbanken zu berücksichtigen. Solange man explizit mit SQL, also SQL*Plus, JDBC, DBI, Pro*C u.ä. auf die Datenbank zugreift, ist es noch einfacher, das zu berücksichtigen, aber heikel wird es bei der Verwendung von Frameworks wie Hibernate, wo man merkwürdige Fehler findet. Wenn man zum Beispiel eine Entität (engl. Entity) hat, die persistiert wird, und dort ein Attribut eine Zeichenkette ist, kann man legitimerweise dort die leere Zeichenkette zuweisen. Sobald dieses Objekt durch Hibernate wandert und persistiert wird, hat man plötzlich stattdessen null dort stehen und bekommt unerwartete NullPointerExceptions. Oder das Attribut ist in der Datenbank mit dem NOT-NULL-Constraint versehen und man bekommt beim Schreiben Oracle-Fehler. Außerdem ist es natürlich ein semantisches Problem, man möchte vielleicht gerne leere Zeichenketten und null dort speichern können und mit verschiedenen Bedeutungen behandeln.

Eine hässliche, aber doch praktikable Möglichkeit, damit umzugehen ist die Getter und Setter der Zeichenketten-Attribute entsprechend zu ändern. Man kann eine „magische Zeichenkette“ definieren, die man garantiert nicht als Wert dort speichern möchte. Dann kann man im Getter vergleichen, ob das Attribut diese Zeichenkette als Wert hat und in dem Fall die leere Zeichenkette zurückgeben. Und im Setter prüft man erst, ob der neue Wert diese magische Zeichenkette ist, was man mit einer Exception ablehnt, und andernfalls, ob der neue Wert die leere Zeichenkette ist, was man durch die magische Zeichenkette ersetzt.

Bespiel in Java (ohne JPA-Annotationen, Synchronisation, toString(), hashCode(), equals() u.s.w.; geht analog für andere Programmiersprachen)


public class MyEntity {
  private static String MAGIC_VALUE = "_._._«NULL»_._._";
  private String value;

  public String getValue() {
    if (MAGIC_VALUE.equals(value)) {
      return "";
    } else {
      return value;
    }
  }

  public void setValue(String newValue) {
    if (MAGIC_VALUE.equals(newValue)) {
      throw new IllegalArgumentException("value="+newValue+" not allowed");
    } else if ("".equals(newValue)) {
      this.value = MAGIC_VALUE;
    } else {
      this.value = newValue;
    }
  }
}

Damit das ganze auch mit Attributen funktioniert, die in der Datenbank als VARCHAR(1) oder VARCHAR(2) definiert sind, ist es vielleicht eine gute Idee, Steuerzeichen oder UTF-8-Zeichen aus einer Sprache, die man nie unterstützen will, zu verwenden. Aber es bleibt eine unbefriedigende Lösung.

Hat jemand eine bessere Idee dazu?

Gibt es noch andere Datenbanken, die den gleichen Fehler wie Oracle in diesem Punkt haben?

Share Button

Data Quality

Deutsch

Very often we experience that software is not working well. Often this is a problem of the software itself, which we all know quite well.

Experience shows, however, that more often the problem is of the data on which the software operates. In short, junk in — junk out.

In organizations that use software, it is a good idea to keep an eye on the underlying data. For software we are used to development processes that at least pretend to take testing and bug-fixing serious. Formalized processes exist for this and in serious IT projects they prove to be helpful for reducing the amount of software bugs. The issue of data quality is often outside these processes, because data content is often provided by the business side, where such quality processes have not always been established. Often data is exchanged between different systems, where we can have quality problems as well.

Some questions about this:

  • Do we know the path of data through the various systems=
  • Is any person responsible for data quality?
  • Is the data quality checked
  • Is there an efficient process to correct data?

Such questions are often less clearly answered than similar questions about software quality, where the development process is well defined, at least initially, responsibilities are known and at least some rudimentary review of quality has become common practice.

Here are some examples. I try to write about them in such a way that it cannot be concluded which in organization they occurred:

Data should represent the reality. As an example we can describe the stock of furniture in an office location. Occasionally pieces of furniture are replaced, removed, or added. I the data is not properly adjusted according to such changes, we eventually lost touch with reality and the application working with these data becomes useless.

Data should be accurate. As an example my name had been misspelled somewhere and the email address had been defined based on that misspelled name. That would not sound too bad at first glance, if I did not feel embarrassed. But it is a problem. The name is used in so many places, where the exact spelling is mandatory, therefore an inaccuracy cannot be tolerated any more. In the old days the mailman could deliver paper mail even with slightly inaccurate addresses. Inaccurate mail addresses simply do not work. In the case of my misspelled name, I was able to find someone who could correct this in some data store within short time. But after a week or so, the misspelled name was back. No one really knew how to fix it for good. By chance I found the guy responsible for the master data system a few months later and he could fix it for good. As it seems, data was duplicated from the master system to other systems, but the data flow was not well documented or at least not well known.

A common result of inaccuracies are duplicates. They often occur because of minor misspellings when writing names in data fields or even when scanning names from printed text, because OCR-software is not perfect. A reason for duplicates can even be transfer of data from one system to another without checking matches with records already present in the target system.

Interesting can also be found when attributes change, for example a person’s name. When data is transferred between different systems and data is not linked well, it is possible that records for the same person exist with the old and the new name. Or differently asked, how many places are there in the IT landscape, where such a change needs to be performed?

A good example of such a weakness can be found with MS-Windows-NT, at least for version 5.1 (Windows XP), but probably any other version as well. It is possible to boot a PC with a working installation with an USB-stick with Linux and copy the whole disk content byte-by-byte to another PC. If the hardware is identical, the other PC will boot and work quite well, if dynamic IP addresses are used and no NT-Domains are involved. But there is one problem. If NT-Domains are used, which is the case in all organizations that use MS-Windows PCs for a large part of there office workers, an internal identity of the PC becomes relevant. This is generated at some point and it is stored in the registry and maybe also in the file system in so many places that it is hard to keep track of that. Having two PCs in the same network with the same internal ID is calling for trouble. So this simple approach that would make life easy for system administrators does not work.

Many of these problems can be treated at least partially, by paying attention to the following issues when creating an IT landscape:

  • How do the system communicate with each other? Which system is the „master“ for the data? Or is there a serious conceptionally correct „multimaster“ architecture in place?
  • Can obviously incorrect data be detected and rejected?
  • How are records of data linked together? How robust are these connections for changes?
  • Are there work-flows that make it easier to keep data consistent and accurate?
  • How stable are interfaces to other systems?
  • Are tests for plausibility of data in place, especially for similarities (duplicates)?
  • Is a comparison with other (possibly more reliable) data sources performed?
  • How are changes in the reality that is expressed by the data detected and used for adjusting the data content?

Today a lot could be done and it makes sense, to perform some automatic tests of data during the productive operation. But it is also important that the people who supply the data work accurate and that the processes are taken serious, so that all parties involved are working to ensure good data quality. Concerning the example of furniture: Those who are responsible to enter information about the furniture into the system, may not be overloaded with other activities that have higher priority, otherwise keeping the furniture database up to date will not be done, will be done too late or will be done half-heartedly. Without good data the expenses for maintaining the IT application are wasted.

Share Button

Datenqualität

English

Sehr häufig erlebt man, daß Software nicht richtig funktioniert.  Oft ist es ein Fehler der Software selbst,das wissen wir nur zu gut.

Die Erfahrung zeigt aber, daß noch häufiger das Problem bei den Daten liegt, mit denen die Software arbeitet.  Kurz gesagt ergeben sich aus falschen Daten falsche Ergebnisse.

Es lohnt sich also, in Organisationen, die mit Software arbeiten, auf die Qualität der zugrundeliegenden Daten zu achten.  Für Software sind wir es gewohnt, daß es Prozesse gibt, die Qualität zu testen und gegebenenfalls Fehler zu finden und zu korrigieren.  Auch wenn diese Prozesse oft Mängel aufweisen, sind sie doch vorhanden und funktionieren meistens auch irgendwie. Natürlich ist es andererseits auch oft einfacher, fehlerhafte Daten als fehlerhafte Software zu korrigieren.

Wer kümmert sich aber um die Qualität der Daten?  Ist die Software einmal abgenommen, ist oft eine „Fachseite“ dafür zuständig, die Daten zu pflegen.  Oder die Daten kommen von anderen Systemen.  Ein paar Fragen dazu:

  • Kennt man den Weg der Daten durch die verschiedenen Systeme?
  • Gibt es Verantwortliche für die Datenqualität?
  • Werden die Daten überprüft?

Die entsprechenden Fragen sind oft weniger klar beantwortet als im Fall der Software, wo man doch den Entwicklungsprozeß zumindest ansatzweise kennt, Verantwortlichkeiten definiert sind und eine zumindest rudimentäre Überprüfung der Qualität üblich geworden ist.

Ein paar Beispiele, natürlich soweit abstrahiert, daß man sie nicht mehr einer bestimmten Organisation zuordnen kann:

Daten sollten eine Realität abbilden, sagen wir mal den Bestand an Möbelstücken in einem Büro.  Wenn nun gelegentlich Möbelstücke ausgetauscht, entfernt oder dazugekauft werden und die Daten mit der Realität nicht Schritt halten, hat man irgendwann den Bezug zur Realität eingebüßt.

Daten sollten genau sein.  Zum Beispiel hat man irgendwo meinen Namen falsch geschrieben und die Mailadresse mit dem falschen Namen definiert.  Das kann egal sein, ist es aber nicht wirklich.  Der Name wird an so vielen Stellen verwendet, wo es auf die buchstabengenaue Schreibweise ankommt, deshalb ist eine Ungenauigkeit heute nicht mehr so leicht zu verschmerzen wie zu den Zeiten der Papierpost und der Briefträger, die noch Zeit hatten, einen Brief mit einer ungeauen Adresse dem richtigen Empfänger zu bringen.  Im Fall des falsch geschriebenen Namens konnte ich damals recht schnell eine Stelle finden, die den Fehler korrigiert hat.  Aber nach einer Woche war er wieder da und niemand wußte, wie man das wirklich löst, bis ich nach ein paar Monaten zufällig die Person gefunden hatte, die das „Master“-System betreute, von dem die Daten immer wieder repliziert wurden.

Eine häufige Folge von Ungenauigkeiten sind Duplikate, die unter anderem durch so eine Ungenauigkeit beim Schreiben von Namen oder anderen Datenfeldern entstehen können. Oder durch verschiedene miteinander kommunzierende Systeme, die sich ihre Daten gegenseitig weiterreichen und irgendwann „vergessen“ auf schon bekannte Daten zu prüfen.

Interessant sind auch Fälle, wo sich eine Attribut ändert, zum Beispiel der Name einer Person.  Nun ist diese Person aber schon vor der Namensänderung im System gewesen und die Daten sind auch entsprechend verknüpft.  Bleiben diese Verknüpfungen bei einer Namensänderung erhalten?

Viele dieser Probleme lassen sich zumindest teilweise behandeln, indem man bei der Erstellung einer IT-Landschaft darauf achtet, daß diese möglichst wenig fehleranfällig ist:

  • Wie kommunzieren die Systeme miteinander?  Welches System ist „Master“ für die Daten?  Oder gibt es eine wirklich funktionierende „Multimaster“-Architektur?
  • Können offensichtlich falsche Daten erkannt und abgelehnt werden?
  • Wie werden Daten miteinander verknüpft und wie resistent sind diese gegenüber Veränderungen?
  • Gibt es Workflows, die es erleichtern, Daten konsistent und aktuell zu halten?
  • Wie stabil sind Schnittstellen zu anderen Systemen?
  • Gibt es Plausibiltitätsprüfungen bei den Daten, insbesondere auch auf Ähnlichkeit (Duplikate)?
  • Wird ein Abgleich mit zuverlässigen Datenquellen durchgeführt?
  • Wie werden Änderungen in der durch die Daten abgebildeten Realität erkannt und in den Datenbestand eingeführt?

Man kann heute recht viel machen und es ist sinnvoll, auch viele Tests der Datenqualität im laufenden Betrieb automatisiert durchzuführen.  Aber es ist auch wichtig, daß die Personen, die die Daten liefern, genau arbeiten und daß die Prozesse so gelebt werden, daß alle Beteiligten daran arbeiten, eine gute Datenqualität sicherzustellen.  Um bei dem Beispiel der Möbel zu bleiben:  Diejenigen, die die Möbel in dem System erfassen müssen, dürfen nicht mit anderen Tätigkeiten so überlastet sein, daß die Erfassung der Möbel keine Priorität mehr hat und nur halbherzig oder zu spät oder gar nicht gemacht wird.  Sonst kann man sich die teure IT-Applikation sparen, die mit dem entsprechenden Datenbestand arbeitet.

 

Share Button

Devoxx Experience

Deutsch

From 2012-11-14 until 2012-11-16 I have been participating in the Devoxx conference in Antwerp. I liked it very much and found it very interesting. Hopefully I will be able to came again 2013.

A tiny problem already occured on my way to Antwerp, because the railroads were not operating as usual due to strikes. Fortunately I could totally avoid this problem by riding my bicycle from from Dusseldorf to Antwerp and after the conference back to Germany.

Part of a huge cinema complex was used as conference location. The theatre rooms were big enough (most of the time), had comfortable seats, good sound systems and magnificent projection facilities.

Devoxx was actually a Java conference, however, it has become much more diverse. There were speeches were covering other progamming languages for the JVM (Groovy, Scala, Clojure, Ceylon, …), software architecture, development processesses, security and many other topics. Most of the presentations were very well done and provided some interesting insights into their issue.

Good presentations were for example:

  • The Future of Software Development Process Methodology Effectiveness
  • Architecture All the Way Down
  • How to build pipelines for Big Data Hadoop using OSS
  • What’s new in Groovy 2.0? (which was an implicit fast track to learning Groovy for me 😉 )
  • Search, the Final Frontier
  • JSR 356: The Java API for WebSocket
  • Building Modular Applications with Enterprise OSGi

Maybe I will write more about some of the individual topics in the next few months.

Share Button

Devoxx

English

Vom 14. November bis 16. November 2012 (2012-11-14 bis 2012-11-16 im ISO-Datumsformat 😉 ) war ich auf der Devoxx-Konferenz in Antwerpen. Das war sehr interessant und ich hoffe, da im nächsten Jahr wieder teilzunehmen.

Eine kleine Hürde tat sich bereits bei der Anreise auf, weil die belgische Bahn ausgerechnet in dieser Woche von Streiks betroffen war. Dies ließ sich aber umschiffen, denn ich konnte von Düsseldorf nach Antwerpen und später wieder zurück nach Deutschland mit dem Fahrrad fahren.

Als Konferenzlokal hat man einen Teil eines Kinokomplexes verwendet und die Kinosäle waren mit Digitalprojektorenm großen Leinwänden und vor allem vielen bequemen Sitzen bestens für den Zweck geeignet.

Die Devoxx ist eigentlich eine Java-Konferenz, war aber sehr viel vielfältiger als das. Es gab interessante Vorträge über andere Programmiersprachen als Java in der JVM-Welt (Groovy, Scala, Clojure, Ceylon, …), über Softwarearchitektur, über Security, über Entwicklungsprozesse und vieles andere in dem Umfeld. Die meisten Vorträge waren sehr gut gemacht und haben in der Stunde einen kleinen Einblick in das Thema gegeben.

Gute Vorträge waren zum Beispiel

Vielleicht werde ich zu den einzelnen Themen irgendwann einmal noch mehr schreiben.

Share Button

Jolla shows Sailfish

The Finnische startup company Jolla, which has been founded by former Nokia emplyees, has shown a first version of its Meego based (and thus Linux based) cell phone operating system Sailfish. This happened on 2012-11-21.

Share Button

Jolla zeigt Sailfish

Die finnische Startup-Firma Jolla, die von ehemaligen Nokia-Mitarbeitern gegründet worden ist, hat am 21. November (2012-11-21) eine erste Version ihres auf Meego (und damit Linux) basierenden Mobiltelefonbetriebssystems Sailfish vorgeführt.

Share Button