Jersey2 Custom Date convertor - date

Has anyone created a CustomConvertor class for Jersey2 to convert
ISO 8601 dates to Date /Epoch time?
I pass date as query param in ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; I need to convert to epoch seconds. I have all that is necessary, I am lost in gluing it up.
I want the custom convertor to kick in once we see the DateEpochMarker interface. I use jersey 2
what is step which I am missing?
Could some one please help me out?
I have a customer Convertor, a marker Interface and resource method.
public class DateToEpochConvertor implements ParamConverter<Long> {
private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
#Override
public Long fromString(String value) {
DateFormat df1 = new SimpleDateFormat(ISO_8601_FORMAT);
Date date = new Date();
try {
date = df1.parse(value);
} catch (ParseException e) {
throw new WebApplicationException("The Date "+value+" is not in the ISO 8601 Format ");
}
return date.getTime();
}
#Override
public String toString(Long value) {
DateFormat df1 = new SimpleDateFormat(ISO_8601_FORMAT);
Date dt = new Date();
dt.setTime(value);
return df1.format(dt);
}
}
Marker Interface
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface DateEpochMarker {}
Resource Method
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Path("/epochtime")
public Long getEpochTime(#DateEpochMarker #QueryParam("startTime") Long startEpochTime){
return startEpochTime;
}

I feel foolish to answer my own Question.
Somehow I was not been able to make the above working, what I ended up was using custom Jodatime Convertor.
Pasting the code so that anyone stumbling upon the same query might have a answer
#Provider
public class DateTimeParamConverterProvider implements ParamConverterProvider {
private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(DateTimeParamConverterProvider.class);
#Override
public <T> ParamConverter<T> getConverter(Class<T> type, Type genericType, Annotation[] annotations) {
if (type.equals(DateTime.class)) {
return (ParamConverter<T>) new DateTimeParamConverter();
} else {
return null;
}
}
private static class DateTimeParamConverter implements ParamConverter<DateTime> {
#Override
public DateTime fromString(String value) {
LOGGER.debug("The ISO Date that is provided is {}", value);
try {
return ISODateTimeFormat.dateTimeNoMillis().parseDateTime(value);
} catch (IllegalArgumentException e) {
return ISODateTimeFormat.dateTime().parseDateTime(value);
}
}
#Override
public String toString(DateTime value) {
return value.toString();
}
}
}

Related

Writable Classes in mapreduce

How can i use the values from hashset (the docid and offset) to the reduce writable so as to connect map writable with reduce writable?
The mapper (LineIndexMapper) works fine but in the reducer (LineIndexReducer) i get the error that it can't get string as argument when i type this:
context.write(key, new IndexRecordWritable("some string");
although i have the public String toString() in the ReduceWritable too.
I believe the hashset in reducer's writable (IndexRecordWritable.java) maybe isn't taking the values correctly?
I have the below code.
IndexMapRecordWritable.java
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
public class IndexMapRecordWritable implements Writable {
private LongWritable offset;
private Text docid;
public LongWritable getOffsetWritable() {
return offset;
}
public Text getDocidWritable() {
return docid;
}
public long getOffset() {
return offset.get();
}
public String getDocid() {
return docid.toString();
}
public IndexMapRecordWritable() {
this.offset = new LongWritable();
this.docid = new Text();
}
public IndexMapRecordWritable(long offset, String docid) {
this.offset = new LongWritable(offset);
this.docid = new Text(docid);
}
public IndexMapRecordWritable(IndexMapRecordWritable indexMapRecordWritable) {
this.offset = indexMapRecordWritable.getOffsetWritable();
this.docid = indexMapRecordWritable.getDocidWritable();
}
#Override
public String toString() {
StringBuilder output = new StringBuilder()
output.append(docid);
output.append(offset);
return output.toString();
}
#Override
public void write(DataOutput out) throws IOException {
}
#Override
public void readFields(DataInput in) throws IOException {
}
}
IndexRecordWritable.java
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashSet;
import org.apache.hadoop.io.Writable;
public class IndexRecordWritable implements Writable {
// Save each index record from maps
private HashSet<IndexMapRecordWritable> tokens = new HashSet<IndexMapRecordWritable>();
public IndexRecordWritable() {
}
public IndexRecordWritable(
Iterable<IndexMapRecordWritable> indexMapRecordWritables) {
}
#Override
public String toString() {
StringBuilder output = new StringBuilder();
return output.toString();
}
#Override
public void write(DataOutput out) throws IOException {
}
#Override
public void readFields(DataInput in) throws IOException {
}
}
Alright, here is my answer based on a few assumptions. The final output is a text file containing the key and the file names separated by a comma based on the information in the reducer class's comments on the pre-condition and post-condition.
In this case, you really don't need IndexRecordWritable class. You can simply write to your context using
context.write(key, new Text(valueBuilder.substring(0, valueBuilder.length() - 1)));
with the class declaration line as
public class LineIndexReducer extends Reducer<Text, IndexMapRecordWritable, Text, Text>
Don't forget to set the correct output class in the driver.
That must serve the purpose according to the post-condition in your reducer class. But, if you really want to write a Text-IndexRecordWritable pair to your context, there are two ways approach it -
with string as an argument (based on your attempt passing a string when you IndexRecordWritable class constructor is not designed to accept strings) and
with HashSet as an argument (based on the HashSet initialised in IndexRecordWritable class).
Since your constructor of IndexRecordWritable class is not designed to accept String as an input, you cannot pass a string. Hence the error you are getting that you can't use string as an argument. Ps: if you want your constructor to accept Strings, you must have another constructor in your IndexRecordWritable class as below:
// Save each index record from maps
private HashSet<IndexMapRecordWritable> tokens = new HashSet<IndexMapRecordWritable>();
// to save the string
private String value;
public IndexRecordWritable() {
}
public IndexRecordWritable(
HashSet<IndexMapRecordWritable> indexMapRecordWritables) {
/***/
}
// to accpet string
public IndexRecordWritable (String value) {
this.value = value;
}
but that won't be valid if you want to use the HashSet. So, approach #1 can't be used. You can't pass a string.
That leaves us with approach #2. Passing a HashSet as an argument since you want to make use of the HashSet. In this case, you must create a HashSet in your reducer before passing it as an argument to IndexRecordWritable in context.write.
To do this, your reducer must look like this.
#Override
protected void reduce(Text key, Iterable<IndexMapRecordWritable> values, Context context) throws IOException, InterruptedException {
//StringBuilder valueBuilder = new StringBuilder();
HashSet<IndexMapRecordWritable> set = new HashSet<>();
for (IndexMapRecordWritable val : values) {
set.add(val);
//valueBuilder.append(val);
//valueBuilder.append(",");
}
//write the key and the adjusted value (removing the last comma)
//context.write(key, new IndexRecordWritable(valueBuilder.substring(0, valueBuilder.length() - 1)));
context.write(key, new IndexRecordWritable(set));
//valueBuilder.setLength(0);
}
and your IndexRecordWritable.java must have this.
// Save each index record from maps
private HashSet<IndexMapRecordWritable> tokens = new HashSet<IndexMapRecordWritable>();
// to save the string
//private String value;
public IndexRecordWritable() {
}
public IndexRecordWritable(
HashSet<IndexMapRecordWritable> indexMapRecordWritables) {
/***/
tokens.addAll(indexMapRecordWritables);
}
Remember, this is not the requirement according to the description of your reducer where it says.
POST-CONDITION: emit the output a single key-value where all the file names are separated by a comma ",". <"marcello", "a.txt#3345,b.txt#344,c.txt#785">
If you still choose to emit (Text, IndexRecordWritable), remember to process the HashSet in IndexRecordWritable to get it in the desired format.

Quarkus GraalVM native image DateTimeFormatter and Localization

I tried to use localization in java to print date with local style.
I already make a similar functionality with numbers and currencies but I failed to apply the same behavior to date.
As I learning when I posted this topics few days ago, GraalVM Quarkus Locale in native mode , use localization in native mode need to use create an "#AutomaticFeature".
This trick works for numbers and currencies :
#AutomaticFeature
public class NativeNumberFormatFeature implements Feature {
#Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
ImageSingletons
.lookup(RuntimeClassInitializationSupport.class)
.initializeAtBuildTime(NumberFormatSupport.class, this.getClass().getName());
ImageSingletons.add(NumberFormatSupport.class, new NumberFormatSupport());
}
}
public class NumberFormatSupport {
private Map<Locale, NumberFormat> numberInstancesByLocale;
private Map<Locale, Map<String, NumberFormat>> currencyInstancesByLocale;
public NumberFormatSupport() {
numberInstancesByLocale = new HashMap<>();
currencyInstancesByLocale = new HashMap<>();
for (Locale locale : Locale.getAvailableLocales()) {
numberInstancesByLocale.put(locale, NumberFormat.getNumberInstance(locale));
currencyInstancesByLocale.put(locale, new HashMap<>());
for (Currency currency : Currency.getAvailableCurrencies()) {
currencyInstancesByLocale.get(locale).put(currency.getCurrencyCode(), NumberFormat.getCurrencyInstance(locale));
currencyInstancesByLocale.get(locale).get(currency.getCurrencyCode()).setCurrency(currency);
}
}
}
public NumberFormat getNumberFormat(Locale locale) {
return (NumberFormat) numberInstancesByLocale.get(locale).clone();
}
public NumberFormat getCurrencyFormat(Locale locale, Currency currency) {
return (NumberFormat) currencyInstancesByLocale.get(locale).get(currency.getCurrencyCode()).clone();
}
}
But it's not works with DateTimeFormatter :
#AutomaticFeature
public class NativeDateFormatterFeature implements Feature {
#Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
ImageSingletons
.lookup(RuntimeClassInitializationSupport.class)
.initializeAtBuildTime(DateFormatterSupport.class, this.getClass().getName());
ImageSingletons.add(DateFormatterSupport.class, new DateFormatterSupport());
}
}
public class DateFormatterSupport {
private Map<Locale, DateTimeFormatter> dateTimeFormatterByLocale;
public DateFormatterSupport() {
dateTimeFormatterByLocale = Arrays.stream(Locale.getAvailableLocales()).collect(Collectors.toMap(
Function.identity(),
l -> DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(l)));
}
public DateTimeFormatter get(Locale locale) {
return dateTimeFormatterByLocale.get(locale);
}
}
In development mode all is ok but in native mode, locale is ignore. Style is always le same, month name isn't translate.
I was looking for a solution on google but i didn't find anything so I ask on StackOverflow.
I am at your disposal if needed.
Regards,
Stéphane.
I don't understand why, but the following solution works :
public class DateFormatterSupport {
private Map<Locale, DateTimeFormatter> dateTimeFormatterByLocale;
public DateFormatterSupport() {
dateTimeFormatterByLocale = Arrays.stream(Locale.getAvailableLocales()).collect(Collectors.toMap(
Function.identity(),
l -> DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(l)));
Arrays.stream(Locale.getAvailableLocales()).forEach(locale -> {
dateTimeFormatterByLocale.get(locale).format(LocalDate.now());
});
}
public DateTimeFormatter get(Locale locale) {
return dateTimeFormatterByLocale.get(locale);
}
}

Javafx Datepicker validation

we tried to validate a javafx datepicker. So we use:
if (fromDatePicker.getValue() == null) {
sb.append("No valid from date!\n");
} else {
System.out.println(fromDatePicker.getValue().toString());
if (!DateUtil
.validEnglishDate(fromDatePicker.getValue().toString())) {
sb.append("No valid from date. Use the format yyyy-MM-dd.\n");
}
}
But at the moment it's impossible to get an invalid Date with the datepicker, because all invalid date's are changed to the start value.
So we asked us is it possible to get an invalid Date with the javafx datepicker?
***** EDIT *****
Example: we have the following datepicker:
DatePicker[2015-05-12]
now we entered "fjdfk" in the DatePicker so we have:
DatePicker[fjdfk]
on save the data's the datepicker changes automatical to DatePicker[2015-05-12]
You could use the DatePicker#setConverter(StringConverter<LocalDate>) to catch any parse exception and warn the user in consequence. Here is a sample :
public class SecureLocalDateStringConverter extends StringConverter<LocalDate> {
/**
* The date pattern that is used for conversion. Change as you wish.
*/
private static final String DATE_PATTERN = "dd/MM/yyyy";
/**
* The date formatter.
*/
public static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern(DATE_PATTERN);
private boolean hasParseError = false;
public boolean hasParseError(){
return hasParseError;
}
#Override
public String toString(LocalDate localDate) {
return DATE_FORMATTER.format(localDate);
}
#Override
public LocalDate fromString(String formattedString) {
try {
LocalDate date=LocalDate.from(DATE_FORMATTER.parse(formattedString));
hasParseError=false;
return date;
} catch (DateTimeParseException parseExc){
hasParseError=true;
return null;
}
}
}
From your control, you'll just have to call converter#hasParseError(), converter being the one you set with DatePicker#setConverter(StringConverter<LocalDate>)

SmartGwt 2.5 DataSourceDateTimeField i18n validation error

I currently use smartGwt (version 2.5) inside an other framework (Broadleaf).
When i set the locale to french :
Date fields are well formated (DD/MM/YYYY) and the calendar is translated in french but when i change the date and save the form a popup appear with the error message :"Doit ĂȘtre une date" (Must be a date in english). The validator expect a date with MM/DD/YYYY format.
Link to the class used by the framework to create the date field : https://github.com/BroadleafCommerce/BroadleafCommerce/blob/BroadleafCommerce-2.2.x/admin/broadleaf-open-admin-platform/src/main/java/org/broadleafcommerce/openadmin/client/datasource/dynamic/module/BasicClientEntityModule.java
I found a post with same problem (forums.smartclient.com/showthread.php?t=19847) but there is no answer.
Please, let me know how to solve this problem
EDIT :
What i have tried :
#Override
public void onModuleLoad() {
DateUtil.setShortDateDisplayFormat(DateDisplayFormat.TOEUROPEANSHORTDATE);
DateUtil.setShortDateDisplayFormatter(new DateDisplayFormatter() {
#Override
public String format(Date date) {
if(date == null)
{
return null;
}
else{
final DateTimeFormat dateFormatter = DateTimeFormat.getFormat("DD/MM/YYYY");
return dateFormatter.format(date);
}
}
});
Because the code below is not allowed :
DateUtil.setShortDateDisplayFormatter(DateUtil.TOEUROPEANSHORTDATE);
I put my code during the application initialization but the problem still present :-(
Screenshot : http://www.hostingpics.net/viewer.php?id=989088date.png
Do you have an other idea?
Set date formatter of the field to DateDisplayFormat.TOEUROPEANSHORTDATE.
dateItem.setDateFormatter(DateDisplayFormat.TOEUROPEANSHORTDATE);
Also check display seconds in DateTimeItem (SmartGWT) to find another way to set a custom formatted date string when dateItem.useTextField is true.
As the forum thread indicates, its also possible to set default date/datetime format for the application using methods of com.smartgwt.client.util.DateUtil, which should be used only once, during application initialization (e.g.- EntryPoint).
DateUtil.setShortDateDisplayFormatter(DateUtil.TOEUROPEANSHORTDATE);
Problem solved !
You have to use the code below during application initialization :
private static final String DATE_FORMAT = "dd/MM/yyyy HH:mm";
public void onModuleLoad() {
DateUtil.setShortDateDisplayFormatter(new DateDisplayFormatter() {
#Override
public String format(Date date) {
if(date != null)
{
final DateTimeFormat dateFormatter = DateTimeFormat.getFormat(DATE_FORMAT);
return dateFormatter.format(date);
}
return null;
}
});
DateUtil.setDateParser(new DateParser()
{
public Date parse(String dateString)
{
try{
if(dateString != null){
final DateTimeFormat format = DateTimeFormat.getFormat(DATE_FORMAT);
return format.parse(dateString);
}
}catch(Exception e){
e.printStackTrace();
}
return null;
}
});
}

SuggestBox override addSelectionHandler

I have a custom Oracle with Objects to pass to the SuggestBox. Then I need get back a object when it's selected from de SuggestBox.
public HandlerRegistration addSelectionHandler(SelectionHandler<SuggestOracle.Suggestion> handler)
The problem is that I don't have Suggestion. I have "CustomSuggestion". I read de API and I try to write a Custom SuggestBox implementing the interface HasSelectionHandlers but I can't because the SuggestBox have a implementation of the interface. I get the error:
The interface HasSelectionHandlers cannot be implemented more than once with different arguments: HasSelectionHandlers<SuggestOracle.Suggestion> and HasSelectionHandlers<CustomSuggestion>
Can you help me? Sorry for my bad english.
Not sure I understand your problem. Have a look at the following example (really basic but you should get an idea on how to deal with custom suggestions). Hope that helps:
public void onModuleLoad() {
SuggestBox box = new SuggestBox(new CustomOracle<CustomSuggestion>());
box.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
#Override
public void onSelection(SelectionEvent<Suggestion> event) {
String value = ((CustomSuggestion) event.getSelectedItem()).fSomeOtherValue;
Window.alert(value);
}
});
RootPanel.get().add(box);
}
private class CustomOracle<CustomSuggestion> extends SuggestOracle {
private LinkedList<Starter.CustomSuggestion> fStore;
public CustomOracle() {
fStore = new LinkedList<Starter.CustomSuggestion>();
fStore.add(new Starter.CustomSuggestion("2", "two", "foo"));
fStore.add(new Starter.CustomSuggestion("22", "twenty-two", "bar"));
fStore.add(new Starter.CustomSuggestion("222", "two-hundred twenty-two", "w000t"));
}
#Override
public void requestSuggestions(Request request, Callback callback) {
String query = request.getQuery();
LinkedList<Starter.CustomSuggestion> result = new LinkedList<Starter.CustomSuggestion>();
for (Starter.CustomSuggestion entry : fStore) {
if (entry.fDisplay.contains(query)) {
result.add(entry);
}
}
callback.onSuggestionsReady(request, new Response(result));
}
}
private class CustomSuggestion implements Suggestion {
private String fReplace;
private String fDisplay;
private String fSomeOtherValue;
public CustomSuggestion(String display, String replace, String someOtherValue) {
fDisplay = display;
fReplace = replace;
fSomeOtherValue = someOtherValue;
}
#Override
public String getDisplayString() {
return fDisplay;
}
#Override
public String getReplacementString() {
return fReplace;
}
}