smartEditor Developer Documentation


Steps to add new form fields to an existing form tab

  1. Tiles and forms
    1. Create a new JSP file based on an existing "similar" form field, i.e. same multiplicity (one, or many) and similar number of fields (to re-use the same styling/form layout.
      For a simple text form field "smlAcousticSensorLength" create a new smlAcousticSensorLength.jsp in webapp/Web-INF/jsp/elements:

<table class="verticalTop">
 <tr>
 <td><label for="smlAcousticSensorLength" id="smlAcousticSensorLength"
 class="firstLabel width190 tooltip"> <fmt:message
 key="element.smlAcousticSensorLength.label" />
 </label></td>
 <td><form:input htmlEscape="true"
 path="storage['smlAcousticSensorLength'].length" size="100"
 id="smlAcousticSensorLength" /> <form:errors
 path="storage['smlAcousticSensorLength'].length"
 cssClass="ui-state-error-text" /></td>
 </tr>
</table>

      • Add required language labels in message(_de).properties in main/resources
For a simple text form field "smlAcousticSensorLength" for example:

element.smlAcousticSensorLength.label=L&#228;nge
element.smlAcousticSensorWeight.label=Gewicht
element.smlAcousticSensorHeight.label=H&#246;he

and

element.smlAcousticSensorLength.label=Length
element.smlAcousticSensorWeight.label=Weight
element.smlAcousticSensorHeight.label=Height

      • If you have multiple possible elements, then make sure that the configuration of AjaxEventDecoration in the JSP file is correctly updated, see link here.
    1. Add the element into the layout in tiles-editor.xml in webapp/Web-INF/defs/.
      • Make the new definition available by adding it ( put-attribute) to the definition of editor.body. For a simple text form field "smlAcousticSensorLength" for example:

<definition name="editor.body" template="/WEB-INF/jsp/body.jsp"
 preparer="de.conterra.smarteditor.tiles.BodyViewPreparer">
 <put-attribute name="smlAcousticSensorLength"
        value="/WEB-INF/jsp/elements/smlAcousticSensorLength.jsp" cascade="true" />	
</definition>

      • The name is used in the body.jsp and body_.jsp pages in webapp/Web-INF/jsp/ to insert the new field.

<div id="tabs-1">
 <tiles:insertAttribute name="smlAcousticSensorLength" />
</div>

    1. If the Element has a Multiple...jsp File: Add the element into the layout in tiles-elements.xml.
      • Create a definition.
      • Make the new definition available by adding it ( put-attribute) to the definition of editor.body.
      • The name is used in the tiles-editor.xml
  1. Transformation and beans
    1. Create new transformation scripts in the directory src/main/resources/xslt:
      • .._toISO.xslt for the transformation of internal beans to ISO document.
      • .._toBean.xslt for the transformation of ISO to internal beans.
      • See link here for more information on the transformation scripts.
    2. Create and configure beans
      • Create a Groovy bean in src/main/resources/groovy.
        • The names of the fields in the bean must match the names of the storage[..].name used in the JSP.
        • The Groovy beans are transformed automatically to XML, which is the input $beanDoc that is read in the .._toISO transformation scripts.
        • Groovy beans can hold other groovy beans and lists (see for example DescriptiveKeywordBean.groovy)
        • The @XStreamAlias links the Groovy bean to the XSLT scripts.
        • The XML element names and other encoding/decoding configuration can be done with XStream annotations, see for example SecurityConstraintsBean.groovy.
      • Add a new definition in beans-definitions.xml in webapp/WEB-INF/ and add the links to the XSLT files and Groovy bean created above in the same fashion as the existing definitions. For a simple text form field "smlAcousticSensorLength" for example:

<lang:groovy id="smlAcousticSensorLength" script-source="classpath:groovy/SmlAcousticSensorLengthBean.groovy">
 <lang:property name="transformToISO" value="/xslt/SmlAcousticSensorLength_sml.xslt" />
 <lang:property name="transformToBean" value="/xslt/SmlAcousticSensorLength_bean.xslt" />
 </lang:groovy>
      • Add an entry for the new definition in the backendBean in beans-definitions.xml.
        • The key of the entry must match the storage in the JSP, the value-ref is the name of the newly added bean definition. For a simple text form field "smlAcousticSensorLength" for example:

<bean id="backendBean" class="de.conterra.smarteditor.beans.BackendBean"
 scope="session">
 <aop:scoped-proxy />
 <property name="validatorId" value="smlValidator" />
 <property name="stickyErrorMessages" value="false" />
 <!-- default validator id -->
 <property name="storage">
 <map>
                              <entry key="smlAcousticSensorLength" value-ref="smlAcousticSensorLength" />
                 </map>
 </property>
</bean>
  1. Double check: Search all the new files (JSP, XSLT) for occurrences of the name/identifier of your "template" to avoid bugs.
  2. Optional
    • If the new form field is a "first level" element then create a tooltip entry. The filename of the tooltip must match the id of the field description's span element or the id of the HTML label element if there is no span.
    • If the new form field is mandatory, then add highlighting in the respective validator JavaScript, e.g. isoValidator.js, and adjust the validation schematron file in /src/main/resources/validation.
      • IMPORTANT: Depending on how your Schematron rules are defined (i.e. their context), you might have to add an empty element to the template so that the rule is fired.
    • If the new form field is mandatory and multi-valued, then you might want to have an empty tab open when you start a new document. To achieve this you must add an empty field (e.g. eventually containing ) into the templates in /src/main/resources/templates/, for both or either of dataset.xml and service.xml.
      • *IMPORTANT:* Check all functionality afterwards! Some of the =.._iso.xslt=-scripts have case differentiation if an element that they want to change already exists. To find these elements, check all source code occurrences of the element name that you added to the template.

How does the transformation to and from metadata documents work?

The smartEditor uses two transformation scripts to load or create external XML metadata documents such as ISO 19115 files. The XML is transformed into a flat XML structure defined by Groovy beans (see directory src/main/resources/groovy in the webapp module). So there is a sort of duality between these to transformation scripts, which is reflected by their naming. Take a look at the src/main/resources/xslt directory to get an idea.

For a simple text form field "smlAcousticSensorLength" for example:

@XStreamAlias("SmlAcousticSensorLength")
class SmlAcousticSensorLengthBean extends BaseBean {

  @XStreamAlias("length")
  String length
}	

.._toISO.xslt

The toISO scripts basically consist of three steps:

  1. Removing existing elements of the type that the script will add later
  2. Copying all non-touched elements from the XML (see the official schemas so that you're not misssing any!). It is good practice to keep the copying statements in the same order as the elements are in the XML schemas.
  3. Adding the new information based on the XML that is passed via the $beanDoc transformation attribute.
    • The beanDoc is an XML encoding of the Groovy bean, starting either with the tag if the form element can be available multiple times, or with the XML alias of the Groovy bean, which is configured by the @XStreamAlias annotation.
For a simple text form field "smlAcousticSensorLength" for example:

<xsl:template
 match="/sml:SensorML/sml:member/sml:System/sml:characteristics/swe:DataRecord[@definition='urn:ogc:def:property:OGC:physicalProperties']/swe:field/swe:DataRecord/swe:field/swe:Quantity[@definition='urn:ogc:def:property:OGC:length']" />
 <xsl:template
 match="/sml:SensorML/sml:member/sml:System/sml:characteristics/swe:DataRecord[@definition='urn:ogc:def:property:OGC:physicalProperties']/swe:field/swe:DataRecord/swe:field[@name='length']" />
 <!-- parameter handed over by transformer -->
 <xsl:param name="beanDoc" />
 <xsl:template match="//sml:member/sml:System/sml:characteristics/swe:DataRecord[@definition='urn:ogc:def:property:OGC:physicalProperties']/swe:field/swe:DataRecord">
 <xsl:copy>
 <xsl:apply-templates select="swe:field" />
 <swe:field name="length">
 <swe:Quantity definition="urn:ogc:def:property:OGC:length"> 
                  <swe:uom code="mm"/> 
                  <swe:value><xsl:value-of select="$beanDoc/*/length" /></swe:value>
                </swe:Quantity> 
 </swe:field>
 </xsl:copy>
 </xsl:template>

.._toBean.xslt

The toBean scripts parse the external metadata XML, namely ISO, into the internal XML structure, roughly in the form of . InternalBeanName is the @XStreamAlias of the Groovy bean.

For creating the correct XPaths it is recommended to use an XSLT-capable XML editor.

<xsl:value-of - statements copy the value of an XML element, therefore they must not end with /text() to copy text content.

For a simple text form field "smlAcousticSensorLength" for example:

<xsl:template match="/">
        <SmlAcousticSensorLength>
            <length>
                <xsl:value-of select="//sml:member/sml:System/sml:characteristics/swe:DataRecord[@definition='urn:ogc:def:property:OGC:physicalProperties']/swe:field/swe:DataRecord/swe:field/swe:Quantity[@definition='urn:ogc:def:property:OGC:length']/swe:value"/>
            </length>
        </SmlAcousticSensorLength>
    </xsl:template>

How can I use existing codelists?

The file codelist_enumeration.xml contains named maps (key value pairs) of many ISO codelists.

In a JSP you can simply call the name and value map by using Codelistname.nvp to fill a dropdown menu. To see how this works internally, see ISODictionary.java the the corresponding bean definition configBean.

How is the AjaxEventDecoration configured?

In the JavaScript block:

  • elementId is the id of the =input=-element of the "+"-Button.
  • elementName and elementIndex are the names of two hidden input fields defined in body.jsp that are used to hold the information which specific tab of the multi-element form is to be removed.
    • ? Or is this also used in the storage[..] statements and/or the id of the =div=-element holding the form section.
  • fragments is the name of the tiles in editor.body.
  • propertyPath is the name of the list of items use in storage, should be left to items.
  • type is the name of the Groovy bean defining the internal structure.
  • formId, "event", "method" should not be changed.
In the HTML, make sure that the anchor elements used in the tab headers correctly link to the div=-elements that are created for each form item (see first =div within the c:forEach... block.

How is the hierachyLevel and serviceType set for the document?

By choosing a specific type of metadata one the "new document" page ( new.jsp), the values for the elements hierarchyLevel and serviceLevel are set based on the value of the specific button in the XSL transformation scripts applyResourceType.xslt and applyServiceType.xslt.

How can I solve the error “Invalid property 'storage[]' of bean class [...]: Could not determine property type for auto-growing a default value

Re-check the beans-definitons.xml: Check if there is an entry for the given in the bean with id backendBean. The entry’s valure-ref is the identifier of the provied bean defined in the same file, and the entry's key is the identifier that the editor is looking for within the storage element.

To identify the form element, search the project for storage[].

Browser message “This webpage has a redirect loop”, in conjunction with endless loop of “de.conterra.suite.edit.controller.EditController - Binding to new form” and “de.conterra.suite.edit.controller.EditController - Displaying new form”

  • Re-check the beans-definitions.xml if there is an entry in the backendBean for the form that you are currently working and if all the identifiers are correct (no typos). Also check if the groovy bean name is correct! Check if the storage identifier used is correct and if the child elements of the storage match the names in the Groovy bean.
  • Make a clean build, clear browser cache (cookies).
  • Debug into Springs’s AbstractFormController.java to see if there are swallowed errors, start in the method showForm(..), then wait for an Exception to be thrown but never logged.

org.apache.tiles.template.NoSuchAttributeException: Attribute '…' not found.

  • Clean build, clear browser cache, …
  • Check if somewhere in the stacktrace is AjaxTilesBackendDecoratorView > Debug into it, or re-check the ajax JS in the (multi)element you’re working on…
  • Debug in DispatcherServlet to find out more…

Metadata elements do not show up in a new form element after opening an existing document

  • Re-check if the output of the _bean.xslt transformation script matches the names or aliases in the Groovy bean.
  • Also, in the _bean.xslt scripts, check that xsl:value-of statements do not end in /text() (which might happen if you develop your patterns in an XSLT editor.
  • Also, in the Groovy bean, check that the classname matches the filename, since there is no validation for that and the bean definition uses the filename.

Metadata elements do not show up in the output document

  • Re-check if the XStreamAlias of the Groovy bean is correctly used in the _iso.xslt transformation. Check the XPaths used to read the beanDoc variable are correct.
  • Also, in the Groovy bean, check that the classname matches the filename, since there is no validation for that and the bean definition uses the filename. Check the xstreamaliases of attributes as well if they match the attribute name in the storage in the jsp files.
  • Save the metadata document to a local file. Then open the debug log. There you will see a repeated occurrence of logs starting with the "de.conterra.smarteditor.service.TemplatesCache - Trying to load XSL resource from URL" which tells you what the currently executed transformation is. Then the BeanTransformerService logs the input and output documents it tries to merge by running the XSLT script, namely the beanDoc in the log after "... XMLBeans documents was", and the created output document after the log "... Result document is".
    • If the problematic output is included, but it is missing from subsequent logs, then a copy statement might be lacking in subsequent XSLT scripts.
    • If the problematic output is not included but the input beanDoc is OK, recheck the matching of the base element in the _iso.xslt script: Is the element that you match against included? It might not be there in the used template, so you have to match higher up in the document and subsequently create the intermediate required parent elements, copy untouched elements, and then do your own transformation. An example of this can be seen in DistributorFormat_iso.xslt.

How to add a new metadata encoding

Describe the SensorML overlay.

How can I add my own Schematron-based validation?

What if the validation fails without an error message?

In rare cases it happens that the smartEditor validation that is activated when a user clicks on "Publish" fails without proper error reporting.

  • Check if the id attribute of the schematron assertions are referring to an actually existing object in the DOM, e.g. ... id="storage["keywords"].items...
  • To identify the error, *debug into* MetadataValidator.validate(..) and ValidationUtils.invokeValidator(..), which logs errors only at DEBUG level (so this level has to be activated for the package org.springframework.validation).
  • Check the logs for occuranced of svrl:failed-assert, then check the logs in the following lines coming from the class de.conterra.smarteditor.util.XPathUtil what the failed assertions are. There should be many fields. If there is only one, re-check if there is a typo in the id of the corresponding assertion.

How is the connection with a webservice realized?

The user interface for the web service editor tab is in the module smarteditor-webapp, file /web-inf/jsp/start/service.jsp

The entry point from the JSP into the Java code is in the module smarteditor-api, method StartEditorController.startServiceHandler() (method receives the values from the .jsp)

Get the data of the webservice:

The class de.conterra.smarteditor.beans.StartEditorBean receives all values from the form in WEB-INF.jsp.start.service.jsp, which is the webpage displayed when selecting "Service" on the smartEditor start page. New form elements for service.jsp can be added here. The method StartEditorController.startServiceHandler() gets the elements using the bean.

The class WebServiceDescriptionDAO is an abstract class to manage the webservice connection, i.e. requesting the data based on the form inputs by the user.

For every webservice there is an implementation extending the abstract class in the package de.conterra.smarteditor.dao:
  • OgcWebServiceDescriptionDAO
  • InspireWebServiceDescriptionDAO
  • ArcImsWebServiceDescriptionDAO
There is a service factory WebServiceDescriptionFactory: Using it, one of the extensions of the class WebServiceDescriptionDAO can be choosen. The different extensions can be configured in the xml file WEB-INF/dao-definitions.xml in the bean with id="webServiceFactory". The class de.conterra.smarteditor.controller.StartEditorController uses the factory.
It gets the right webservice class because of the selection of the user in WEB-INF.jsp.start.service.jsp (value: serviceType). So the keys in src/main/resources/codelist_enumeration.xml, identifier: CT_ServiceTypeExt, need to be the same like in WEB-INF/dao-definitions.xml in the bean with id="webServiceFactory".

All these classes have the method getDescription(). This method does a "GetCapabilities" request to the web service and returns the result (the data for the form) as an object of the type org.w3c.dom.Document.

With the result of getDescription() the class StartEditorController initializes the backend and the editor is displayed to the user.

Publish the data to the webservice:

The "publish" button is declared in WEB-INF/jsp/menu.jsp. Clicking on it the form #updateMetadata (see WEB-INF/jsp/body.jsp) will be submitted. The form #updateMetadata is linked to de.conterra.smarteditor.controller.EditController.java within the file WEB-INF/dispatcher-servlet.xml.

The class EditController.java receives the data from the form with the method onSubmit(). This method returns the "successView" ("editor.puplish", declared in WEB-INF/dispatcher-servlet.xml). "editor.publish" links to the JSP /WEB-INF/jsp/selectStates.jsp (see WEB-INF/defs/tiles-editor.xml).

At the page selectStates.jsp the fields for values which should be selected by the user (like Authorization Token, ProcedureID,...) can be added. Clicking on the "publish" button the class PublishController.java receives the data (declared in WEB-INF/dispatcher-servlet.xml, in selectStates.jsp commandName="publish": for the form which data are used and action="publish.do": for the new URL and the change to the active controller "!PublishController.java").

The data from this form (id="publishForm") are from the class de.conterra.smarteditor.beans.PublishBean, which is defined in WEB-INF/dispatcher-servlet.xml in the "publishController" bean. Additional values, which the user can select (like Authorization Token, ProcedureID,...) need to be declared in this bean to transfer them to the controller.

In the Class PublishController.java the form data are received within the method onSubmit(). In this method the data get to be published to the csw endpoint.

Therefore the class de.conterra.smarteditor.dao.CatalogServiceDAO.java is used. For this class exists a bean in WEB-INF/dao-definitions.xml. For another service like the SOS an equivalent class like CatalogServiceDAO.java needs to be created.

The bean userInfoBean, defined in WEB-INF/dao-definitions.xml (class: de.conterra.smarteditor.beans.UserInfoBean), stores the information, if the data should be updated or inserted to the csw, in setUpdate() and can they can be get by isUpdate().

For the response of the SOAP request there is the Class de.conterra.smarteditor.cswclient.facades.TransactionResponse.java. This response is put to the ModelMap and used in the "successView" = "editor.finished" (see WEB-INF/dispatcher-servlet.xml in the bean "publishController"). "editor.finished" links to the JSP /WEB-INF/jsp/finished.jsp (see WEB-INF/defs/tiles-editor.xml). For services which are not a csw an equivalent class of TransactionResponse.java needs to be created.

The JSP finished.jsp shows uses the methods from de.conterra.smarteditor.cswclient.facades.TransactionResponse.java to display the result.

How to create tooltips?

To create a tooltip for a label, a new file for the content of the tooltip has to be created. The content is described with HTML and the file needs to have the ending ".html". The file has to be put into the folder src/main/webapp/tooltips. The name of this file has to be the same like the id of the html element for which the tooltip should be created. This html element needs to have the class "tooltip".
Topic revision: r14 - 03 Sep 2015 08:56:20, JanaKlemp
This site is powered by FoswikiCopyright &© by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki? Send feedback