Hystrix sees List as incompatible return type in - hystrix

I have the following Hystrix fallback
public List<Data> getDataFallback(String id, LocalDate startDate, LocalDate endDate, Throwable t) {
log.warn("Could not get data");
throw new DataException(ErrorMessage.builder()
.code(COULD_NOT_GET_DATA.name())
.internalMessage(t.getMessage())
.build());
}
It is attached to the following method
#Override
#HystrixCommand(fallbackMethod = "getDataFallback")
public List<Data> getData(String id, LocalDate startDate, LocalDate endDate) {
return repository.find(id, startDate, endDate);
}
When running JUnit tests, and presumably also when running the service, I get the following error when running the getData-method
com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: Incompatible return types.
Command method: public java.util.List package.class.getData(java.lang.String,java.time.LocalDate,java.time.LocalDate);
Fallback method: public java.util.List package.class.getDataFallback(java.lang.String,java.time.LocalDate,java.time.LocalDate,java.lang.Throwable);
Hint: Different size of types variables.
Command type literals size = 2: [java.util.List<package.Data>, class package.Data]
Fallback type literals size = 1: [interface java.util.List]
If I wrap the list in a wrapper object, the exact same configuration works without a hitch. What gives?

Method Return type
and Parameter should be same
only one extra argument (Throwable throwable)
#GetMapping("/msg")
#HystrixCommand(fallbackMethod="fallBackGetUsers")
public String localMessage() {
return userServices.localMassage();
}
/*
* fallBack method parameter should be same and One Extra parameter will be
* there only and Return type also same
*/
public String fallBackGetUsers(Throwable throwable) {
return "fallback";
}

Related

How to pass parameter value to aspect

How do we get the parameter value(uId) from calling method(deleteTask) of JointPoint(in logJdbcOperation)? I am able to log sql statement and sql parameters but cannot find a way to log value of uId.
Please, guide me.
public int deleteTask(String taskname, String uId) {
String sql = "delete from shedlock where NAME= :name";
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
((MapSqlParameterSource) namedParameters).addValue("name", taskname, Types.VARCHAR);
return namedJdbcTemplate.update(sql, namedParameters);
}
#Aspect
#Component
public class LoggingAspect {
private static final Logger LOGGER = LogManager.getLogger(LoggingAspect.class);
#Before("execution(* org.springframework.jdbc.core..NamedParameterJdbcOperations.*(String, ..))")
public void logJdbcOperation(JoinPoint jp) {
Object[] methodArgs = jp.getArgs();
String statement = methodArgs[0].toString();
MapSqlParameterSource params = (MapSqlParameterSource)methodArgs[1];
LOGGER.debug("SQL statement:" + statement);
}
}
The code shared does not show how the uId is passed on to the method namedJdbcTemplate.update(sql, namedParameters). It is not possible to get the parameter to the calling method . Any parameter of the target method can be obtained.
Following code would advice the method deleteTask and get the arguments passed to it.
#Around("execution(* package.deleteTask(..)) && args(taskName,uId)")
public Object logDeleteTask(ProceedingJoinPoint pjp, String taskName, String uId) {
LOGGER.debug("Task Name :" + taskName);
LOGGER.debug("Uid :" + uId);
Object ret = 0;
try {
ret = pjp.proceed();
} catch (Throwable e) {
// handle exception if any
}
return ret;
}
Reference : Passing Parameter to Advice
Note : Remember to modify the package.deleteTask(..) to approrpriate package name.

Codenameone How to externalize the Location object

I receive following error message when I try to add Location for externalization. Please advise how to externalize the Location object. Please advise.
See the code used for Storage and Externalization
code:
addOfflineCommand("location", latitude, longitude, time1);
latitude - double data type;
longitude - double data type;
time1 - long data type;
private void addOfflineCommand(String name, Object... args) {
List<OfflineCommand> l_noAppt = (List<OfflineCommand>)
Storage.getInstance().readObject(appName + user + "-offlineCommandsLocEnc");
l_noAppt.add(new OfflineCommand(name, args));
}
Please see the Error Message below:
[EDT] 0:4:37,444 - Exception: java.io.IOException - Object type not supported: com.codename1.location.Location value: altitude = 1000.0
latitude40.714353
longtitude-74.00597299999998
direction0.0
timeStamp1529000278457
velocity50.0
at java.io.DataInputStream.readFully(DataInputStream.java:197)
at java.io.DataInputStream.readUTF(DataInputStream.java:609)
at java.io.DataInputStream.readUTF(DataInputStream.java:564)
at com.codename1.io.Util.readObject(Util.java:562)
at com.codename1.io.Util.readObject(Util.java:595)
at com.X.Xmobile.server.OfflineCommand.internalize(OfflineCommand.java:40)
at com.codename1.io.Util.readObject(Util.java:689)
at com.codename1.io.Util.readObject(Util.java:664)
at com.codename1.io.Storage.readObject(Storage.java:261)
at com.X.Xmobile.server.ServerImpl.addOfflineCommand(ServerImpl.java:1165)
at com.X.Xmobile.server.ServerImpl.finishActivity(ServerImpl.java:1504)
at com.X.Xmobile.forms.CommentForm.lambda$new$3(CommentForm.java:70)
java.io.IOException: Object type not supported: com.codename1.location.Location value: altitude = 1000.0
latitude40.714353
longtitude-74.00597299999998
direction0.0
timeStamp1529000278457
velocity50.0
at com.codename1.io.Util.writeObject(Util.java:457)
at com.codename1.io.Util.writeObject(Util.java:394)
at com.X.Xmobile.server.OfflineCommand.externalize(OfflineCommand.java:34)
at com.codename1.io.Util.writeObject(Util.java:258)
at com.codename1.io.Util.writeObject(Util.java:286)
at com.codename1.io.Storage.writeObject(Storage.java:224)
at com.X.Xmobile.server.ServerImpl.addOfflineCommand(ServerImpl.java:1201)
at com.X.Xmobile.server.ServerImpl.finishActivity(ServerImpl.java:1504)
[EDT] 0:6:4,551 - Exception: java.io.IOException - Object type not supported: com.co
Following is the class OfflineCommand class created which is used for externalization.
public class OfflineCommand implements Externalizable {
private static final int VERSION = 1;
private String name;
private Object[] arguments;
public OfflineCommand() {}
public OfflineCommand(String name, Object... args) {
this.name = name;
this.arguments = args;
}
#Override
public int getVersion() {
return VERSION;
}
#Override
public void externalize(DataOutputStream out) throws IOException {
Util.writeUTF(name, out);
Util.writeObject(arguments, out);
}
#Override
public void internalize(int version, DataInputStream in) throws IOException {
name = Util.readUTF(in);
arguments = (Object[])Util.readObject(in);
}
#Override
public String getObjectId() {
return "OfflineCommand";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object[] getArguments() {
return arguments;
}
public void setArguments(Object[] arguments) {
this.arguments = arguments;
}
}
Location isn't externalizable. It doesn't support object serialization explicitly. Normally you would need to convert the write/read data to work with the location data instead of the location object. However, since your code is generic you would need to extend location to implement externalizable.
public class ExternalizableLocation extends Location implements Externalizable {
// implement the externalizable interface here...
}
In your init(Object) class register ExternalizableLocation as externalizable and in every place where you store a Location object replace it with ExternalizableLocation.

Overriding Hystrix command properties specified in .properties file

I need to override command timeout property specified in my application.properties file. Here is what I tried
#Test
public void testTokenQueryTimeout() throws Exception
{
String propertyToSet ="hystrix.command.quickbaseTokenQueryCommand.execution.isolation.thread.timeoutInMilliseconds";
String prop="";
try {
prop = ConfigurationManager.getConfigInstance().getProperty(
propertyToSet).toString();
logger.info("\n\n\noriginal quickbaseTokenQueryCommand timeout ="+prop);
System.setProperty(
propertyToSet,"10");
prop = ConfigurationManager.getConfigInstance().getProperty(
propertyToSet).toString();
logger.info("\n\n\nupdated quickbaseTokenQueryCommand timeout ="+prop);
String response = accountValidation.isValidToken(token);
logger.info(response);
Assert.assertFalse(true);
}
catch (AccountValidationServiceException e)
{
Assert.assertTrue(Constants.ERRCODE_TOKEN_QUERY_TIMED_OUT.equals(e.getErrorCode()));
}
finally {
ConfigurationManager.getConfigInstance().clearProperty(propertyToSet);
System.clearProperty(propertyToSet);
if(!GeneralUtil.isObjectEmpty(System.getProperty(
propertyToSet)))prop = System.getProperty(
propertyToSet);
logger.info("Updated testTokenQueryTimeout timeout ="+prop);
}
}
Notice, System.setProperty(propertyToSet,"10"). With this approach this test case passes i.e. the property gets changed and command times out but another test case fails due to this command timeout though I am clearing the property from System.
I also tried setting the property using ConfigurationManager.getConfigInstance().setProperty(
propertyToSet).toString(),"10"); But in that case, this change of property has no effect and command does not timeout.
Is there something I am missing here.
Please help.
Try using the ConcurrentCompositeConfiguration class
application.properties
hystrix.command.HelloWorldCommand.execution.isolation.thread.timeoutInMilliseconds=200
Command
public class HelloWorldCommand extends HystrixCommand<String> {
public HelloWorldCommand() {
super(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"));
}
#Override
protected String run() throws Exception {
TimeUnit.MILLISECONDS.sleep(1100);
return "Hello";
}
}
Test
public class HelloWorldCommandTest {
#Test
public void commandConfigTest() {
String propertyKey = "hystrix.command.HelloWorldCommand.execution.isolation.thread.timeoutInMilliseconds";
ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) ConfigurationManager.getConfigInstance();
Integer originalTimeout = (Integer) config.getProperty(propertyKey);
config.setOverrideProperty(propertyKey, 1200);
String result = new HelloWorldCommand().execute();
assertThat(result, is("Hello"));
config.setOverrideProperty(propertyKey, originalTimeout);
Integer timeoutValue = (Integer) config.getProperty(propertyKey);
assertThat(timeoutValue, is(originalTimeout));
}
}

Mixin annotation not getting honored when passed as a parameter

I have a third party class SpecialObject as:
public class SpecialObject {
private String name;
private Integer id;
private Date date;
public String getFoo() {return "foo";} //Outlier
public String getName() { return name;}
public Integer getId() {return id;}
public Date getDate() {return date;}
public void setName(String name) {this.name = name;}
public void setId(Integer id) {this.id = id;}
public void setDate(Date date) {this.date = date;}
}
I wish to only project out name and date properties when serializing it. Using the magic of MixinAnnotation from Jackson, I created a Mixin interface as:
#JsonAutoDetect(getterVisibility = Visibility.NONE)
public interface SpecialObjectMixin {
#JsonProperty
public String getName();
#JsonProperty
public Date getDate();
}
In order to facilitate handling of this SpecialObject as parameter, I have also defined a SpecialObjectHandler which implements the fromString() method.
#Override
public SpecialObject fromString(String json) {
try {
return objectMapper.readValue(json, SpecialObject.class);
} catch (IOException exception) {
throw new IllegalArgumentException("Unable to write JSON output",
exception);
}
}
When the deserializer invokes this method, the objectMapper throws an error as
Caused by: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "foo" (Class com.kilo.SpecialObject), not marked as ignorable
at [Source: java.io.StringReader#2d2217da; line: 1, column: 60] (through reference chain: com.kilo.SpecialObject["foo"])
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:673)
at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:659)
at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1365)
at org.codehaus.jackson.map.deser.BeanDeserializer._handleUnknown(BeanDeserializer.java:725)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:703)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1863)
at com.kilo.SpecialObjectHandler.fromString(SpecialObjectHandler.java:34)
My question is that is there a way that I can have the objectMapper (org.codehaus.jackson.map.ObjectMapper) also honor annotations from the Mixin where I had configured it to only deal with name and date? Feel free to point out something elementary that I may have overlooked. Thanks in advance!
It was a problem with configuration. The mixin was only set on the serialization config and not on the deserialization config causing the issue. Setting it on both configs solves the problem.

Spring JDBC BeanPropertyRowMapper yes no ('Y','N') to boolean bean properties

I have a class with some string, int and boolean fields. I have the getters and setters declared for them.
public class SomeClass {
private int id;
private String description;
private boolean active;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
I am BeanPropertyRowMapper to get all the objects from and Oracle DB.
#Override
public List<Destination> getAll() {
List<SomeClass> objs = jdbcTemplate.query(
myQuery, new BeanPropertyRowMapper<SomeClass>(SomeClass.class));
return objs;
}
If the debug is turned on I see:
[3/14/13 10:02:09:202 EDT] 00000018 SystemOut O DEBUG BeanPropertyRowMapper - Mapping column 'ID' to property 'id' of type int
[3/14/13 10:02:09:202 EDT] 00000018 SystemOut O DEBUG BeanPropertyRowMapper - Mapping column 'DESCRIPTION' to property 'description' of type class java.lang.String
And then it fails trying to map active. Active is defined as 1 byte CHAR in the DB with values as 'Y' or 'N'. What is the best way to use BeanPropertyRowMapper and successfully convert values such as 'Y', and 'N' to boolean?
So I figured out how to do this. I extended BeanPropertyRowMapper and handler boolean types through some custom code before handing off the control to beanpropertyrowmapper for rest of the data types.
Note: It works for me because I use oracle and all of the 'boolean' type columns are strings with 'y','yes','n' & 'no' type values.
Those who use numerical 1,0 or other formats could potentially improve it further by making it generic through an object yes map and getting objects from resultset and looking them up in this map. Hope this helps someone else in a situation like mine.
import java.beans.PropertyDescriptor;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
/**
* Extends BeanPropertyRowMapper to allow for boolean fields
* mapped to 'Y,'N' type column to get set correctly. Using stock BeanPropertyRowMapper
* would throw a SQLException.
*
*/
public class ExtendedBeanPropertyRowMapper<T> extends BeanPropertyRowMapper<T> {
//Contains valid true values
public static final Set<String> TRUE_SET = new HashSet<String>(Arrays.asList("y", "yes", "true"));
public ExtendedBeanPropertyRowMapper(Class<T> class1) {
super(class1);
}
#Override
/**
* Override <code>getColumnValue</code> to add ability to map 'Y','N' type columns to
* boolean properties.
*
* #param rs is the ResultSet holding the data
* #param index is the column index
* #param pd the bean property that each result object is expected to match
* (or <code>null</code> if none specified)
* #return the Object value
* #throws SQLException in case of extraction failure
* #see org.springframework.jdbc.core.BeanPropertyRowMapper#getColumnValue(java.sql.ResultSet, int, PropertyDescriptor)
*/
protected Object getColumnValue(ResultSet rs, int index,
PropertyDescriptor pd) throws SQLException {
Class<?> requiredType = pd.getPropertyType();
if (boolean.class.equals(requiredType) || Boolean.class.equals(requiredType)) {
String stringValue = rs.getString(index);
if(!StringUtils.isEmpty(stringValue) && TRUE_SET.contains(stringValue.toLowerCase())){
return true;
}
else return false;
}
return super.getColumnValue(rs, index, pd);
}
}
BeanPropertyRowMapper will convert values into a Boolean object with 0=false and 1=true. Just tried this and it works.
This blog post has more information, as well as code examples in Java and C with OCCI.
Old question, but you can do something like
public void setIsActive(String active) {
this.active = "Y".equalsIgnoreCase(active);
}
As pointed out by Harikumar, BeanPropertyRowMapper does in fact convert 0 and 1 to boolean values. I couldn't find any documentation to support this, but this is in fact the current case.
So, a solution that doesn't require you to extend BeanPropertyRowMapper would be to decode your column into these values:
#Override
public List<Destination> getAll() {
List<SomeClass> objs = jdbcTemplate.query(
"SELECT ID, DESCRIPTION, " +
" DECODE(ACTIVE, 'Y', 1,'N', 0) as ACTIVE " +
" FROM YOUR_TABLE",
new BeanPropertyRowMapper<SomeClass>(SomeClass.class));
return objs;
}

Resources