Customize DateTimeField in Vaadin 8 - vaadin8

In DateTimeField component, it displays both date (year, month, day) and hour (hour, minute, second).
Now i don't want to get date. Any idea to only allow to show and get time ( hour:minute:second) ?

tl;dr
No built-in way to display time-of-day only, as of Vaadin 8.1.
You can make your own.
Details
The existing DateTimeField]() widget supports only the legacy GregorianCalendar class which is a combination of a date and a time-of-day plus a time zone. So not useful for time-of-day only values.
Unfortunately, as of Vaadin 8.1, it seems the bundled field widgets have not yet been updated for the java.time types such as the java.time.LocalTime you would want.
As a workaround for the lack of an intelligent LocalTime-savvy field, I suggest either:
Try the date-time-fields Add-on.An add-on is an extension you can add to your Vaadin project, to deliver some nugget of Vaadin-related functionality. An add-on may be visual widget related, or may be non-visual backend feature. If visual such as this, you may need to do a rebuild of your project to cause the widgetset to be re-created. Perhaps a Maven clean and install (not sure as the technique for this keeps changing with various Vaadin releases).
Make your own. Use a regular text field. Write a validator to verify user input parses as a LocalTime object. You have a choice of writing a Vaadin-specific validator or a standard Bean Validation validator. See How to add Validators in Vaadin 8?
Example
Here is some rough code for creating your own LocalTime-savvy field. This is by no means complete, but may help point you in the right direction.
final Label whenLabel = new Label( "when?" );
final TextField whenField = new TextField( );
whenField.setCaption( "When:" );
whenField.setValueChangeMode( ValueChangeMode.BLUR );
whenField.addValueChangeListener( new HasValue.ValueChangeListener < String >( )
{
static final long serialVersionUID = 201710132100L;
#Override
public void valueChange ( HasValue.ValueChangeEvent < String > valueChangeEvent )
{
String input = whenField.getValue( );
System.out.println( "input: " + input );
try
{
// DateTimeFormatter f = DateTimeFormatter.ISO_LOCAL_TIME; // Constant ISO_LOCAL_TIME is for time-of-day in standard ISO 8601 format.
// DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT ).withLocale( Locale.CANADA_FRENCH ); // Automatically localize.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT ).withLocale( Locale.US ); // Automatically localize.
LocalTime localTime = LocalTime.parse( input , f );
String timeIso8601 = localTime.toString( );
whenLabel.setValue( timeIso8601 );
} catch ( DateTimeParseException e )
{
whenLabel.setValue( e.getClass().getCanonicalName() );
System.out.println( "ERROR - failed to parse input: " + input );
}
}
} );

In my solution I used a TextField and added a custom validator to the relevant binder:
.withValidator(this::validateTime, "Invalid time")
I implemented the validate method as follows:
private boolean validateTime(final String timeString) {
try {
LocalTime.parse(timeString);
return true;
} catch (final DateTimeParseException e) {
return false;
}
}

now Vaadin 14.2.x provided new DateTimePicker component to solve this problem.
https://vaadin.com/releases/vaadin-14

Related

Jackson XML "Undeclared general entity" caused by custom entity

I'm deserializing a large XML file (not mine) and it contains custom entities defined as:
<!ENTITY math "mathematics">
and elements used this way:
<field>&math;</field>
When I try to deserialize it by:
XmlMapper xmlMapper = new XmlMapper();
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
return xmlMapper.readValue(classloader.getResourceAsStream("file.xml"), MyClass.class);
I get this error:
com.fasterxml.jackson.databind.JsonMappingException: Undeclared general entity "math"
I think it might be a security measure to prevent Xml External Entity injections.
Is there a way to mark these custom entities as valid? Like create an Enum for them or something?
If not, is there a flag to just parse these as Strings?
Update:
I was able to work around this problem by basically doing a find-replace on the text file. It's quite an ugly solution and if anyone has a better idea, I'm all ears. :)
I know it may be a little late, but just in case someone else is stuck on the same issue:
You have to set a custom XMLResolver as XMLInputFactory's property:
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.ctc.wstx.api.WstxInputProperties;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLStreamException;
var xmlMapper = new XmlMapper();
xmlMapper.getFactory().getXMLInputFactory().setProperty(
WstxInputProperties.P_UNDECLARED_ENTITY_RESOLVER,
new XMLResolver() {
#Override
public Object resolveEntity(String publicId, String systemId, String baseUri, String ns) throws XMLStreamException {
// replace the entity with a string of your choice, e.g.
switch (ns) {
case "nbsp":
return " ";
default:
return "";
}
// some useful tool is org.apache.commons.text.StringEscapeUtils
// e.g.
// return StringEscapeUtils.escapeXml10(StringEscapeUtils.unescapeHtml4('&' + ns + ';'));
}
}
);
// then xmlMapper.readValue....

How to parse different ISO date/time formats with Jackson and java.time?

Our Rest API takes JSON input from several external parties. They all use "ISO-ish" formats, but the formatting of the time zone offset is slightly different. These are some of the most common formats we see:
2018-01-01T15:56:31.410Z
2018-01-01T15:56:31.41Z
2018-01-01T15:56:31Z
2018-01-01T15:56:31+00:00
2018-01-01T15:56:31+0000
2018-01-01T15:56:31+00
Our stack is Spring Boot 2.0 with Jackson ObjectMapper. In our data classes we use the type java.time.OffsetDateTime a lot.
Several developers have tried to achieve a solution that parses all of the above formats, none have been successful. Particularly the fourth variant with a colon (00:00) seems to be unparseable.
It would be great if the solution works without having to place an annotation on each and every date/time field of our models.
Dear community, do you have a solution?
One alternative is to create a custom deserializer. First you annotate the respective field:
#JsonDeserialize(using = OffsetDateTimeDeserializer.class)
private OffsetDateTime date;
And then you create the deserializer. It uses a java.time.format.DateTimeFormatterBuilder, using lots of optional sections to deal with all the different types of offsets:
public class OffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> {
private DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// offset (hh:mm - "+00:00" when it's zero)
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
// offset (hhmm - "+0000" when it's zero)
.optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
// offset (hh - "+00" when it's zero)
.optionalStart().appendOffset("+HH", "+00").optionalEnd()
// offset (pattern "X" uses "Z" for zero offset)
.optionalStart().appendPattern("X").optionalEnd()
// create formatter
.toFormatter();
#Override
public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return OffsetDateTime.parse(p.getText(), fmt);
}
}
I also used the built-in constant DateTimeFormatter.ISO_LOCAL_DATE_TIME because it takes care of the optional fraction of seconds - and the number of fractional digits seems to be variable as well, and this built-in formatter already takes care of those details for you.
I'm using JDK 1.8.0_144 and found a shorter (but not much) solution:
private DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// offset +00:00 or Z
.optionalStart().appendOffset("+HH:MM", "Z").optionalEnd()
// offset +0000, +00 or Z
.optionalStart().appendOffset("+HHmm", "Z").optionalEnd()
// create formatter
.toFormatter();
Another improvement you can make is change the formatter to be static final, because this class is immutable and thread-safe.
This is just about a quarter of an answer. I neither have experience with Kotlin nor Jackson, but I have a couple of solutions in Java that I’d like to contribute. I should be glad if you can fit them into a total solution somehow.
String modifiedEx = ex.replaceFirst("(\\d{2})(\\d{2})$", "$1:$2");
System.out.println(OffsetDateTime.parse(modifiedEx));
On my Java 9 (9.0.4) the one-arg OffsetDateTime.parse parses all of your example strings except the one with offset +0000 without colon. So my hack is to insert that colon and then parse. The above parses all of your strings. It doesn’t work readily in Java 8 (there were some changes from Java 8 to Java 9).
The nicer solution that works in Java 8 too (I have tested):
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendPattern("[XXX][XX][X]")
.toFormatter();
System.out.println(OffsetDateTime.parse(ex, formatter));
The patterns XXX, XX and X match +00:00, +0000 and +00, respectively. We need to try them in order from the longest to the shortest to make sure that all text is being parsed in all cases.
Thank you very much for all your input!
I chose the deserializer suggested by jeedas combined with the formatter suggested by Ole V.V (because it's shorter).
class DefensiveIsoOffsetDateTimeDeserializer : JsonDeserializer<OffsetDateTime>() {
private val formatter = DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendPattern("[XXX][XX][X]")
.toFormatter()
override fun deserialize(p: JsonParser, ctxt: DeserializationContext)
= OffsetDateTime.parse(p.text, formatter)
override fun handledType() = OffsetDateTime::class.java
}
I also added a custom serializer to make sure we use the correct format when producing json:
class OffsetDateTimeSerializer: JsonSerializer<OffsetDateTime>() {
override fun serialize(
value: OffsetDateTime,
gen: JsonGenerator,
serializers: SerializerProvider
) = gen.writeString(value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
override fun handledType() = OffsetDateTime::class.java
}
Putting all the parts together, I added a #Configuraton class to my spring classpath to make it work without any annotations on the data classes:
#Configuration
open class JacksonConfig {
#Bean
open fun jacksonCustomizer() = Jackson2ObjectMapperBuilderCustomizer {
it.deserializers(DefensiveIsoOffsetDateTimeDeserializer())
it.serializers(OffsetDateTimeSerializer())
}
}

Prevent angular2 app from crashing due to invalid date (maybe override DatePipe?)

I basically have an HTML5 date picker like this:
<input class="form-control" type="date" [(ngModel)]="startDate" />
And another area that displays the date from the model like this:
{{startDate | date:shortDate}}
I don't think it's relevant to this question but to be thorough, I have this kind of thing behind the scenes:
private startDate: string;
private endDate: string;
updateDateFilter() {
let from = new Date(this.startDate);
let to = new Date(this.endDate);
**Stuff to filter based on date range**
}
It's awesome. There's a lot more going on but it'd probably be extraneous detail.
The trouble is you can click into the date picker and, say, select the year and hit delete which does this:
No problem right? Wrong, because the Angular2 date pipe gets confused and my whole app crashes after displaying this surprisingly helpful error message:
ORIGINAL EXCEPTION: Invalid argument '' for pipe 'DatePipe'
The solution seems simple but I'm not sure how to go about it. I'm thinking I just need to catch the date string before it's processed by the DatePipe and make sure there's valid content for the day, month, and year. But Angular2 does magic and the since it's done using model binding I don't see a clear route to intervention.
I probably need to override the DatePipe, keeping its functionality but checking the string first. I don't know how. If you know how to do that, do share. Any alternative approaches would be helpful too. I just can't have the app crash whenever the date is incomplete.
Thanks!
You could create your own pipe and either extend the default DatePipe or just instantiate it and call it programmatically.
#Pipe({
name: 'customDate'
})
export class CustomDatePipe extends DatePipe {
transform(date: string, pattern?: string): string {
// validate/initialize date
return super.transform(date, pattern);
}
}
Or the second case:
export class CustomDatePipe {
private datePipe: DatePipe = new DatePipe();
transform(date: string, pattern?: string): string {
// validate/initialize date
return this.datePipe.transform(date, pattern);
}
}
Did not had the chance to check this but I assume both approaches should work.

Tapestry: Events from Palette component

I'm using palette components on a page and I want the available elements in two of them to change depending on what is selected in the first.
What is the best way to achieve this? Which events are thrown by the palette component, that I could listen to, adapt the palette's model and perform a zone update? I thought it would work the same way as for select components doing something like this:
void onValueChanged() {
// do something
}
Unfortunately that doesn't work for palettes.
I'm using Tapestry 5.4-beta-6, but I guess things haven't changed that much since earlier versions.
I'd probably do this with a mixin.
public class PaletteChange {
#Parameter
private String zone;
#InjectContainer
private Palette palette;
public void afterRender() {
Link eventLink = componentResources.createEventLink("change");
JSONObject args = new JSONOBject(
"id", pallete.getClientId(),
"url", eventLink,
"zone", zone
);
javascriptSupport.addScript("palleteChange(%s)", args);
}
Object onChange(#RequestParameter("value") String value) {
CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
resources.triggerEvent("change", new String[] { value }, callback);
return callback.getResult();
}
}
Javascript
function palleteChange(spec) {
var field = $('#' + spec.id + '/select[1]');
field.on('change', function() {
var zoneManager = Tapestry.findZoneManagerForZone(spec.zone);
var params = { value: field.val() };
zoneManager.updateFromURL(spec.url, params);
});
}
Then use the mixin in your code
<t:palette t:id="myPalette" t:mixins="paletteChange" zone="myZone" ... />
<t:zone t:id="myZone">
...
</t:zone>
Page
#Inject
private Zone myZone;
Block onChangeFromMyPalette(String value) {
doStuff(value);
return myZone.getBody();
}
See here for a similar mixin.
I finally used the didChange element together with a similar mixin like the Observe mixin. I put a demo on Github for anyone, who is interested.
Just a few notes:
I used 5.4 beta 6, it already has the necessary client side events.
I couldn't make it work using a Tapestry javascript module, so I still use javascriptSupport.addInitializerCall.
The remaining problem is, that updating the second palette with a zone update will reset any changes the user has made in this palette, since they are only kept on the client side (in a hidden field). I will still need to look into that, but it is not part of the original question.

Hibernate flush optimization using `hibernate.ejb.use_class_enhancer`

I am trying to use the hibernate feature that enhances the flush performance without making code changes. I came across the option hibernate.ejb.use_class_enhancer.
I made the following changes.
1) enabled the property hibernate.ejb.use_class_enhancer to true.
Build failed with error 'Cannot apply class transformer without LoadTimeWeaver specified'
2) I added
context:load-time-weaver to the context files.
Build failed with the following error :
Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring’s agent: -javaagent:spring-agent.jar
3) I added the following to the maven-surefire-plugin
javaagent:${settings.localRepository}/org/springframework/spring-
agent/2.5.6.SEC03/spring-agent-2.5.6.SEC03.jar
the build is successful now.
We have an interceptor that tracks the number of entities being flushed in a transaction.
After I did the above changes, I was expecting that number to come down significantly, but, they did not.
My question is:
Are the above changes correct/enough for getting the 'entity flush optimization'?
How to verify that the application is indeed using the optimization?
Edit:
After debugging, I found the following.
There is a time when our DO class is submitted for transformation, but, the logic that figures out whether a given class is supposed to be transformed is not handling the class names correctly (in my case), because of that, the DO class goes without being transformed.
Is there a way I can pass my logic instead ?
the relevant code is below.
The return copyEntities.contains( className ); is coming out false for the following inputs.
copyEntities contains list of strings "com.x.y.abcDO", "com.x.y.asxDO" where are the className is "com.x.y.abcDO_$$_jvsteb8_48"
public InterceptFieldClassFileTransformer(List<String> entities) {
final List<String> copyEntities = new ArrayList<String>( entities.size() );
copyEntities.addAll( entities );
classTransformer = Environment.getBytecodeProvider().getTransformer(
//TODO change it to a static class to make it faster?
new ClassFilter() {
public boolean shouldInstrumentClass(String clas sName) {
return copyEntities.contains( className );
}
},
//TODO change it to a static class to make it faster?
new FieldFilter() {
#Override
public boolean shouldInstrumentField(String clas sName, String fieldName) {
return true;
}
#Override
public boolean shouldTransformFieldAccess(
String transformingClassName, String fieldOwnerClassName, String fieldName
) {
return true;
}
}
);
}
edited on June 15th
I updated my project to use Spring 4.0.5.RELEASE and hibernate to 4.3.5.Final
I started using org.hibernate.jpa.HibernatePersistenceProvider
and
org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver
and
hibernate.ejb.use_class_enhancer=true
with these changes, I am debugging the flush behavior. I have a question in this code block .
private boolean isUnequivocallyNonDirty(Object entity) {
if(entity instanceof SelfDirtinessTracker)
return ((SelfDirtinessTracker) entity).$$_hibernate_hasDirtyAttributes();
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
persistenceContext.getSession().getFactory().getCustomEntityDirtinessStrategy();
if ( customEntityDirtinessStrategy.canDirtyCheck( entity, getPersister(), (Session) persistenceContext.getSession() ) ) {
return ! customEntityDirtinessStrategy.isDirty( entity, getPersister(), (Session) persistenceContext.getSession() );
}
if ( getPersister().hasMutableProperties() ) {
return false;
}
if ( getPersister().getInstrumentationMetadata().isInstrumented() ) {
// the entity must be instrumented (otherwise we cant check dirty flag) and the dirty flag is false
return ! getPersister().getInstrumentationMetadata().extractInterceptor( entity ).isDirty();
}
return false;
}
In my case, the flow is returning false because of persister saying yes for hasMutableProperties. I think the interceptor did not have a chance to answer at all.
Is it not that the bytecode transformer cause an interceptor here? Or the bytecode transform should make the entity a SelfDirtinessTracker?
Can anyone explain, what is the behavior I should expect here from the bytecode transformation here.

Resources