I implemented a converter to convert String to UUID and include length validation.
public class StringToUUIDConverter implements Converter<String, UUID> {
public UUID convert(String source){
if(source.length>36){
throw new RuntimeException();
}else{
return ...
}
}
}
At first, I thought I would get a RuntimeException when I passed in a string of length 37. However, I tested it and found that no exception was thrown.
Code snippet of TypeConverterDelegate.convertIfNecessary()
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
ConversionUtils convert all exception to ConversionFailedException :
abstract class ConversionUtils {
#Nullable
public static Object invokeConverter(GenericConverter converter, #Nullable Object source,
TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Throwable ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
}
TypeConverterDelegate catches the exception I throw and follows another set of logic to convert String to UUID. Although I've done what I want with InitBinder, what should I do if I really want to throw an exception here?
Related
To make the mapping between hibernate and my database work, I have this mapping :
<property name="userRolesV2" column="user_roles_v2">
<type name="io.markethero.repository.CommaSeparatedGenericEnumType">
<param name="enumClassName">io.markethero.model.UserLoginRoleV2</param>
<param name="collectionClassName">java.util.Set</param>
</type>
</property>
The idea is to directly get the Collection in my field class instead of doing the mapping each time.
For example, the field in the class could be a Set, a List, or a Queue.
In the database, the value is like "enumValue1,enumValue2,enumValue3".
To do that, my class CommaSeparatedGenericEnumType is like this:
public class CommaSeparatedGenericEnumType implements UserType, ParameterizedType {
private Class enumClass = null;
private Class targetCollection = null;
public void setParameterValues(Properties params) {
String enumClassName = params.getProperty("enumClassName");
String collectionClassName = params.getProperty("collectionClassName");
if (enumClassName == null) {
throw new MappingException("enumClassName parameter not specified");
}
if (collectionClassName == null) {
throw new MappingException("collectionClassName parameter not specified");
}
try {
this.enumClass = Class.forName(enumClassName);
} catch (ClassNotFoundException e) {
throw new MappingException("enumClass " + enumClassName + " not found", e);
}
try {
this.targetCollection = Class.forName(collectionClassName);
} catch (ClassNotFoundException e) {
throw new MappingException("targetCollection " + collectionClassName + " not found", e);
}
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
String commaSeparatedValues = rs.getString(names[0]);
List<Object> result = new ArrayList<>();
if (!rs.wasNull()) {
String[] enums = commaSeparatedValues.split(",");
for (String string : enums) {
result.add(Enum.valueOf(enumClass, string));
}
}
return result;
}
#Override
#SuppressWarnings("unchecked")
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (null == value) {
st.setNull(index, Types.VARCHAR);
} else {
List<Object> enums = (List) value;
StringBuilder sb = new StringBuilder("");
for (Object each : enums) {
sb.append(each.toString()).append(",");
}
if (sb.toString().isEmpty()) {
st.setNull(index, Types.VARCHAR);
} else {
String commaSeparatedIds = sb.toString().substring(0, sb.toString().length() - 1);
st.setString(index, commaSeparatedIds);
}
}
}
}
I would like to be able to parametrize which collection nullSafeGet and nullSafeSet are going to use, because for now, it's only working with a list.
Thank you!
Maybe my question was asked was oddly, but here what I did:
For the getter, I used the factory pattern already implemented by Spring : CollectionFactory.
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
String commaSeparatedValues = rs.getString(names[0]);
Collection<Object> result = CollectionFactory.createCollection(this.targetCollection, 50);
if (!rs.wasNull()) {
String[] enums = commaSeparatedValues.split(",");
for (String string : enums) {
try {
result.add(Enum.valueOf(enumClass, string));
} catch (IllegalArgumentException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeGet] No such enum value"+ string +"for enum : " + enumClass, e);
}
}
}
return result;
}
For the setter, I used the reflection API.
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
} else {
if (value instanceof Collection) {
try {
StringBuilder sb = new StringBuilder("");
Constructor<?> c = value.getClass().getConstructor(Collection.class);
Collection<Object> enums = (Collection<Object>) c.newInstance((Collection<Object>) value);
for (Object each : enums) {
sb.append(each.toString()).append(",");
}
String commaSeparatedIds = sb.substring(0, sb.toString().length() - 1);
if (sb.length() > 0) {
st.setString(index, commaSeparatedIds);
} else {
st.setNull(index, Types.VARCHAR);
}
} catch (NoSuchMethodException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] No such constructor found for class : " + value.getClass(), e);
} catch (InstantiationException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] Class : " + value.getClass() + " cannot be instantiate ", e);
} catch (IllegalAccessException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] You cannot access to constructor of class : " + value.getClass(), e);
} catch (InvocationTargetException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] InvocationTargetException for class : " + value.getClass(), e);
}
} else {
st.setNull(index, Types.VARCHAR);
throw new IllegalArgumentException();
}
}
}
We have a spring boot Application which makes RESTFul calls to a bunch of backends, one of them returns null reponses at times, and we are observing the connections are not released during these instances because of this code in RestTemplate class:
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
else {
return null;
}
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
Is there a way we can release the connection or consume the contents for when response is null or erring out?
Edited to add code causing errors:
MyHttpClientClass{
private X getResponseBody(RestClient client, URI uri, HttpMethod method, HttpEntity<T> entity, Class<R> responseType, MyErrorHandler errorHandler) {
try
{ String host = this.getHost();
ResponseEntity<X> resp = client.exchange(uri, method, entity, responseType);
return resp.getBody();
} catch (HttpServerErrorException | HttpClientErrorException e)
{ handleHttpException(e, errorHandler);
throw e;
} catch (Exception e) {
log(e);
throw e; } } }
-----------
Class1 implements Callable<T>
{
#Override public T doCall() throws Exception {
try
{ return this.getRestClient().exchange(this.getUri(),
this.getHttpMethod(), this.getEntity(), getResponseType()).getBody(); } catch (HttpClientErrorException ex) { throw ex; } catch (HttpStatusCodeException ex) { if(this.isNeededRetry(ex)) { throw ex; }else { return generateErrorResponse(ex).getBody(); } } catch (RestClientException ex) { throw ex; } catch (Exception ex) { throw ex; } } }
----------
MySpringApplicationClass{
public X get(String api, String params, Class<R> responseType, String path, List<String> pathVariables, MyErrorHandler errorHandler)
{
return getResponseBody(...);
}}
When the date format is not correct (for example when I manually post 13,02,2018 instead of 13.02.2018 and also other incorrect dates such as 13.02.999) the app crashes. How can I fix it? (the manual input is important, i can`t just disable it).
XHTML:
<rich:calendar enableManualInput="true" datePattern="dd.MM.yyyy"
value="#{myBean.data.myDate}">
<f:converter converterId="mydate"/>
</rich:calendar>
Converter:
#FacesConverter("mydate")
public class LocalDateConverter implements Converter {
private static final DateTimeFormatter formatter;
static {
formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
formatter.withLocale(new Locale("ru"));
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return LocalDate.parse(value, formatter);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
} else if (value instanceof LocalDate) {
return ((LocalDate) value).format(formatter);
} else if (value instanceof LocalDateTime) {
return ((LocalDateTime) value).format(formatter);
} else {
throw new IllegalArgumentException("Value is not java.time.LocaleDate");
}
}
Converters should throw a ConverterException which can contain a FacesMessage. This message can be displayed on your XHTML page, near the input component that caused the exception using <h:message for="inputComponentId"/>.
The problem occurs in your getAsObject method. There you should catch the DateTimeParseException exception and throw a ConverterException:
try {
return LocalDate.parse(value, formatter);
}
catch (DateTimeParseException ex) {
throw new ConverterException(new FacesMessage("Invalid date: " + value));
}
See also:
https://docs.oracle.com/javaee/7/tutorial/jsf-custom010.htm
How to use java.time.ZonedDateTime / LocalDateTime in p:calendar
You don't need converter at all. Simply include label attribute in rich:calendar component and let system figure out if value is correct. Example:
<h:outputLabel for="programStartDate" value="#{msg.programStartDate}" />
<rich:calendar id="programStartDate" value="#{program.programStartDate}"
label="#{msg.programStartDate}" inputStyle="width: 100px;"
datePattern="#{referenceData.defaultDatePattern}"
timeZone="#{referenceData.timezone}"
enableManualInput="true" popup="true" required="true" />
use a try catch and catch the exception so it doesn't crash but continue without allowing the exception to crash your program
#FacesConverter("mydate")
public class LocalDateConverter implements Converter
{
private static final DateTimeFormatter formatter;
static {
formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
formatter.withLocale(new Locale("ru"));
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
return LocalDate.parse(value, formatter);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
try
{
if (value == null)
{
return "";
}
else if (value instanceof LocalDate) {
return ((LocalDate)value).format(formatter);
} else if (value instanceof LocalDateTime) {
return ((LocalDateTime)value).format(formatter);
} else {
throw new IllegalArgumentException("Value is not java.time.LocaleDate");
}
}
catch (Exception)
{
return "SOME DEFAULT DATE";
}
}
}
How can i get list of tiket with its categories through Jpa Specification
Example model:
#Entity
#Table(name = "tickets")
public class Ticket {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private Category
}
Method of service:
public Page<Ticket> findAll(Pageable pageable) {
return ticketRepository.findAll((root, query, cb) -> {
root.join("category");
return query.getRestriction();
}, pageable);
}
I was able to eager load the collection by using a fetch instead of a join.
public Page<Ticket> findAll(Pageable pageable) {
return ticketRepository.findAll((root, query, cb) -> {
root.fetch("category");
return query.getRestriction();
}, pageable);
}
The fetch method will use the default join type (inner). If want to load tickets with no category, you'll have to pass JoinType.LEFT as the second parameter.
as they say, stackoverflow giveth, stackoverflow taketh..
I took this class off SO quite some time ago, feel free to recycle it..
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Hibernate;
public class BeanLoader {
/**
* StackOverflow safe, if called before json creation, cyclic object must be avoided
*/
public static void eagerize(Object obj) {
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp, new ArrayList<String>());
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item, new ArrayList<String>());
}
}
}
}
}
/**
* StackOverflows if passed a bean containing cyclic fields. Call only if sure that this won't happen!
*/
public static void eagerizeUnsafe(Object obj) {
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp);
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item);
}
}
}
}
}
private static void eagerize(Object obj, ArrayList<String> visitedBeans) {
if (!visitedBeans.contains(obj.getClass().getName())){
visitedBeans.add(obj.getClass().getName());
} else {
return;
}
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp, visitedBeans);
}
if (origProp instanceof User){
((User) origProp).setRelatedNra(null);
User u=(User) origProp;
if (u.getRelatedMps()!=null)
u.getRelatedMps().clear();
if (u.getRelatedDps()!=null)
u.getRelatedDps().clear();
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item, (ArrayList<String>) visitedBeans.clone());
}
}
}
}
}
}
modify it as you see fit.. the method you'll call is "eagerizeUnsafe".
YMMV, but this should to the trick to eagerize all the collections of a lazily initialized bean.
I'm trying to get the fallback solution working on mobile but having some issues.
I've the following jsp structure
views
-mobile
--about.jsp
-tablet
--about.jsp
--intermediary.jsp
about.jsp
intermediary.jsp
Currently I don't have a intermediary.jsp in the mobile but I have it in the fallback directory. The resolveViewName within the AbstractDeviceDelegatingViewResolver only fires if the view is null.
public View resolveViewName(String viewName, Locale locale) throws Exception {
String deviceViewName = getDeviceViewName(viewName);
View view = delegate.resolveViewName(deviceViewName, locale);
if (enableFallback && view == null) {
view = delegate.resolveViewName(viewName, locale);
}
if (logger.isDebugEnabled() && view != null) {
logger.deb
The problem I'm having is I can't find any viewResolver to return null. The InternalResourceViewResolver doesn't return null and the UrlBasedViewResolver always returns the view name of /mobile/intermediary.jsp which doesn't exist which in turn throws a 404. Anyone know which resolver I should be using for the fallback solution to work?
Thanks,
look at the source you will find the answer:
public abstract class AbstractUrlBasedView{
...
public boolean checkResource(Locale locale) throws Exception {
return true;
}
...
}
public class InternalResourceView extends AbstractUrlBasedView {
//!!not override the checkResource method!!
}
public class FreeMarkerView extends AbstractTemplateView {
...
#Override
public boolean checkResource(Locale locale) throws Exception {
try {
// Check that we can get the template, even if we might subsequently get it again.
getTemplate(getUrl(), locale);
return true;
}
catch (FileNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("No FreeMarker view found for URL: " + getUrl());
}
return false;
}
catch (ParseException ex) {
throw new ApplicationContextException(
"Failed to parse FreeMarker template for URL [" + getUrl() + "]", ex);
}
catch (IOException ex) {
throw new ApplicationContextException(
"Could not load FreeMarker template for URL [" + getUrl() + "]", ex);
}
}
...
}
so that you may be extends InternalResourceView like below:
public class MyInternalResourceView extends InternalResourceView {
private static final boolean SEP_IS_SLASH = File.separatorChar == '/';
protected File getTemplate(String name, Locale locale) throws IOException {
File source;
if(getServletContext()!=null){
name = getServletContext().getRealPath("")+name;
source = new File( SEP_IS_SLASH ? name : name.replace('/',
File.separatorChar));
}
else{
source = new File( SEP_IS_SLASH ? name : name.replace('/',
File.separatorChar));
}
if (!source.isFile()) {
return null;
}
return source;
}
#Override
public boolean checkResource(Locale locale) throws Exception {
try {
// Check that we can get the template, even if we might subsequently
// get it again.
return getTemplate(getUrl(), locale)!=null;
} catch (IOException ex) {
return false;
}
}
}
and in viewResolver set your view class
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:viewClass="com.xxxx.MyInternalResourceView" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:contentType="text/html;charset=UTF-8" />