Here's a simple value bean annotated with Spring's new (as of 3.0) convenience #DateTimeFormat annotation (which as I understand replaces the pre-3.0 need for custom PropertyEditors as per this SO question):
import java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;
public class Widget {
private String name;
#DateTimeFormat(pattern = "MM/dd/yyyy")
private LocalDate created;
// getters/setters excluded
}
When biding the values from a form submission to this widget, the date format works flawlessly. That is, only date strings in the MM/dd/yyyy format will convert successfully to actual LocalDate objects. Great, we're halfway there.
However, I would also like to be able to also display the created LocalDate property in a JSP view in the same MM/dd/yyyy format using JSP EL like so (assuming my spring controller added a widget attribute to the model):
${widget.created}
Unfortunately, this will only display the default toString format of LocalDate (in yyyy-MM-dd format). I understand that if I use spring's form tags the date displays as desired:
<form:form commandName="widget">
Widget created: <form:input path="created"/>
</form:form>
But I'd like to simply display the formatted date string without using the spring form tags. Or even JSTL's fmt:formatDate tag.
Coming from Struts2, the HttpServletRequest was wrapped in a StrutsRequestWrapper which enabled EL expressions like this to actually interrogate the OGNL value stack. So I'm wondering if spring provide something similar to this for allowing converters to execute?
EDIT
I also realize that when using spring's eval tag the date will display according the pattern defined in the #DateTimeFormat annotation:
<%# taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:eval expression="widget.created"/>
Interestingly, when using a custom PropertyEditor to format the date, this tag does NOT invoke that PropertyEditor's getAsText method and therefore defaults to the DateFormat.SHORT as described in the docs. In any event, I'd still like to know if there is a way to achieve the date formatting without having to use a tag--only using standard JSP EL.
You may use the tag to provide you these kind of formattings, such as money, data, time, and many others.
You may add on you JSP the reference:
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
And use the formatting as:
<fmt:formatDate pattern="yyyy-MM-dd" value="${now}" />
Follows below a reference:
http://www.tutorialspoint.com/jsp/jstl_format_formatdate_tag.htm
To precise Eduardo answer:
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:formatDate pattern="MM/dd/yyyy" value="${widget.created}" />
I also prefer to not do any formatting via tags. I realise this may not be the solution you are looking for and are looking for a way to do this via spring annotations. Nevertheless, In the past I've used the following work around:
Create a new getter with the following signature:
public String getCreatedDateDisplay
(You can alter the name of the getter if you prefer.)
Within the getter, format the created date attribute as desired using a formatter such as SimpleDateFormat.
Then you can call the following from your JSP
${widget.createDateDisplay}
I was dispirited to learn that spring developers have decided not to integrate Unified EL (the expression language used in JSP 2.1+) with Spring EL stating:
neither JSP nor JSF have a strong position in terms of our development focus anymore.
But taking inspiration from the JIRA ticket cited, I created a custom ELResolver which, if the resolved value is a java.time.LocalDate or java.time.LocalDateTime, will attempt to pull the #DateTimeFormat pattern value in order to format the returned String value.
Here's the ELResolver (along with the ServletContextListener used to bootstrap it):
public class DateTimeFormatAwareElResolver extends ELResolver implements ServletContextListener {
private final ThreadLocal<Boolean> isGetValueInProgress = new ThreadLocal<>();
#Override
public void contextInitialized(ServletContextEvent event) {
JspFactory.getDefaultFactory().getJspApplicationContext(event.getServletContext()).addELResolver(this);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
#Override
public Object getValue(ELContext context, Object base, Object property) {
try {
if (Boolean.TRUE.equals(isGetValueInProgress.get())) {
return null;
}
isGetValueInProgress.set(Boolean.TRUE);
Object value = context.getELResolver().getValue(context, base, property);
if (value != null && isFormattableDate(value)) {
String pattern = getDateTimeFormatPatternOrNull(base, property.toString());
if (pattern != null) {
return format(value, DateTimeFormatter.ofPattern(pattern));
}
}
return value;
}
finally {
isGetValueInProgress.remove();
}
}
private boolean isFormattableDate(Object value) {
return value instanceof LocalDate || value instanceof LocalDateTime;
}
private String format(Object localDateOrLocalDateTime, DateTimeFormatter formatter) {
if (localDateOrLocalDateTime instanceof LocalDate) {
return ((LocalDate)localDateOrLocalDateTime).format(formatter);
}
return ((LocalDateTime)localDateOrLocalDateTime).format(formatter);
}
private String getDateTimeFormatPatternOrNull(Object base, String property) {
DateTimeFormat dateTimeFormat = getDateTimeFormatAnnotation(base, property);
if (dateTimeFormat != null) {
return dateTimeFormat.pattern();
}
return null;
}
private DateTimeFormat getDateTimeFormatAnnotation(Object base, String property) {
DateTimeFormat dtf = getDateTimeFormatFieldAnnotation(base, property);
return dtf != null ? dtf : getDateTimeFormatMethodAnnotation(base, property);
}
private DateTimeFormat getDateTimeFormatFieldAnnotation(Object base, String property) {
try {
if (base != null && property != null) {
Field field = base.getClass().getDeclaredField(property);
return field.getAnnotation(DateTimeFormat.class);
}
}
catch (NoSuchFieldException | SecurityException ignore) {
}
return null;
}
private DateTimeFormat getDateTimeFormatMethodAnnotation(Object base, String property) {
try {
if (base != null && property != null) {
Method method = base.getClass().getMethod("get" + StringUtils.capitalize(property));
return method.getAnnotation(DateTimeFormat.class);
}
}
catch (NoSuchMethodException ignore) {
}
return null;
}
#Override
public Class<?> getType(ELContext context, Object base, Object property) {
return null;
}
#Override
public void setValue(ELContext context, Object base, Object property, Object value) {
}
#Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return true;
}
#Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
return null;
}
#Override
public Class<?> getCommonPropertyType(ELContext context, Object base) {
return null;
}
}
Register the ELResolver in web.xml:
<listener>
<listener-class>com.company.el.DateTimeFormatAwareElResolver</listener-class>
</listener>
And now when I have ${widget.created} in my jsp, the value displayed will be formatted according to the #DateTimeFormat annotation!
Additionally, if the LocalDate or LocalDateTime object is needed by the jsp (and not just the formatted String representation), you can still access the object itself using direct method invocation like: ${widget.getCreated()}
Related
I have a service defined as follows.
public String getData(#QueryParam("date") Date date)
I'm trying to pass a java.util.Date to it from my client (which is jaxrs:client of CXF, not a generic HTTP client or browser).
My service receives the date as Thu Mar 01 22:33:10 IST 2012 in the HTTP URL. Since CXF won't be able to create a Date object using this String, my client receives a 404 error.
I tried using a ParameterHandler on the service side, but I still can't parse it successfully because I'm not expecting the date in any specific format.
As per this post, passing a Date is supposed to work out of the box, but I can't seem to get the basic case working. Am I required to do anything in order to successfully pass a Date object from my client to service? Appreciate any help.
Thanks
The problem is that JAX-RS dictates that parameter unbundling be done in one of two ways:
The parameter bean has a public constructor that accepts a String
The parameter bean has a static valueOf(String) method.
In your case, the Date is being unbundled via its Date(String) constructor, which cannot handle the input format your client is sending. You have a couple options available to remedy this:
Option 1
Get your client to change the format of the date before they send it. This is the ideal, but probably the hardest to accomplish!
Option 2
Handle the crazy date format. The options for this are:
Change your method signature to accept a string. Attempt to construct a Date object out of that and if that fails, use your own custom SimpleDateFormat class to parse it.
static final DateFormat CRAZY_FORMAT = new SimpleDateFormat("");
public String getData(#QueryParam("date") String dateString) {
final Date date;
try {
date = new Date(dateString); // yes, I know this is a deprecated method
} catch(Exception e) {
date = CRAZY_FORMAT.parse(dateString);
}
}
Define your own parameter class that does the logic mentioned above. Give it a string constructor or static valueOf(String) method that invokes the logic. And an additional method to get the Date when all is said and done.
public class DateParameter implements Serializable {
public static DateParameter valueOf(String dateString) {
try {
date = new Date(dateString); // yes, I know this is a deprecated method
} catch(Exception e) {
date = CRAZY_FORMAT.parse(dateString);
}
}
private Date date;
// Constructor, Getters, Setters
}
public String getData(#QueryParam("date") DateParameter dateParam) {
final Date date = dateParam.getDate();
}
Or finally, you can register a parameter handler for dates. Where its logic is simply the same as mentioned for the other options above. Note that you need to be using at least CXF 2.5.3 in order to have your parameter handler evaluated before it tries the default unbundling logic.
public class DateHandler implements ParameterHandler<Date> {
public Map fromString(String s) {
final Date date;
try {
date = new Date(dateString); // yes, I know this is a deprecated method
} catch(Exception e) {
date = CRAZY_FORMAT.parse(dateString);
}
}
}
Percepiton's answer was very useful, but ParameterHandler has been deprecated in Apache-cxf 3.0, see the Apache-cxf 3.0 Migration Guide:
CXF JAX-RS ParameterHandler has been dropped, please use JAX-RS 2.0 ParamConverterProvider.
So I add an example with the ParamConverterProvider :
public class DateParameterConverterProvider implements ParamConverterProvider {
#Override
public <T> ParamConverter<T> getConverter(Class<T> type, Type type1, Annotation[] antns) {
if (Date.class.equals(type)) {
#SuppressWarnings("unchecked")
ParamConverter<T> paramConverter = (ParamConverter<T>) new DateParameterConverter();
return paramConverter;
}
return null;
}
}
public class DateParameterConverter implements ParamConverter<Date> {
public static final String format = "yyyy-MM-dd"; // set the format to whatever you need
#Override
public Date fromString(String string) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
try {
return simpleDateFormat.parse(string);
} catch (ParseException ex) {
throw new WebApplicationException(ex);
}
}
#Override
public String toString(Date t) {
return new SimpleDateFormat(format).format(t);
}
}
The #SuppressWarnings is required to suppress an "unchecked or unsafe operations" warning during compilation. See How do I address unchecked cast warnings for more details.
The ParamConverterProvider can be registred as provider. Here is how I did it:
<jaxrs:server id="myService" address="/rest">
<jaxrs:serviceBeans>
...
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="dateParameterConverterProvider" />
</jaxrs:providers>
</jaxrs:server>
<bean id="dateParameterConverterProvider" class="myPackage.DateParameterConverterProvider"/>
See Apache-cxf JAX-RS : Services Configuration for more information.
Using a custom DateParam class seems the safest option. You can then base your method signatures on that and implement the ugly conversion logic inside the valueOf() method or the class constructor. It is also more self-documenting than using plain strings
As #Perception suggests in option two, you can handle the date. But you should use following:
private Date getDateFromString(String dateString) {
try {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = df.parse(dateString);
return date;
} catch (ParseException e) {
//WebApplicationException ...("Date format should be yyyy-MM-dd'T'HH:mm:ss", Status.BAD_REQUEST);
}
}
You call it from within the resource as
Date date = getDateFromString(dateString);//dateString is query param.
How do you reference an constants with EL on a JSP page?
I have an interface Addresses with a constant named URL. I know I can reference it with a scriplet by going: <%=Addresses.URL%>, but how do I do this using EL?
EL 3.0 or newer
If you're already on Java EE 7 / EL 3.0, then the #page import will also import class constants in EL scope.
<%# page import="com.example.YourConstants" %>
This will under the covers be imported via ImportHandler#importClass() and be available as ${YourConstants.FOO}.
Note that all java.lang.* classes are already implicitly imported and available like so ${Boolean.TRUE} and ${Integer.MAX_VALUE}. This only requires a more recent Java EE 7 container server as early versions had bugs in this. E.g. GlassFish 4.0 and Tomcat 8.0.0-1x fails, but GlassFish 4.1+ and Tomcat 8.0.2x+ works. And you need to make absolutely sure that your web.xml is declared conform the latest servlet version supported by the server. Thus with a web.xml which is declared conform Servlet 2.5 or older, none of the Servlet 3.0+ features will work.
Also note that this facility is only available in JSP and not in Facelets. In case of JSF+Facelets, your best bet is using OmniFaces <o:importConstants> as below:
<o:importConstants type="com.example.YourConstants" />
Or adding an EL context listener which calls ImportHandler#importClass() as below:
#ManagedBean(eager=true)
#ApplicationScoped
public class Config {
#PostConstruct
public void init() {
FacesContext.getCurrentInstance().getApplication().addELContextListener(new ELContextListener() {
#Override
public void contextCreated(ELContextEvent event) {
event.getELContext().getImportHandler().importClass("com.example.YourConstants");
}
});
}
}
EL 2.2 or older
This is not possible in EL 2.2 and older. There are several alternatives:
Put them in a Map<String, Object> which you put in the application scope. In EL, map values are accessible the usual Javabean way by ${map.key} or ${map['key.with.dots']}.
Use <un:useConstants> of the Unstandard taglib (maven2 repo here):
<%# taglib uri="http://jakarta.apache.org/taglibs/unstandard-1.0" prefix="un" %>
<un:useConstants className="com.example.YourConstants" var="constants" />
This way they are accessible the usual Javabean way by ${constants.FOO}.
Use Javaranch's CCC <ccc:constantsMap> as desribed somewhere at the bottom of this article.
<%# taglib uri="http://bibeault.org/tld/ccc" prefix="ccc" %>
<ccc:constantsMap className="com.example.YourConstants" var="constants" />
This way they are accessible the usual Javabean way by ${constants.FOO} as well.
If you're using JSF2, then you could use <o:importConstants> of OmniFaces.
<html ... xmlns:o="http://omnifaces.org/ui">
<o:importConstants type="com.example.YourConstants" />
This way they are accessible the usual Javabean way by #{YourConstants.FOO} as well.
Create a wrapper class which returns them through Javabean-style getter methods.
Create a custom EL resolver which first scans the presence of a constant and if absent, then delegate to the default resolver, otherwise returns the constant value instead.
The following does not apply to EL in general, but instead to SpEL (Spring EL) only (tested with 3.2.2.RELEASE on Tomcat 7).
I think it is worth mentioning it here in case someone searches for JSP and EL (but uses JSP with Spring).
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<spring:eval var="constant" expression="T(com.example.Constants).CONSTANT"/>
You usually place these kinds of constants in a Configuration object (which has getters and setters) in the servlet context, and access them with ${applicationScope.config.url}
You can't. It follows the Java Bean convention. So you must have a getter for it.
I'm defining a constant in my jsp right at the beginning:
<%final String URI = "http://www.example.com/";%>
I include the core taglib in my JSP:
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
Then, I make the constant available to EL by following statement:
<c:set var="URI" value="<%=URI%>"></c:set>
Now, I can use it later. Here an example, where the value is just written as HTML comment for debugging purposes:
<!-- ${URI} -->
With your constant class, you can just import your class and assign the constants to local variables. I know that my answer is a sort of quick hack, but the question also bumps up when one wants to define constants directly in the JSP.
I implemented like:
public interface Constants{
Integer PAGE_SIZE = 20;
}
-
public class JspConstants extends HashMap<String, String> {
public JspConstants() {
Class c = Constants.class;
Field[] fields = c.getDeclaredFields();
for(Field field : fields) {
int modifier = field.getModifiers();
if(Modifier.isPublic(modifier) && Modifier.isStatic(modifier) && Modifier.isFinal(modifier)) {
try {
Object o = field.get(null);
put(field.getName(), o != null ? o.toString() : null);
} catch(IllegalAccessException ignored) {
}
}
}
}
#Override
public String get(Object key) {
String result = super.get(key);
if(StringUtils.isEmpty(result)) {
throw new IllegalArgumentException("Check key! The key is wrong, no such constant!");
}
return result;
}
}
Next step put instance of this class into servlerContext
public class ApplicationInitializer implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().setAttribute("Constants", new JspConstants());
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
add listener to web.xml
<listener>
<listener-class>com.example.ApplicationInitializer</listener-class>
</listener>
access in jsp
${Constants.PAGE_SIZE}
Static properties aren't accessible in EL. The workaround I use is to create a non-static variable which assigns itself to the static value.
public final static String MANAGER_ROLE = 'manager';
public String manager_role = MANAGER_ROLE;
I use lombok to generate the getter and setter so that's pretty well it. Your EL looks like this:
${bean.manager_role}
Full code at https://rogerkeays.com/access-java-static-methods-and-constants-from-el
Yes, you can. You need a custom tag (if you can't find it somewhere else). I've done this:
package something;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.taglibs.standard.tag.el.core.ExpressionUtil;
/**
* Get all class constants (statics) and place into Map so they can be accessed
* from EL.
* #author Tim.sabin
*/
public class ConstMapTag extends TagSupport {
public static final long serialVersionUID = 0x2ed23c0f306L;
private String path = "";
private String var = "";
public void setPath (String path) throws JspException {
this.path = (String)ExpressionUtil.evalNotNull ("constMap", "path",
path, String.class, this, pageContext);
}
public void setVar (String var) throws JspException {
this.var = (String)ExpressionUtil.evalNotNull ("constMap", "var",
var, String.class, this, pageContext);
}
public int doStartTag () throws JspException {
// Use Reflection to look up the desired field.
try {
Class<?> clazz = null;
try {
clazz = Class.forName (path);
} catch (ClassNotFoundException ex) {
throw new JspException ("Class " + path + " not found.");
}
Field [] flds = clazz.getDeclaredFields ();
// Go through all the fields, and put static ones in a Map.
Map<String, Object> constMap = new TreeMap<String, Object> ();
for (int i = 0; i < flds.length; i++) {
// Check to see if this is public static final. If not, it's not a constant.
int mods = flds [i].getModifiers ();
if (!Modifier.isFinal (mods) || !Modifier.isStatic (mods) ||
!Modifier.isPublic (mods)) {
continue;
}
Object val = null;
try {
val = flds [i].get (null); // null for static fields.
} catch (Exception ex) {
System.out.println ("Problem getting value of " + flds [i].getName ());
continue;
}
// flds [i].get () automatically wraps primitives.
// Place the constant into the Map.
constMap.put (flds [i].getName (), val);
}
// Export the Map as a Page variable.
pageContext.setAttribute (var, constMap);
} catch (Exception ex) {
if (!(ex instanceof JspException)) {
throw new JspException ("Could not process constants from class " + path);
} else {
throw (JspException)ex;
}
}
return SKIP_BODY;
}
}
and the tag is called:
<yourLib:constMap path="path.to.your.constantClass" var="consts" />
All public static final variables will be put into a Map indexed by their Java name, so if
public static final int MY_FIFTEEN = 15;
then the tag will wrap this in an Integer and you can reference it in a JSP:
<c:if test="${consts['MY_FIFTEEN'] eq 15}">
and you don't have to write getters!
You can. Try in follow way
#{T(com.example.Addresses).URL}
Tested on TomCat 7 and java6
Even knowing its a little late, and even knowing this is a little hack - i used the following solution to achieve the desired result. If you are a lover of Java-Naming-Conventions, my advice is to stop reading here...
Having a class like this, defining Constants, grouped by empty classes to create kind of a hierarchy:
public class PERMISSION{
public static class PAGE{
public static final Long SEE = 1L;
public static final Long EDIT = 2L;
public static final Long DELETE = 4L;
...
}
}
can be used from within java as PERMISSION.PAGE.SEE to retrieve the value 1L
To achieve a simliar access-possibility from within EL-Expressions, I did this:
(If there is a coding-god - he hopefully might forgive me :D )
#Named(value="PERMISSION")
public class PERMISSION{
public static class PAGE{
public static final Long SEE = 1L;
public static final Long EDIT = 2L;
public static final Long DELETE = 4L;
...
//EL Wrapper
public Long getSEE(){
return PAGE.SEE;
}
public Long getEDIT(){
return PAGE.EDIT;
}
public Long getDELETE(){
return PAGE.DELETE;
}
}
//EL-Wrapper
public PAGE getPAGE() {
return new PAGE();
}
}
finally, the EL-Expression to access the very same Long becomes: #{PERMISSION.PAGE.SEE} - equality for Java and EL-Access. I know this is out of any convention, but it works perfectly fine.
#Bozho already provided a great answer
You usually place these kinds of constants in a Configuration object (which has getters and setters) in the servlet context, and access them with ${applicationScope.config.url}
However, I feel an example is needed so it brings a bit more clarity and spare someone's time
#Component
public Configuration implements ServletContextAware {
private String addressURL = Addresses.URL;
// Declare other properties if you need as also add corresponding
// getters and setters
public String getAddressURL() {
return addressURL;
}
public void setServletContext(ServletContext servletContext) {
servletContext.setAttribute("config", this);
}
}
There is a workaround that is not exactly what you want, but lets you active almost the same with touching scriptlets in a quite minimal way. You can use scriptlet to put value into a JSTL variable and use clean JSTL code later in the page.
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# page import="com.whichever.namespace.Addresses" %>
<c:set var="ourUrl" value="<%=Addresses.URL%>"/>
<c:if test='${"http://www.google.com" eq ourUrl}'>
Google is our URL!
</c:if>
So, I have a property SQL.Date in my POJO class. I want to bind it using Binder from Vaadin Component, but always returned like this:
Property type 'java.sql.Date' doesn't match the field type 'java.time.LocalDate'. Binding should be configured manually using converter.
So here's my Getter Setter contained in the POJO class
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; }
And here's when I use the Binder component:
binder = new Binder<>(Person.class);
binder.bindInstanceFields( this );
FYI, I use Spring Boot JPA for the data. Is there any relation between the error message and usage of Spring Boot?
This
Property type 'java.sql.Date' doesn't match the field type
'java.time.LocalDate'. Binding should be configured manually using
converter.
tells what to do. Without seeing your code I assume you have Vaadin DateField in some Vaadin FormLayout that you are trying to populate with java.sql.Date value (or binder.bindInstanceFields() tries).
Unfortunately DateField seems to accept only LocalDate. Therefore you need to convert the value somehow.
There are lots of different "date" converters in vaadin Converter type hierarchy but this one is missing (or maybe I missed it?) so I created it:
public class SqlDateToLocalDateConverter
implements Converter<LocalDate,java.sql.Date> {
#Override
public Result<java.sql.Date> convertToModel(LocalDate value,
ValueContext context) {
if (value == null) {
return Result.ok(null);
}
return Result.ok( java.sql.Date.valueOf( value) );
}
#Override
public LocalDate convertToPresentation(java.sql.Date value,
ValueContext context) {
return value.toLocalDate();
}
}
It seems that you are using declarative ui? I am no able to tell it now howto port this with that with little effort.
If you were binding fields manually it would go like this:
binder.forField(myForm.getMyDateField())
.withConverter(new SqlDateToLocalDateConverter())
.bind(MyBean::getSqlDate, MyBean::setSqlDate);
So i guess you need to find a way to add this converter to handle assumed DateField. Anyway message suggests that you might not be able to use the easy way binder.bindInstanceFields() but bind fields manually.
You can create a custom date field and use it instead of DateField. Then you don't need to bind it every time just use automated binding
public class CustomDateField extends CustomField<Date> {
DateField dateField;
public CustomDateField(String caption) {
setCaption(caption);
dateField = new DateField();
dateField.setDateFormat("dd/MM/yy");
}
#Override
protected Component initContent() {
return dateField;
}
#Override
protected void doSetValue(Date date) {
dateField.setValue(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
}
#Override
public Date getValue() {
return dateField.getValue() != null ? Date.from(dateField.getValue().atStartOfDay(ZoneId.systemDefault()).toInstant()) : null;
}
}
I'm consuming a REST webservice and directly using the JAXB objects in my view. One has a date as a XMLGregorianCalendar like this:
#XmlAttribute(name = "record")
#XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar record;
While trying to use a standard converter
<h:outputText value="#{bean.value.record}" >
<f:convertDateTime pattern="dd.MM.yy" />
</h:outputText>
I get the error message (translated into english) in my JSF2 environment (JBoss-7.1.1-Final)
javax.faces.convert.ConverterException: fSelection:dtSelection:0:j_idt42:
Converting of '2012-07-25T20:15:00' into string not possible.
It seems, the type XMLGregorianCalendar is not supported by the default converter. I'm wondering if a JSF converter for this date type is available, because this requirement does not seem to be that unusual ...
Edit Ravi provided a functional example of a custom converter, but this seems to be to unflexible:
the pattern is hardcoded
no support for the user local
The value should be of type java.util.Date.
So get the Date object from the XMLGregorianCalendar like this:
record.toGregorianCalendar().getTime();
UPDATE:
You can use like this:
<h:outputText value="#{bean.value.record.toGregorianCalendar().time}" >
<f:convertDateTime pattern="dd.MM.yy" />
</h:outputText>
This should actually work but since you said you are getting an IllegalAccessException, I am not sure for the exact reason.
Alternatively, you can also write your own converter if you would like to, the converter will look like this:
And if you want to use the same attributes that you would use with a dateTimeConverter, then you need to pass them as attributes to the component and extend DateTimeConverter like this:
#FacesConverter("com.examples.Date")
public class XMLGregorianCalConverter extends DateTimeConverter {
private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("GMT");
private String dateStyle = "default";
private Locale locale = null;
private String pattern = null;
private String timeStyle = "default";
private TimeZone timeZone = DEFAULT_TIME_ZONE;
private String type = "date";
#Override
public Object getAsObject(FacesContext context, UIComponent component, String newValue) {
// TODO Auto-generated method stub
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
Map<String, Object> attributes = component.getAttributes();
if(attributes.containsKey("pattern")){
pattern = (String) attributes.get("pattern");
}
setPattern(pattern);
if(attributes.containsKey("locale")){
locale = (Locale) attributes.get("locale");
}
setLocale(locale);
if(attributes.containsKey("timeZone")){
timeZone = (TimeZone) attributes.get("timeZone");
}
setTimeZone(timeZone);
if(attributes.containsKey("dateStyle")){
dateStyle = (String) attributes.get("dateStyle");
}
setDateStyle(dateStyle);
if(attributes.containsKey("timeStyle")){
timeStyle = (String) attributes.get("timeStyle");
}
setTimeStyle(timeStyle);
if(attributes.containsKey("type")){
type = (String) attributes.get("type");
}
setType(type);
XMLGregorianCalendar xmlGregCal = (XMLGregorianCalendar) value;
Date date = xmlGregCal.toGregorianCalendar().getTime();
return super.getAsString(context, component, date);
}
}
and use on your page like this:
<h:outputText value="#{bean.value.record}" >
<f:converter converterId="com.examples.Date" />
<f:attribute name="pattern" value="dd.MM.yy" />
</h:outputText>
Code inspired/copied from this question: JSF convertDateTime with timezone in datatable
You can register a custom XML Adapter to convert from XMLGregorianCalendar to either Calendar or Date along these lines: How do I customise date/time bindings using JAXWS and APT?
I have problem on processing input request parameter (of course it's type String) to java.util.Date. I thought that following code added to my bean might solve this problem, but I was wrong:
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public void setDate(String dateString) {
try {
date = DateFormat.getDateInstance().parse(dateString);
} catch (ParseException e) {
date = new Date();
}
}
It throws an exception after submiting form:
javax.servlet.ServletException: BeanUtils.populate
org.apache.struts.util.RequestUtils.populate(RequestUtils.java:469)
org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:818)
java.lang.IllegalArgumentException: Cannot invoke com.epam.testapp.model.News.setDate - argument type mismatch
org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:1778)
org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:1759)
org.apache.commons.beanutils.PropertyUtilsBean.setNestedProperty(PropertyUtilsBean.java:1648)
org.apache.commons.beanutils.PropertyUtilsBean.setProperty(PropertyUtilsBean.java:1677)
Is this fundamental of struts 1 form that this code won't work because of mismatch of returning getter and accepting setter parameter types? How can I solve this problem ? I don't want at all to make method named like setStringDate(String stringDate(){...} and think on every page which method should I call :(
Date object cannot be a property in struts as date format can vary (depending on specification). Some may have dd-MM-yyyy, dd-MMMM-yy, etc.
I would suggest having a property:
private String date;
public String getDate() { return date; }
public void setDate(String date) { this.date = date; }
And in your action, convert the date string into Date object.
As per my knowledge I think , overloaded methods don't work very well in form beans .Try naming the two methods differently, and I think you'll have better luck.