Spring Integration Message History - java

I have setup #EnableMessageHistory
I have created custom transformers like this
public class FileMoveTransformer implements GenericTransformer<CustomerPojo, CustomerPojo> {
private boolean renameFile;
private String toLocation;
private static final Logger LOGGER = LoggerFactory.getLogger(FileMoveTransformer.class);
public FileMoveTransformer(String toLocation, final boolean renameFile) {
this.toLocation = toLocation;
this.renameFile = renameFile;
}
#Override
public CustomerPojo transform(CustomerPojo input) {
return input;
}
}
When I look at the Message history its showing like this
How do I change the "name" attribute to my own transformer as above will make not sense to print.

The MessageHistory makes it based on the bean name:
private static Properties extractMetadata(NamedComponent component) {
Entry entry = new Entry();
String name = component.getComponentName();
String type = component.getComponentType();
if (name != null && !name.startsWith("org.springframework.integration")) {
entry.setName(name);
if (type != null) {
entry.setType(type);
}
}
if (!entry.isEmpty()) {
entry.setTimestamp(Long.toString(System.currentTimeMillis()));
}
return entry;
}
Since you don't provide an explicit id for the endpoint which uses your FileMoveTransformer, you get that generated bean name based on the endpoint ConsumerEndpointFactoryBean class.
Since you don't show how you use this FileMoveTransformer, I only can abuse guessing that it is about an IntegrationFlow and you have something like this:
.transform(new FileMoveTransformer())
So, consider to add an id there like:
.transform(new FileMoveTransformer(), e -> e.id("fileMoveTransformer"))
https://docs.spring.io/spring-integration/reference/html/java-dsl.html#java-dsl-endpoints
Otherwise, please, share how you use it and we will let you know what need to be changed to bring your own custom id for the component and make your message history much readable.

Related

Can value return from method are assignable for constant?

I want to format some files in SpringBoot with one request for each file. With each request, I have to call the getOutputFolder(dirName) method to get an output path in order to save the file in the expected path but my solution comes with high at overhead cost. I want to define one constant and then when I have to call the function I instead call this. But I feel it seems to be wrong or at least like a sneaky way to do. Is there any better way to solve this problem?
private static final String OUTPUT_FOLDER_PATH = getOutputFolderPath();
private String getOutputFolder(String dirName) {
String pathStr = getOutputFolderPath() + dirName + File.separator + "submit" + File.separator;
Path outputDirPath = Paths.get(pathStr);
Path path = null;
boolean dirExists = Files.exists(outputDirPath);
if (!dirExists) {
try {
path = Files.createDirectories(outputDirPath);
} catch (IOException io) {
logger.error("Error occur when create the folder at: {}", pathStr);
}
}
return dirExists ? pathStr : Objects.requireNonNull(path).toString();
}
You might want to look at jcache.
To do this, you need to install it to your Spring Boot project
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'javax.cache:cache-api:1.1.0'
// or the maven equivalent if you are using maven
Then create a org.springframework.cache.CacheManager bean to configure the cache.
#Bean
public CacheManager cacheManager() {
CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();
// The class arguments is <String, String> because the method to cache accepts a String and returns a String
// just explore this object for the config you need.
MutableConfiguration<String, String> configuration = new MutableConfiguration<>();
String cacheName = "OUTPUT_FOLDER_CACHE";
cacheManager.createCache(cacheName, configuration);
return cacheManager;
}
When this is setup, you can now annotated the method to be cached.
#Cacheable(
cacheNames = { "OUTPUT_FOLDER_CACHE" }, // The same string in config
unless = "#result == null" // Dont' cache null result; or do, if you need it.
)
String getOutputFolder(String dirName) {
// method contents...
}
When properly configured: the method will return the cache value if it exists, or run the actual method, cache the result and return the result if the cached value does not exist.
You can solve that issue by using ThreadLocal.
A threadlocal can store value and you can make useful for yourself. Suppose if your getOutputFolderPath() is different for different request then you can
store the getOutputFolderPath() value while a new request dispatched on server and you can do your all operation upto your request live.
See Threadlocal Docs
#Service
public class FileSaveService {
private static final ThreadLocal<String> path=new ThreadLocal<>();
#PostConstruct
public void init() {
setPath(getOutputFolderPath());
}
public void setPath(String pathString) {
path.set(pathString);
}
public String getPath() {
if(path.get() == null) return getOutputFolderPath();
return path.get();
}
#PreDestroy
public void destroy() {
path.remove();
}
}

Enum Localization generic approach

Enum
public enum EmployeeStatus {
ACTIVE, IN_ACTIVE
}
In callers scattered all over the application whicg get and set the enum like below. Here are examples
Caller_1
if(employee.getStatus() == EmployeeStatus.STATUS.SUBMITTED) {
}
Caller_2
employee.setStatus(EmployeeStatus.STATUS.SUBMITTED)
Problem
I need to implement the internationalization so that end user sees the employee status as per locale. So when i set the status for french locale
it should set the value from right resource bundel. Is there a way i can achieve this without changing the caller code. Here is the solution
I can think of
My proposed solution :-
public enum EmployeeStatus {
ACTIVE, IN_ACTIVE
public static String toString() {
return I18n.getMessage("label." + this);
}
}
public final class I18n {
private I18n() {
}
private static ResourceBundle bundle;
public static String getMessage(String key) {
if(bundle == null) {
bundle = ResourceBundle.getBundle("path.to.i18n.messages");
}
return bundle.getString(LocaleContextHolder.getLocale());
}
}
With this approach I need to add toString method in every Enum without change in caller ? Is there a better generic approach spring provides ?
I am using spring 4. See if spring can help here .

How to write a method using single responsibility princpile?

I have a below class in which isValid method is being called.
I am trying to extract few things from Record object in the isValid method. And then I am validating few of those fields. If they are valid, then I am populating the holder map with some additional fields and then I am populating my DataHolder builder class and finally return the DataHolder class back.
If they are not valid, I am returning null.
Below is my class:
public class ProcessValidate extends Validate {
private static final Logger logger = Logger.getInstance(ProcessValidate.class);
#Override
public DataHolder isValid(String processName, Record record) {
Map<String, String> holder = (Map<String, String>) DataUtils.extract(record, "holder");
String deviceId = (String) DataUtils.extract(record, "deviceId");
Integer payId = (Integer) DataUtils.extract(record, "payId");
Long oldTimestamp = (Long) DataUtils.extract(record, "oldTimestamp");
Long newTimestamp = (Long) DataUtils.extract(record, "newTimestamp");
String clientId = (String) DataUtils.extract(record, "clientId");
if (isValidClientIdDeviceId(processName, deviceId, clientId) && isValidPayId(processName, payId)
&& isValidHolder(processName, holder)) {
holder.put("isClientId", (clientId == null) ? "false" : "true");
holder.put("isDeviceId", (clientId == null) ? "true" : "false");
holder.put("abc", (clientId == null) ? deviceId : clientId);
holder.put("timestamp", String.valueOf(oldTimestamp));
DataHolder dataHolder =
new DataHolder.Builder(record).setClientId(clientId).setDeviceId(deviceId)
.setPayId(String.valueOf(payId)).setHolder(holder).setOldTimestamp(oldTimestamp)
.setNewTimestamp(newTimestamp).build();
return dataHolder;
} else {
return null;
}
}
private boolean isValidHolder(String processName, Map<String, String> holder) {
if (MapUtils.isEmpty(holder)) {
// send metrics using processName
logger.logError("invalid holder is coming.");
return false;
}
return true;
}
private boolean isValidpayId(String processName, Integer payId) {
if (payId == null) {
// send metrics using processName
logger.logError("invalid payId is coming.");
return false;
}
return true;
}
private boolean isValidClientIdDeviceId(String processName, String deviceId, String clientId) {
if (Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(deviceId)) {
// send metrics using processName
logger.logError("invalid clientId and deviceId is coming.");
return false;
}
return true;
}
}
Is my isValid method doing lot of things? Can it be broken down in multiple parts? Or is there any better way to write that code?
Also I don't feel great with the code I have in my else block where I return null if record is not valid. I am pretty sure it can written in much better way.
Update:
In my case this is what I was doing. I am calling it like this:
Optional<DataHolder> validatedDataHolder = processValidate.isValid(processName, record);
if (!validatedDataHolder.isPresent()) {
// log error message
}
// otherwise use DataHolder here
So now it means I have to do like this:
boolean validatedDataHolder = processValidate.isValid(processName, record);
if (!validatedDataHolder) {
// log error message
}
// now get DataHolder like this?
Optional<DataHolder> validatedDataHolder = processValidate.getDataHolder(processName, record);
You are correct isValid() is doing too many things. But not only that, when most of us see a method that is called isValid() - we expect a boolean value to be returned. In this case, we're getting back and instance of DataHolder which is counterintuitive.
Try to split the things that you do in the method, for example:
public static boolean isValid(String processName, Record record) {
return isValidClientIdDeviceId(processName, record) &&
isValidPayId(processName, record) &&
isValidHolder(processName, record);
}
and then construct DataHolder in a different method, say:
public static Optional<DataHolder> getDataHolder(String processName, Record record) {
Optional<DataHolder> dataHolder = Optional.empty();
if (isValid(processName, record)) {
dataHolder = Optional.of(buildDataHolder(processName, record));
// ...
}
return dataHolder;
}
It will make your program easier to both read and maintain!
I think things start with naming here.
As alfasin is correctly pointing out, the informal convention is that a method named isValid() should return a boolean value. If you really consider returning a DataHolder; my suggestion would be to change name (and semantics a bit), like this:
DataHolder fetchHolderWithChecks(String processName, Record ...)
And I wouldn't return null - either an Optional; or simply throw an exception. You see, don't you want to tell your user about that error that occured? So when throwing an exception, you would have a mean to provide error messages to higher levels.
On validation itself: I often use something like this:
interface OneAspectValidator {
void check(... // if you want to throw an exception
boolean isValid(... // if you want valid/invalid response
And then various implementations of that interface.
And then, the "validation entry point" would somehow create a list, like
private final static List<OneAspectValidator> validators = ...
to finally iterate that list to validate those aspects one by one.
The nice thing about that approach: you have the code for one kind of validation within one dedicated class; and you can easily enhance your validation; just by creating a new impl class; and adding a corresponding object to that existing list.
I know this might not be directly actionable, but the first thing you should do if you want to clean up this code is to use OO (Object-Orientation). If you are not using OO properly, then there is no point arguing the finer details of OO, like SRP.
What I mean is, I couldn't tell what you code is about. Your classnames are "ProcessValidate" (is that even a thing?), "Record", "DataHolder". That is pretty suspect right there.
The string literals reveal more about the domain ("payId", "deviceId", "clientId") than your identifiers, which is not a good sign.
Your code is all about getting data out of other "objects" instead of asking them to do stuff (the hallmark of OO).
Summary: Try to refactor the code into objects that reflect your domain. Make these objects perform tasks specific to their responsibilities. Try to avoid getting information out of objects. Try to avoid setting information into objects. When that is done, it will be much more clear what SRP is about.

How do I validate an annotation at compile time?

I have an annotation defined:
public #interface AdminMapping {
public final static int USER = 100;
public final static int COMPANY = 10;
public final static int ADMIN = 0;
int adminLevel() default AdminMapping.USER;
String displayName();
String category() default "";
String hasPermission() default "";
String parentCategory() default "";
}
I want to make it so you can't have a parentCategory unless you have a category....
#AdminMapping(category="Company", displayName="FOO", adminLevel=AdminMapping.USER)
public static final String MONKEY = "chimp";
#AdminMapping(parentCategory="Company", displayName="BAR", adminLevel=AdminMapping.USER) //NOT VALID Parent without Category
public static final String HORSE = "zebra";
while I'm at it I'd also like it if I could make it so the category doesn't equal the parentCategory.
I know how to use ConstraintValidator to validate MONKEY and HORSE but I want to validate the actual AdminMapping entries. Can this be done at compile time?
Any help is appreciated.
Use PMD. PMD is a tool that parses your source code and lets you check that it meets certain rules. It comes with lots of built in rules but you can write your own so you can write a rule to check your annotations are defined how you want.
See this page for an explanation of writing your own rules.
For example, it is easy to write a rule that checks whether an annotation has both parentCategory and category defined.
First declare the custom rule in a rules xml file:
<?xml version="1.0"?>
<ruleset name="My custom rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<rule name="NoParent"
message="No parent category without category"
class="com.test.CategoryRule">
<description>
No parent category without category
</description>
</rule>
Then define a Java class that will do the actual enforcement.
public class CategoryRule extends AbstractJavaRule {
public Object visit(ASTMemberValuePairs node, Object data) {
boolean hasCategory = false;
boolean hasParentCategory = false;
for ( int i = 0; i < node.jjtGetNumChildren(); ++i ) {
if ( node.jjtGetChild(i).getImage().equals("category") ) {
hasCategory = true;
} else if ( node.jjtGetChild(i).getImage().equals("parentCategory") ) {
hasParentCategory = true;
}
}
if ( hasCategory && !hasParentCategory ) {
addViolation(data, node);
}
return data;
}
}
Obviously you'd need to write the code to be a bit more robust in reality, check the annotation type, but hopefully that enough to get you started.
You need to get a reference to the annotation and check its attributes
AdminMapping mapping = ...; // depends on what your annotation is annotating
if (mapping.category().isEmpty()) {
if (!mapping.parentCategory().isEmpty()) {
// we got a beef
}
}

Jackson Change JsonIgnore Dynamically

I have a class and there are variables inside it as well. Sometimes I want to ignore some fields and sometimes not when deserializing (maybe at serializing too). How can I do it at Jackson?
For serialization, "filtering properties" blog entry should help. Deserialization side has less support, since it is more common to want to filter out stuff that is written.
One possible approach is to sub-class JacksonAnnotationIntrospector, override method(s) that introspect ignorability of methods (and/or fields) to use whatever logic you want.
It might also help if you gave an example of practical application, i.e what and why you are trying to prevent from being deserialized.
You might want to use JsonViews ( took it originally from http://wiki.fasterxml.com/JacksonJsonViews - broken now - web archive link: https://web.archive.org/web/20170831135842/http://wiki.fasterxml.com/JacksonJsonViews )
Quoting it:
First, defining views means declaring classes; you can reuse existing ones, or just create bogus classes -- they are just view identifiers with relationship information (child inherits view membership from parents):
// View definitions:
class Views {
static class Public { }
static class ExtendedPublic extends PublicView { }
static class Internal extends ExtendedPublicView { }
}
public class Bean {
// Name is public
#JsonView(Views.Public.class) String name;
// Address semi-public
#JsonView(Views.ExtendPublic.class) Address address;
// SSN only for internal usage
#JsonView(Views.Internal.class) SocialSecNumber ssn;
}
With such view definitions, serialization would be done like so:
// short-cut:
objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class);
// or fully exploded:
objectMapper.getSerializationConfig().setSerializationView(Views.Public.class);
// (note: can also pre-construct config object with 'mapper.copySerializationConfig'; reuse)
objectMapper.writeValue(out, beanInstance); // will use active view set via Config
// or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
and result would only contain 'name', not 'address' or 'ssn'.
You should probably look at the modules feature of recent Jackson versions.
One possible mechanism would be to use a BeanDeserializerModifier.
I've been looking for a useful online tutorial or example, but nothing immediately appears. It might be possible to work something up if more is known of your context. Are you managing your ObjectMappers manually, or using them in a JAX-RS setting, injected in Spring, or what?
I searched the entire web (yes I did) to find the answer. then I wrote something on my own.
I'm working with Jackson ion deserialisation. I wrote a custom reader that ignores the fields dynamically.
You can do the same thing for json deserialisation.
Lets assume an entity like this.
User {
id
name
address {
city
}
}
Create a tree structure to represent field selection.
public class IonField {
private final String name;
private final IonField parent;
private final Set<IonField> fields = new HashSet<>();
// add constructs and stuff
}
Custom Ion Reader extending from amazon ion-java https://github.com/amzn/ion-java
public class IonReaderBinaryUserXSelective extends IonReaderBinaryUserX {
private IonField _current;
private int hierarchy = 0;
public IonReaderBinaryUserXSelective(byte[] data, int offset, int length,
IonSystem system, IonField _current) {
super(system, system.getCatalog(), UnifiedInputStreamX.makeStream(data, offset, length));
this._current = _current;
}
#Override
public IonType next() {
IonType type = super.next();
if (type == null) {
return null;
}
String file_name = getFieldName();
if (file_name == null || SystemSymbols.SYMBOLS.equals(file_name)) {
return type;
}
if (type == IonType.STRUCT || type == IonType.LIST) {
IonField field = _current.getField(getFieldName());
if (field != null) {
this._current = field;
return type;
} else {
super.stepIn();
super.stepOut();
}
return next();
} else {
if (this._current.contains(file_name)) {
return type;
} else {
return next();
}
}
}
#Override
public void stepIn() {
hierarchy = (hierarchy << 1);
if (getFieldName() != null && !SystemSymbols.SYMBOLS.equals(getFieldName())) {
hierarchy = hierarchy + 1;
}
super.stepIn();
}
#Override
public void stepOut() {
if ((hierarchy & 1) == 1) {
this._current = this._current.getParent();
}
hierarchy = hierarchy >> 1;
super.stepOut();
}
Construct dynamic view. This Tree dynamically created and passed to the reader to deserialise.
Let's say we only need city inside the address.
IonField root = new IonField("user", null);
IonField address = new IonField("address", root);
IonField city = new IonField("city", address);
address.addChild(city);
root.addChild(id);
//now usual stuff.
IonFactory ionFactory = new IonFactory();
IonObjectMapper mapper = new IonObjectMapper(ionFactory);
File file = new File("file.bin"); // ion bytes
byte[] ionData = Files.readAllBytes(file.toPath());
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonReader ionReader = new IonReaderBinaryUserXSelective(ionData, 0, ionData.length, ionSystem, root);
User user = mapper.readValue(ionReader, User.class);

Categories