Spring Roo best practise showing dates in local time - java

The power of Spring Roo is that it handles the difficult stuff.
Question: if there a good best practice and implementation on how to show to each user, where ever he lives on this planet, the dates in local time.
Issue:
When a user enters a date and goes to the "list all visits" page, he/she will see a total different date, if he /she is in a different timezone than the OpenShift server. For example I'm in GMT+1 (West Europe) timezone.
My situation:
Web application is developed with Spring Roo 2.0 / gvNIX 2.0
Will be hosted on OpenShift (rhcloud.com)
Like the petclinic sample: https://petclinic-gvnix.rhcloud.com/
Users can live everywhere in the world, so every timezone.
Java 8 is at this time not an option, because OpenShift has not TomCat 8 deployment yet, and, if possible, I want to keep away from creating a OpenShift DIY application.
Requirement:
Each user should see the dates in his local time.
Reproduce:
Go the the Petclinic sample and log in: https://petclinic-gvnix.rhcloud.com/
Choose Visit -> Create new visit
Choose List all Visits
When you are in a different timezone than the Openshift server you get something like above

Having thought about this problem, I think I have a clean solution for implementing Timezone support without affecting current projects.
My starting point is that the solution should be backward compatible and should not break current projects. Also the change should be as little as possible.
I tested it and it works, also when my web application runs on Openshift, where it is hosted in TimeZone GMT-5:00 and me living in West Europe, GMT +1:00
Please give me your opinions.
I will describe the solution / proposal for the show.jspx views. But in the same way it can be implemented for the other views as well.
The essense of the change is that I added timeZone to the fmt:formatDate .. statement in WEB-INF/tags/form/fields/column.tagx/display.jspx:
Old:
<fmt:formatDate value="${object[field]}" pattern="${fn:escapeXml(dateTimePattern)}" />
New:
<fmt:timeZone value="${timeZone}">
<fmt:formatDate value="${object[field]}" pattern="${fn:escapeXml(dateTimePattern)}" timeZone="${timeZone}" />
</fmt:timeZone>
For this to work I also added the declaration and a default timeZone setting when not used.
<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions" xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" xmlns:spring="http://www.springframework.org/tags" xmlns:form="http://www.springframework.org/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
<jsp:output omit-xml-declaration="yes" />
<jsp:directive.attribute name="id" type="java.lang.String" required="true" rtexprvalue="true" description="The identifier for this tag (do not change!)" />
<jsp:directive.attribute name="object" type="java.lang.Object" required="true" rtexprvalue="true" description="The form backing object" />
<jsp:directive.attribute name="field" type="java.lang.String" required="true" rtexprvalue="true" description="The field name" />
<jsp:directive.attribute name="label" type="java.lang.String" required="false" rtexprvalue="true" description="The label used for this field, will default to a message bundle if not supplied" />
<jsp:directive.attribute name="date" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Indicate that this field is of type java.util.Date" />
<jsp:directive.attribute name="calendar" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Indicate that this field is of type java.util.Calendar" />
<jsp:directive.attribute name="dateTimePattern" type="java.lang.String" required="false" rtexprvalue="true" description="The date / time pattern to use if the field is a date or calendar type" />
========== Added declaration
<jsp:directive.attribute name="timeZone" type="java.lang.String" required="false" rtexprvalue="true" description="The timezone to use if the field is a date or calendar type" />
========== End
<jsp:directive.attribute name="render" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Indicate if the contents of this tag and all enclosed tags should be rendered (default 'true')" />
<jsp:directive.attribute name="z" type="java.lang.String" required="false" description="Used for checking if element has been modified (to recalculate simply provide empty string value)" />
<c:if test="${empty render or render}">
<c:if test="${not empty object and empty label}">
<spring:message code="label_${fn:toLowerCase(fn:substringAfter(id,'_'))}" var="label" htmlEscape="false" />
</c:if>
<c:if test="${empty dateTimePattern}">
<c:set value="MM/dd/yyyy" var="dateTimePattern" />
</c:if>
========== Added default setting. Taking the timezone of the server!
<c:if test="${empty timeZone}">
<jsp:scriptlet>
jspContext.setAttribute("timeZone", java.util.TimeZone.getDefault().getID());
</jsp:scriptlet>
</c:if>
========== End
<div id="_${fn:escapeXml(id)}_id">
<label for="_${fn:escapeXml(field)}_id">
<c:out value="${label}" />
:
</label>
<div class="box" id="_${fn:escapeXml(id)}_${fn:escapeXml(field)}_id">
<c:choose>
<c:when test="${date}">
<spring:escapeBody>
========== Changed: added timeZone support for Date
<fmt:timeZone value="${timeZone}">
<fmt:formatDate value="${object[field]}" pattern="${fn:escapeXml(dateTimePattern)}" timeZone="${timeZone}" />
</fmt:timeZone>
========== End
</spring:escapeBody>
</c:when>
<c:when test="${calendar}">
<spring:escapeBody>
========== Changed: added timeZone support for Calendar
<fmt:timeZone value="${timeZone}">
<fmt:formatDate value="${object[field].time}" pattern="${fn:escapeXml(dateTimePattern)}" timeZone="${timeZone}" />
</fmt:timeZone>
========== End
</spring:escapeBody>
</c:when>
<c:otherwise>
<spring:eval expression="object[field]" />
</c:otherwise>
</c:choose>
</div>
</div>
<br />
</c:if>
</jsp:root>
When adding / changing these few lines, all the old code will continue to work.
You will have to apply these changes also to the WEB-INF/tags/form/fields/column.tagx file.
I you use gvNIX JQuery the same changes should be applied there also.
This is all the change you have to do!!!!
To add timezone support in all your show.jspx files:
1) add the extra timezone option for any date.
old:
<field:display date="true" dateTimePattern="${fileUpload_uploaddate_date_format}" field="uploadDate" id="s_com_myproject_onlineviewer_domain_FileUpload_uploadDate" object="${fileupload}" z="user-managed"/>
new:
<field:display date="true" dateTimePattern="${fileUpload_uploaddate_date_format}" field="uploadDate" id="s_com_myproject_onlineviewer_domain_FileUpload_uploadDate" object="${fileupload}" timeZone="${fileUpload_uploaddate_date_timezone}" z="user-managed"/>
2) In your controller specify your timeZone like:
uiModel.addAttribute("fileUpload_uploaddate_date_timezone", "Europe/Amsterdam");
In the spirit of Spring Roo's dataformat addDateTimeFormatPatterns(uiModel); I added addTimeZone(uiModel); .
In this method you can specify the source of the timezone.
In my web application I ask the user to specify his/her timezone when they reigster. All other workaround fail somewhere.
void addTimeZone(Model uiModel) {
uiModel.addAttribute("fileUpload_uploaddate_date_timezone", UserUtils.geTimeZone());
}
BTW, an addTimeZone default method in the AspectJ controller could be as the sample below, leaving the programmer to be able to push in and alter it.
void FileUploadController.addTimeZone(Model uiModel) {
uiModel.addAttribute("fileUpload_uploaddate_date_timezone", TimeZone.getDefault().getID());
}

Related

OWL java use same data property for two classes

I am working with OWL and I have defined 2 classes and they share a data propertie:
<Declaration>
<DataProperty IRI="#hasLastName" />
</Declaration>
<DataPropertyDomain>
<DataProperty IRI="#hasLastName" />
<Class IRI="#Class1" />
</DataPropertyDomain>
<DataPropertyDomain>
<DataProperty IRI="#hasLastName" />
<Class IRI="#Class2" />
</DataPropertyDomain>
In java I have created a project that starting from an owl file and an xml file it creates a form.
in my xml file I define the sections of the form in this way:
<section>
<iri>http://www.sample.com/myontology#class1</iri>
<infoList>
<info type="text" property="http://www.sample.com/myontology#hasLastName" required="true" />
<info type="text" property="http://www.sample.com/myontology#hasFirstName" required="true" />
<info type="text" property="http://www.sample.com/myontology#hasEmail" required="false" />
<info type="text" property="http://www.sample.com/myontology#hasPhone" required="false" />
</infoList>
</section>
<section>
<iri>http://www.sample.com/myontology#class2</iri>
<infoList>
<info type="text" property="http://www.sample.com/myontology#hasLastName" required="true" />
<info type="text" property="http://www.sample.com/myontology#hasUser" required="false" />
<info type="text" property="http://www.sample.com/myontology#hasRole" required="false" />
</infoList>
</section>
How can I access the different Last name Properties for class 1 and class 2?
I retrieve it by http://www.sample.com/myontology#hasLastName
There is something like http://www.sample.com/myontology#Class1#hasLastName ?
Sorry I am a very beginner with Ontology and it is not very clear to me
Assuming you have an ontology as follows:
Datatype: xsd:string
DataProperty: hasLastName
Domain:
Person,
Student
Range:
xsd:string
Class: Person
Class: Student
the following code will retrieve the 2 domains:
IRI lastNamePropertyIRI = IRI.create(ontologyIRI + "#hasLastName");
OWLDataProperty lastNameProperty = dataFactory.getOWLDataProperty(lastNamePropertyIRI);
List<OWLClassExpression> domainClasses =
ontology
.dataPropertyDomainAxioms(lastNameProperty)
.map(OWLDataPropertyDomainAxiom::getDomain)
.collect(Collectors.toList());
for (OWLClassExpression owlClass : domainClasses) {
logger.trace("Domain class = " + owlClass);
}
However, there are some other problems I am concerned with here which relates to the ontology rather than the code.
For the ontology I have given, whenever you specify that an individual john is linked to some surname via hasLastName, the ontology reasoner will infer that john is both a Person and a Student, i.e. the domain of hasLastName is the intersection of Person and Student. Clearly, this is not true for people in general. There are 2 possible solutions to this, depending on your needs:
(1) You can specify that the domain of hasLastName is Person or Student which will take the domain to be the union of Person and Student.
(2) The solution I prefer is to define Student as a subclass of Person and then to state that the domain of hasLastName is the single class Person.

How to specify language in addPrompt() in Rivr Java VoiceXML library

My VoiceXML provider, Nexmo, seems not to handle the xml:lang="es-ES" attribute in the root vxml (This is generated by Rivr with a context.setLanguage("es-ES") in my Dialog)
I want Nexmo to use a spanish TTS engine but as I am using Rivr, I can't see where I can specify that I want the "prompt" to include, for example, xml:lang="es-es-female", so it generates VoiceXML:
<prompt xml:lang="es-es-female">
Hola.
</prompt>
interaction().addPrompt() only accepts the SpeechSynthesis object which does not allow (as far as I see) language options.
I've also tried include SSML in the SpeechSynthesis object (using a DocumentFragment as I see in Rivr Javadoc) but that won't work. Probably Nexmo does not support SSML.
Any workarounds? (A part from changing to a better VoiceXML provider)
Thanks a lot!!!
If you only want to play a message without getting input from the user, use can use the Message class:
//Play a synthesis message in another language
Message message = new Message("synthesis-french-message",
new SpeechSynthesis("Ceci est un message."));
message.setLanguage("fr-CA");
DialogueUtils.doTurn(message, context);
If you need to specify the language for a prompt in an Interaction, this can be done with the InteractionBuilder. The setLanguage() method can be used before the addPrompt() method. Multiple languages can be used within the same interaction:
Interaction interaction = OutputTurns.interaction("multilingual-interaction")
.setLanguage("es-ES")
.addPrompt(new SpeechSynthesis("Holá."))
.setLanguage("fr-CA")
.addPrompt(new SpeechSynthesis("Bonjour."))
.build(new SpeechRecognition(new GrammarReference("grammar.grxml")),
Duration.seconds(2));
DialogueUtils.doTurn(interaction, context);
If you don't want to use the builder, you can do it by hand but it's much longer:
List<Interaction.Prompt> prompts = new ArrayList<Interaction.Prompt>();
Interaction.Prompt spanishPrompt = new Interaction.Prompt(new SpeechSynthesis("Holá."));
spanishPrompt.setLanguage("es-ES");
prompts.add(spanishPrompt);
Interaction.Prompt frenchPrompt = new Interaction.Prompt(new SpeechSynthesis("Bonjour."));
frenchPrompt.setLanguage("fr-CA");
prompts.add(frenchPrompt);
SpeechRecognition speechRecognition = new SpeechRecognition(new GrammarReference("grammar.grxml"));
FinalRecognitionWindow finalRecognitionWindow = new FinalRecognitionWindow(speechRecognition,
Duration.seconds(2));
Interaction interaction2 = new Interaction("multilingual-interaction2",
prompts,
finalRecognitionWindow);
DialogueUtils.doTurn(interaction2, context);
The output VoiceXML is:
<?xml version="1.0" encoding="UTF-8"?>
<vxml application="/rivr-cookbook-message-language/dialogue/root/efe10575-1766-48fb-9e13-572a771bc5f4" version="2.1"
xmlns="http://www.w3.org/2001/vxml">
<script>application.rivr.localErrorHandling = false; application.rivr.inputTurn = {};</script>
<form id="form">
<block name="prompt0">
<prompt bargein="false" xml:lang="es-ES">Holá.</prompt>
</block>
<block name="prompt1">
<prompt bargein="false" xml:lang="fr-CA">Bonjour.</prompt>
</block>
<field name="recognition">
<grammar mode="voice" src="grammar.grxml" />
<property name="timeout" value="2000ms" />
</field>
<filled mode="any">
<script>application.rivr.addRecognitionResult()</script>
<goto next="#submitForm" />
</filled>
</form>
<catch>
<if cond="_event.substring(0, 5) == "error"">
<if cond="application.rivr.localErrorHandling">
<goto next="#fatalErrorForm" />
<else />
<script>application.rivr.localErrorHandling=true</script>
</if>
</if>
<script>application.rivr.addEventResult(_event, _message)</script>
<goto next="#submitForm" />
</catch>
<form id="fatalErrorForm">
<block>
<exit />
</block>
</form>
<form id="submitForm">
<block>
<var expr="application.rivr.toJson(application.rivr.inputTurn)" name="inputTurn" />
<if cond="application.rivr.hasRecording(application.rivr.inputTurn)">
<var expr="application.rivr.inputTurn.recordingMetaData.data" name="recording" />
<assign expr="undefined" name="application.rivr.inputTurn.recordingMetaData.data" />
<submit enctype="multipart/form-data" method="post" namelist="inputTurn recording"
next="/rivr-cookbook-message-language/dialogue/efe10575-1766-48fb-9e13-572a771bc5f4/0/multilingual-interaction2" />
<else />
<submit method="post" namelist="inputTurn"
next="/rivr-cookbook-message-language/dialogue/efe10575-1766-48fb-9e13-572a771bc5f4/0/multilingual-interaction2" />
</if>
</block>
</form>
</vxml>

How to enable advanced search by d:date properties in Alfresco?

I have a custom content model I created for Alfresco that has type with a d:date property. I am able to build the repository and share projects with seemingly no errors. However, I am unable to search by the properties using the data type d:date or d:int. I resolved the d:int problem by changing the data type to d:text and adding a regex constraint, but I'm not sure if that would be prudent for the d:date property.
Is there some additional configuration that I need to supply or create in order to search by properties that are not d:text?
Here is a snippet showing the type declaration:
<types>
<!-- Enterprise-wide generic document type -->
<type name="gl:x">
<title>Document</title>
<parent>cm:content</parent>
<properties>
<property name="gl:period">
<type>d:text</type>
</property>
<property name="gl:year">
<type>d:text</type>
<constraints>
<constraint ref="gl:documentYears" />
</constraints>
</property>
<property name="gl:docType">
<type>d:text</type>
<constraints>
<constraint ref="gl:documentTypeList" />
</constraints>
</property>
<property name="gl:date">
<type>d:date</type>
</property>
</properties>
</type>
</types>
The share search forms and properties forms seem to be rendering correctly, so I don't think that there is any problem within those.
The advanced search page accepts two types of parameters.
One is simply the "keywords" field. This performs a full text search, i.e. it looks for the provided keywords in ANY text property. There is no need to configure the full text search for custom types (e.g. your gl:x) - it automatically picks up any text property in any model in the system.
The other is the group of single parameters: name, title, description, mime-type, modified-date, modifier. These properties can be of any type. A d:date property would be perfectly acceptable here, as the modified-date parameter testifies.
But here custom properties are not picked-up automatically. They need to be configured explicitly.
Notice that in the upper part of the advanced search page is a drop-down called "Look for" with two options: content and folders. The best approach would be to add an option for your content type gl:x and to configure a search form for it.
You can find the definition of the two standard search forms in tomcat/webapps/share/WEB-INF/classes/alfresco/share-form-config.xml. The file is rather long so here are the two sections to look for:
<config evaluator="model-type" condition="cm:content">
<forms>
<!-- Default Create Content form -->
<form>
</form>
<!-- Document Library Create Google Doc form -->
<form id="doclib-create-googledoc">
</form>
<!-- Search form -->
<form id="search">
</form>
</forms>
</config>
<!-- cm:folder type (creating nodes) -->
<config evaluator="model-type" condition="cm:folder">
<forms>
<!-- Document Library Common form -->
<form id="doclib-common">
</form>
<!-- Search form -->
<form id="search">
</form>
</forms>
</config>
I've skipped the details, but what is important is that "cm:content" and "cm:folder" each defines a <form id="search"> with the desired search properties/parameters.
As an experiment you could modify share-form-config.xml directly and add your own definition:
<config evaluator="model-type" condition="gl:x">
<forms>
<!-- Search form -->
<form id="search">
<field-visibility>
<show id="gl:date" />
</field-visibility>
<appearance>
<field id="gl:date">
<control template="/org/alfresco/components/form/controls/daterange.ftl" />
</field>
</appearance>
</form>
</forms>
</config>
Also you have to add the new search form to the AdvancedSearch configuration found in tomcat/webapps/share/WEB-INF/classes/alfresco/share-config.xml:
<config evaluator="string-compare" condition="AdvancedSearch">
<advanced-search>
<forms>
<form labelId="search.form.label.cm_content" descriptionId="search.form.desc.cm_content">cm:content</form>
<form labelId="search.form.label.cm_folder" descriptionId="search.form.desc.cm_folder">cm:folder</form>
<form labelId="search.form.label.gl_x" descriptionId="search.form.desc.gl_x">gl:x</form>
</forms>
</advanced-search>
</config>
Remember to restart alfresco after every change.
When you're satisfied with the results, it would be better to move your custom definitions to a separate share-config-custom.xml in your project (share-config.xml and share-form-config.xml should never be modified directly).
For more details: https://wiki.alfresco.com/wiki/Share_Advanced_Search

Slow iteration in displaytag on 70000 or more records

I am using displayTag for tabular view in my reports . It works fine up to 60K records but when the data exceeds 70K or more it took some time to display . Is there any limit for data in displayTag and same issue in export option why so much delay on heavy data.
My table has 30 columns and 70k+ rows
Code
<display:table id="listDisplay1" list="showLeadList" name="showLeadList" export="${session['Lead Status Report'][4]}" requestURI="" cellpadding="0" cellspacing="0" pagesize="50">
<display:setProperty name="export.csv" value="false" />
<display:setProperty name="export.xml" value="false" />
<display:setProperty name="export.excel" value="false" />
<display:setProperty name="export.[mymedia].filename" value="LeadStatus_details${currentDateTime}.xlsx" />
<display:setProperty name="export.pdf" value="true" />
<display:setProperty name="export.pdf.filename" value="LeadStatus_details${currentDateTime}.pdf"/>
<display:setProperty name="paging.banner.placement" value="bottom" />
<display:column property="sno" title="S.No." media="html excel"></display:column>
<s:iterator var="DisplayMap" value="#attr.listDisplay1.columnMap" >
<display:column title="${DisplayMap.key}" value="${DisplayMap.value}" />
</s:iterator>
</display:table>
I am using version(2.3.15) of Struts2.
2nd Question Is it true that displayTag use get method for export? if yes then how can we modify it to post method?
This is the only problem I ever had with displayTag!
it load whole table in memory for each time you use it! even if you change the page!
you have to change Query in source code and add some boundaries!

struts validator client side, alertbox without message

I use commons-validator v1.2 specification-version : 1.0 whereas my spec-version for struts is : 1.1. Although everything works fine except the message display in the alert box. The alert box display case are relevant.
By instance this jsp field :
<td class="RechBlocCiel">
<input name="nom" type="text" class="inputForm" size="2" maxlength="50" />
</td>
I put in validation.xml :
<field property="nom" depends="required">
<msg name="required" key="error.nom" />
</field>
related to
<validator name="required"
classname="org.apache.struts.validator.FieldChecks"
method="validateRequired"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionMessages,
javax.servlet.http.HttpServletRequest"
msg="errors.required" />
and in struts config the plugin is declared :
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />
</plug-in>
<message-resources
parameter="xxx.resources.ApplicationResources" />
In xxx.resources.ApplicationResources I have the properties file : ApplicationResources.properties which contains:
errors.required = <li> Le champ [{0}] est obligatoire.</li>
error.nom = nom
It seems to me that everything is complete, so I'm not sure where to search now, except in a problem of version between validator and struts.
I 'm looking for the message : "Le champ nom est obligatoire."
I add depends = "" to
<validator name="required"
classname="org.apache.struts.validator.FieldChecks"
method="validateRequired"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionMessages,
javax.servlet.http.HttpServletRequest"
depends="" msg="errors.required" />
then the error.nom message appears in the alert box. I don't solve the problem completely yet but at least it works with
error.nom = Le champ nom est obligatoire.
Though this solutino isn't generic for all my form fields required. Instead of adding the field name to the generic message I have redondance of "Le champ" "est obligatoire" in my .properties
definitive answer :
I needed to use struts tag : html:text etc. to benefit of this mecanism.
Then, I modify the validation rules :
<field property="nom" depends="required">
<arg0 key="error.nom"/>
</field>
And it works fine!

Categories