WPS process cleanup error - date paring error

Description

After successful execution of WPS processes the Geoserver Process status log in the UI fill up. No entries are ever deleted due to a date parsing error.

errors from geoserver log:

2017-02-21 11:33:01,253 DEBUG [geoserver.wps] - Removing statuses matching [[[ NOT [ completionTime IS NULL ] ] AND org.geotools.filter.temporal.BeforeImpl@1139a6ba] AND [[ NOT [ lastUpdated IS NULL ] ] AND org.geotools.filter.temporal.BeforeImpl@c3551a6]]
2017-02-21 11:33:01,253 DEBUG [geotools.util] - InterpolationConverterFactory can be applied from Strings to Interpolation only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - CRSConverterFactory can be applied from Strings to CRS only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - InterpolationConverterFactory can be applied from Strings to Interpolation only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - CRSConverterFactory can be applied from Strings to CRS only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - InterpolationConverterFactory can be applied from Strings to Interpolation only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - CRSConverterFactory can be applied from Strings to CRS only.
2017-02-21 11:33:01,253 TRACE [geotools.util] - Error applying the converter class org.geotools.xml.XmlConverterFactory$XmlConverter on (Tue Feb 21 11:23:03 CET 2017,class java.util.Date)
java.lang.IllegalArgumentException: Failed to parse time Tue Feb 21 11:23:03 CET 2017 at:Tue Feb 21 11:23:03 CET 2017
at org.geotools.xml.impl.DatatypeConverterImpl.parseTime(DatatypeConverterImpl.java:189)
at org.geotools.xml.XmlConverterFactory$XmlConverter.convertFromString(XmlConverterFactory.java:123)
at org.geotools.xml.XmlConverterFactory$XmlConverter.convert(XmlConverterFactory.java:76)
at org.geotools.util.Converters.convert(Converters.java:168)
at org.geotools.util.Converters.convert(Converters.java:129)
at org.geotools.temporal.TemporalConverterFactory$2.convert(TemporalConverterFactory.java:51)
at org.geotools.util.Converters.convert(Converters.java:168)
at org.geotools.util.Converters.convert(Converters.java:129)
at org.geoserver.wps.property.BeanPropertyAccessor.get(BeanPropertyAccessor.java:37)
at org.geotools.filter.AttributeExpressionImpl.tryAccessor(AttributeExpressionImpl.java:248)
at org.geotools.filter.AttributeExpressionImpl.evaluate(AttributeExpressionImpl.java:208)
at org.geotools.filter.temporal.BinaryTemporalOperatorImpl.toInstant(BinaryTemporalOperatorImpl.java:64)
at org.geotools.filter.temporal.BinaryTemporalOperatorImpl.toTemporal(BinaryTemporalOperatorImpl.java:77)
at org.geotools.filter.temporal.BinaryTemporalOperatorImpl.evaluate(BinaryTemporalOperatorImpl.java:52)
at org.geotools.filter.AndImpl.evaluate(AndImpl.java:44)
at org.geotools.filter.AndImpl.evaluate(AndImpl.java:44)
at org.geoserver.wps.MemoryProcessStatusStore.remove(MemoryProcessStatusStore.java:74)
at org.geoserver.wps.executor.ProcessStatusTracker.cleanExpiredStatuses(ProcessStatusTracker.java:135)
at org.geoserver.wps.WPSStorageCleaner.run(WPSStorageCleaner.java:50)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
2017-02-21 11:33:01,253 DEBUG [geotools.util] - InterpolationConverterFactory can be applied from Strings to Interpolation only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - CRSConverterFactory can be applied from Strings to CRS only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - InterpolationConverterFactory can be applied from Strings to Interpolation only.
2017-02-21 11:33:01,253 DEBUG [geotools.util] - CRSConverterFactory can be applied from Strings to CRS only.

I wonder if this ever worked with java 8, but maybe I'm missing something. Below are some research from the code base:

According to the API doc for java 8 for java.util.Date

public String toString()

Converts this Date object to a String of the form:

dow mon dd hh:mm:ss zzz yyyy

This comment from the source below clearly states that a date string on the form:

dow mon dd hh:mm:ss zzz yyyy

would never be parsed correctly by this method

geotools/modules/extension/xsd/xsd-core/src/main/java/org/geotools/xml/impl/DatatypeConverterImpl.java

/** <p>Parses the lexical representation of the given dateTime value

  • and converts it into an instance of {@link java.util.Calendar}.

  • Valid lexical representations of a dateTime value include

  • <pre>

  • YYYY-MM-DDThh:mm:ss

  • YYYY-MM-DDThh:mm:ss.sss

  • YYYY-MM-DDThh:mm:ssZ

  • YYYY-MM-DDThh:mm:ss-01:00

  • </pre>

  • The former examples are all specified in UTC time. The last example

  • uses a negative offset of one hour to UTC.</p>

  • @param arg0 The input string being parsed.

  • @param lenient parameter used for allowing lenient parsing

  • @return The input string converted into an instance of

  • {@link java.util.Calendar}.

  • @see javax.xml.bind.ParseConversionEvent
    */
    public Calendar parseDateTime(String arg0, boolean lenient) {
    XsDateTimeFormat format = new XsDateTimeFormat();
    ParsePosition pos = new ParsePosition(0);
    Calendar cal = (Calendar) format.parseObject(arg0, pos, lenient);
    if (cal == null) {
    throw new IllegalArgumentException("Failed to parse dateTime " + arg0 +
    " at:" + arg0.substring(pos.getErrorIndex()));
    }
    return cal;
    }

If we follow the stacktrace a bit furhter there seems to be an explicit to String cast in the XmlConverter.convert method.

geotools/modules/extension/xsd/xsd-core/src/main/java/org/geotools/xml/XmlConverterFactory.java

public class XmlConverterFactory implements ConverterFactory {

public Converter createConverter(Class source, Class target, Hints hints) {
// make sure either source or target is String in order not to step over
// TemporalConverterFactory
if (String.class.equals(target)) {
if (java.util.Date.class.isAssignableFrom(source)

Calendar.class.isAssignableFrom(source)) {
return new XmlConverter();
}
} else if (String.class.equals(source)) {
if (java.util.Date.class.isAssignableFrom(target)

Calendar.class.isAssignableFrom(target)) {
return new XmlConverter();
}
}
return null;
}

static class XmlConverter implements Converter {
public Object convert(Object source, Class target)
throws Exception {
if (String.class.equals(target)) {
return convertToString(source);
}
return convertFromString((String) source, target);
}

private Object convertFromString(final String source, final Class<?> target) {

// don't bother performing conversions if the target types are not dates/times
if(!Calendar.class.equals(target) && !Date.class.isAssignableFrom(target))
return null;

//JD: this is a bit of a hack but delegate to the
// commons converter in case we are executing first.
try {
Converter converter = new CommonsConverterFactory().createConverter(String.class,
target, null);

if (converter != null) {
Object converted = null;

try {
converted = converter.convert(source, target);
} catch (Exception e) {
//ignore
}

if (converted != null) {
return converted;
}
}
}
catch(Exception e) {
//fall through to jaxb parsing
}

Calendar date;

//try parsing as dateTime
try {
try {
date = DatatypeConverterImpl.getInstance().parseDateTime(source);
}
catch(Exception e) {
//try as just date
date = DatatypeConverterImpl.getInstance().parseDate(source);
}

} catch (Exception e) {
//try as just time
date = DatatypeConverterImpl.getInstance().parseTime(source);
}

if (Calendar.class.equals(target)) {
return date;
}

if (Date.class.isAssignableFrom(target)) {
Date time = date.getTime();

//check for subclasses
if (java.sql.Date.class.equals(target)) {
return new java.sql.Date(time.getTime());
}

if (Time.class.equals(target)) {
return new Time(time.getTime());
}

if (Timestamp.class.equals(target)) {
return new Timestamp(time.getTime());
}

return time;
}

return null;
}

private String convertToString(Object unconvertedValue) {
String textValue = null;

if (unconvertedValue instanceof Calendar) {

Calendar cal = (Calendar) unconvertedValue;
textValue = DatatypeConverterImpl.getInstance().printDateTime(cal);

} else if(unconvertedValue instanceof java.sql.Date){
DatatypeConverterImpl converter = DatatypeConverterImpl.getInstance();
Object hint = Hints.getSystemDefault(Hints.LOCAL_DATE_TIME_HANDLING);
Calendar cal;
if(Boolean.TRUE.equals(hint)){
cal = Calendar.getInstance();
}else{
cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
}
cal.setTimeInMillis(((java.util.Date) unconvertedValue).getTime());
textValue = converter.printDate(cal);
} else if (unconvertedValue instanceof java.util.Date) {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(((java.util.Date) unconvertedValue).getTime());
DatatypeConverterImpl converter = DatatypeConverterImpl.getInstance();

if (unconvertedValue instanceof java.sql.Time) {
textValue = converter.printTime(cal);
} else {
// java.util.Date and java.sql.TimeStamp
textValue = converter.printDateTime(cal);
}
}

return textValue;
}
}

}

geotools/modules/library/main/src/main/java/org/geotools/temporal/TemporalConverterFactory.java

public class TemporalConverterFactory implements ConverterFactory {

static Converter dateToInstant = new Converter() {
public <T> T convert(Object source, Class<T> target) throws Exception {
return (T) new DefaultInstant(new DefaultPosition((Date)source));
}
};

static Converter stringToInstant = new Converter() {

public <T> T convert(Object source, Class<T> target) throws Exception {
//first go to java.util.Date
Date d = Converters.convert(source, Date.class);

//then go from date to instant
return d != null ? dateToInstant.convert(d, target) : null;
}

};

public Converter createConverter(Class<?> source, Class<?> target, Hints hints) {
if (Instant.class.isAssignableFrom(target)) {
if (Date.class.isAssignableFrom(source)) {
return dateToInstant;
}

if (String.class.equals(source)) {
return stringToInstant;
}
}

return null;
}
}

geoserver/src/extension/wps/wps-core/src/main/java/org/geoserver/wps/executor/ProcessStatusTracker.java

public void cleanExpiredStatuses(long expirationThreshold) {
Date date = new Date(expirationThreshold);
Not completionTimenotNull = FF.not(FF.isNull(FF.property("completionTime")));
Filter completionTimeExpired = FF.before(FF.property("completionTime"), FF.literal(date));
Filter completionTimeFilter = FF.and(completionTimenotNull, completionTimeExpired);
Not lastUpdatedNotNull = FF.not(FF.isNull(FF.property("lastUpdated")));
Filter lastUpdatedExpired = FF.before(FF.property("lastUpdated"), FF.literal(date));
Filter lastUpdatedFilter = FF.and(lastUpdatedNotNull, lastUpdatedExpired);
And filter = FF.and(completionTimeFilter, lastUpdatedFilter);
store.remove(filter);
}

geotools/modules/library/main/src/main/java/org/geotools/filter/FilterFactoryImpl.java

public Literal literal(Object obj) {
try {
return new LiteralExpressionImpl(obj);
}
catch (IllegalFilterException e) {
new IllegalArgumentException().initCause(e);
}

return null;
}

geotools/modules/library/main/src/main/java/org/geotools/filter/LiteralExpressionImpl.java

public LiteralExpressionImpl(Object literal)
throws IllegalFilterException {
this.setValue(literal);
}

public final void setValue(Object literal) {
this.literal = literal;
}

public Object getValue() {
return literal;
}

public String toString() {
return literal == null ? "NULL" : literal.toString();
}

Environment

OS Name Microsoft Windows Server 2008 R2 Standard
Version 6.1.7600 Build 7600

Java 1.8.0_73 x64
Tomcat 7.0.75 x64

geoserver-2.9.3
geoserver-2.9.3-wps-plugin

Bygginformation
Version
2.9.3
Git revision
0d588545aeb7550c04ce74ea8dbb932e3512f0dd
Byggdatum
24-Nov-2016 20:02
GeoTools-version
15.3 (rev 2db372589d85dfdc256a292f7cc11e8f81cdcdf2)
GeoWebCache-version
1.9.2 (rev 1.9.x/b259119a71b04329bd939d0ec791de997c5b0dbf)

Assignee

Unassigned

Reporter

Bjorn Nilsson

Triage

Fix versions

None

Affects versions

Components

Priority

Medium
Configure