Beim programmatischen Erstellen von benutzerdefinierten Spalten (SPField) in Sharepoint sind einige Dinge zu beachten und erwarten auch beachtet zu werden. Hält man sich jedoch an die Spielregeln (API Dokumentation) und erzielt trotzdem nicht das gewünschte Ergebniss, ist wahrscheinlich wieder ein Sharepoint Bug gefunden worden. Dies musste ich heute wieder mal schmerzhaft feststellen.
Sharepoint verwendet zwei Namen um Felder zu kennzeichnen. Den internalName sowie den displayName (bzw. Title). Der internalName wird von Sharepoint selbst sowie von dem Sharepoint Objekt Modell verwendet um Spalten(SPField) und Objekte wieder zu erkennen und anzusprechen. Der displayName und die Title – Eigenschaft werden verwendet um die Darstellung der Spalten zu steueren. So können eindeutige Namen Verwendung finden um Objekte eindeutig anzusprechen. Gleichzeitig kann der Anzeigename variabel gestaltet werden.
So hatte ich versucht eine Sharepoint Spalte über die API zu erstellen und anschließend zur SPFieldCollection einer Liste hinzuzufügen.
Dies funktionierte mit den folgenden Codeschnipseln auch sehr gut.
SPField currField = colFields.CreateNewField(SPFieldTypeString, displayName);
currField.staticName = internalName;
colFields.AddFieldAsXml(currField.SchemaXml, true, SPAddFieldOptions.AddToAllContentTypes);
Anschließend beim Beschreiben der Spalte gingen jedoch die Probleme los.
SPField newField = newItem.Fields.GetFieldByInternalName(internalFieldName);
Laut API Dokumentation und MSDN welche besagt, dass der staticName dazu dient, um den internalName zu setzen, wäre der obige Programm – Code richtig. (“Gets or sets the internal name of the field.”)
Das Ausführen der Methode verursachte eine ArgumentException mit der Meldung: Value does not fall within the expected range.
Darfaufhin begann ich ein wenig weiter zu forschen (Danke an Sharepoint Manager) und fand herraus, dass die AddFieldAsXml Methode in Verbindung mit einem SPField welches mit CreateNewField erzeugt wurde einen Bug von Sharepoint hervorruft. AddFieldAsXml setzt den displayName wie angegeben, aber leider auch den internalName mit dem Wert des display Namens. Das hat zur Folge, dass die Spalten nicht mehr über den interalName ansprechbar sind.
Wer jetzt also vor genau diesem Problem stehen sollte, hat zwei schnelle Möglichkeiten.
Workaround:
Die Lösung des Problems ist regelrecht simpel. Es wird der gleiche obige Code verwendet um das SPField zu erstellen, jedoch als displayName der internalName gesetzt. Dadurch legt Sharepoint die Spalte mit dem richtigen internalName an. Sobald die Spalte angelegt wurde, muss nur noch der displayName geändert werden.
string internalName = "internerName";
string displayName = "displayName";
SPField currField = colFields.CreateNewField(SPFieldTypeString, internalName);
colFields.AddFieldAsXml(currField.SchemaXml, true, SPAddFieldOptions.AddToAllContentTypes);
SPField rewriteField = colFields[internalName];
rewriteField.Title = displayName;
rewriteField.Update();
Wird die GetField- oder GetFieldByInternalName - Methode jetzt mit dem internalName aufgerufen liefert sie die gewünschte Spalte.
Da es sich bei Axis2 um eine Neuentwicklung gegenüber der Vorgängerversion handelt, wurde ein komplett anderes Data Binding Konzept umgesetzt. Zum Einsatz kommen bestehende Lösungen wie:
die in Axis2 integriert werden. XMLBeans stammen ursprünglich von BEA Systems und wird von Apache weiterentwickelt. XMLBeans heben den Informationsgehalt eines XML-Infosets während der Verarbeitung auf, so dass Metadaten zur Verfügung stehen, die bspw. für eine Schema-Validierung genutzt werden können. Wenn XMLBeans als Data Binging genutzt werden sollen, muss “-d xmlbeans” als Parameter angegeben werden. (Defaultwert ist ADB):
WSDL2JAVA ... -d xmlbeans meine.wsdl
Das Framework generiert für jeden benutzerdefinierten Datentyp eine Interfaceklasse, mit der man bei der Entwicklung in Berührung kommt. Alle Interfaces erben von XMLObject und erhalten eine interne statische Inner Class “Factory” mit der eine Klasse des jeweiligen Types erzeugt werden kann. Beispiel zur Erzeugung eines Objektes vom Typ Kunde:
KundeDocument kundeDoc = KundeDocument.Factory.newInstance();
Ein XML-Document kann über die Methode “save” in ein XML-Format serialisiert werden. Mit “xmlText” wird ein String als XML zurückgegeben.
String kundeXml = kundeDoc.xmlText();
kundeDoc.save(new File("kundeDoc.xml"));
Andere Data Bindings bilden Datantypen wie bspw. xsd:String, xsd:Token, xsd:anyUrl oder xsd:Name auf String ab. XMLBeans bietet hierfür eigene Datentypen, um die unterschiedlichen Wertebereiche und semantischen Bedeutung zu behalten.
Nach dem gestrigen Erfolg folgte heute eine Anforderung, die den Wechsel zu einem anderen Maven2-Plugin und einer neuen Mapping-Datei führte.
Die neue Anforderung war, dass Attribute aus der DTD auf andere Typen als String gemappt werden sollen. Da DTDs keine Typisierung wie XML-Schema kennen, muss dies mit einem Mapping-File geschehen. Das Maven2-Plugin aus dem gestrigen Artikel war leider mit dem Mapping der Attribute überfordert.
Jetzt wurde das Maven2-JAXB-Plugin von java.net verwendet, damit ändert sich die pom.xml folgendermaßen:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.mms-dresden</groupId>
<artifactId>hsp-search-gen</artifactId>
<name>Hilfe- und Support-Portal generierter Code fuer die Suche</name>
<version>0.1-SNAPSHOT</version>
<parent>
<groupId>de.mms-dresden</groupId>
<artifactId>hsp</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<description>Generierter Code fuer die Suche im Hilfeportal</description>
<build>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
<extension>true</extension>
<schemaLanguage>DTD</schemaLanguage>
<schemaIncludes>
<schemaInclude>*.dtd</schemaInclude>
</schemaIncludes>
<bindingIncludes>
<bindingInclude>*.jaxb</bindingInclude>
</bindingIncludes>
<verbose>true</verbose>
<args>
<arg>-Xinject-listener-code</arg>
</args>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jvnet.jaxb2-commons</groupId>
<artifactId>property-listener-injector</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>maven2-repository.dev.java.net</id>
<url>http://download.java.net/maven/2/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
</dependencies>
</project>
Bei der Angabe der Repository-Pfade muss man aufpassen, weil die Angaben auf der Projektseite des Plugins inzwischen nicht mehr stimmen. Stattdessen mussten die Pfade aus der Repositoryseite von java.net verwendet werden.
Die Dependency-Versionen sind jetzt im Parent-POM definiert, entsprechen aber den Versionen aus dem gestrigen Beitrag.
Das Mapping mappt jetzt das Attribut HITCOUNT des Elements NAVIGATIONENTRY auf den Java-Typ int (genauer gesagt den Wrapper Integer um auch null-Werte darstellen zu können) und sieht jetzt folgendermaßen aus:
<?xml version="1.0" ?>
<!--
The syntax of the binding file for DTD is defined in the JAXB EA.
See vendorSchemaLangs.html for details.
-->
<xml-java-binding-schema xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:ci="http://jaxb.dev.java.net/plugin/listener-injector">
<!-- specify the package name for the generated code -->
<options package="de.mms_dresden.mip.hsp.base.services.search" />
<xjc:serializable/>
<element name="NAVIGATIONENTRY" type="class">
<attribute name="HITCOUNT" convert="int" />
</element>
</xml-java-binding-schema>
Releaseparty at Atlassian? Confluence 3.2 BETA and 3.1.2 with soms bugfixes were released yesterday. [...]
Tino Schmidt's Vortrag zu Enterprise Mashups auf der webciety, 4.3 Remix the Web http://bit.ly/d26rtA [...]
neuer Blogpost: February Cumulative Update (2010) http://bit.ly/cwxZGE [...]
Webinar am 16.03.: „Communote Enterprise Microblogging - Funktionen und Einsatzbereiche im Unternehmen“ http://bit.ly/96eexF [...]