How do I configure this JdbcMessageHandler to pull parameters from the message instead of static beans? - spring-integration

The Symptom:
When invoked, the cleanupMessageHandler component (a JdbcMessageHandler) of this subflow is not pulling parameters from the message, but is instead attempting to pull from a static bean.
The exception message:
org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter 'ALC_startTime': Invalid property 'ALC_startTime' of bean class [org.springframework.integration.jdbc.JdbcMessageHandler$1]: Bean property 'ALC_startTime' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
The SQL
Delete from SB_STREAM.TT_9321383
WHERE (LASTUPDATESTAMP >= to_timestamp (:ALC_startTime, 'YYYY-MM-DD HH24:MI:SS.FF')
and LASTUPDATESTAMP <= to_timestamp (:ALC_endTime, 'YYYY-MM-DD HH24:MI:SS.FF'))
AND ttkey = BIGINT(:ID)
AND (ttobjectname = :ALC_object AND ttschema = :ALC_schema)
The Flow:
return flowDef
.filter(getFilterExpression(rule)).channel(new DirectChannel())
.handle(inboundAdapter) // a JdbcOutboundAdapter
.split(insertDeleteSplitter)
.publishSubscribeChannel(taskExecutor, c ->
c.subscribe(s -> s
.filter ("....")
.transform(genericTransformer)
.handle(insertUpdateMessageHandler(rule))) // a JDBCMessageHandler
.subscribe(s -> s
.filter("....")
.transform(genericTransformer)
.handle(deleteMessageHandler(rule))) // a JDBCMessageHandler
.subscribe(sub -> sub
.handle(cleanupMessageHandler(rule))) // a JDBCMessageHandler
.errorHandler(new CustomErrorHandler() //
);
The cleanupMessagehandler
private MessageHandler cleanupMessageHandler(RuleMetadata rule) {
<snip ...>
SQLTextAndParamsList sql = sqlFactory.getCleanupSQL(rule);
JdbcMessageHandler handler = new JdbcMessageHandler(dbprx.getDatasource(), sql.getSql());
return handler;
}
Message as received by JdbcMessageHandler
SQLParameterSources at point where JDBCOperation is invoked
Alternate coding - batchArray of values at JDBCOperation is invoked (added ExpressionEvaluatingSqlParameterSourceFactory to the JdbcMessageHandler)
Debugger view at AbstractNestablePropertyAccessor.getPropertyValue()
Debugger view at CachedIntrospectionResults.getPropertyDecriptor()
Edit: code changes
The generated SQL was updated to this:
Delete from SB_STREAM.TT_9321383
WHERE (LASTUPDATESTAMP >= to_timestamp (:payload[ALC_startTime], 'YYYY-MM-DD HH24:MI:SS.FF')
and LASTUPDATESTAMP <= to_timestamp (:payload[ALC_endTime], 'YYYY-MM-DD HH24:MI:SS.FF'))
AND ttkey = :payload[ID]
AND (ttobjectname = :payload[ALC_object] AND ttschema = :payload[ALC_schema])

According to your first screenshot, the payload of your message is an ArrayList, which indeed does not have those ALC_startTime, ALC_endTime properties. There are just no those getters on the ArrayList!
To be able to read a content of your list, you need to configure an ExpressionEvaluatingSqlParameterSourceFactory on your JdbcMessageHandler. See docs for more info: https://docs.spring.io/spring-integration/docs/current/reference/html/jdbc.html#passing-parameters-by-using-spel-expressions.
Probably better to convert your list into a Map somehow or some POJO representation. With the list it is going to be hard for your to determine the target value by just index...
Edit:
This is a batch update, so the array list of maps is correct.
The SPEl expressions in the SQL parameters are malformed. The correct form is :payload[ALC_startTime].

Related

How to hardcode the entity varchar value?

The requirement is to store the hardcoded value for varchar which is in an entity file(.eti). I tried adding to the default option but it is not reflecting.
Default option works well with boolean values (true/false), typelists (you can choose a default typecode), monetary amounts too, but it looks like it is not allowed to specify a default varchar.
Therefore the easiest way would be to create a preupdate rule which inserts that default value every time when you create a new record in the database.
Preupdate rule example:
#gw.rules.RuleName("YourEntityAssignDefaultValue")
internal class YourEntityAssignDefaultValueRule {
static function doCondition(yourEntity : entity.YourEntity) : boolean {
return yourEntity.New
}
static function doAction(yourEntity : entity.YourEntity, actions : gw.rules.Action) {
yourEntity.yourColumn = "defaultValue"
}
}
you can achieve through getter and setter properties in an appropriate enhancement class.
public property get PolicyNumber(): String {
return this.PolicyNumber }
and somewhere class you must be assigned the value to the PolicyNumber field then it will reflect.

PF Calendar in Dialog nulls Java Property in UiComponent#visitTree

Prerequisites:
xmlns:p="http://primefaces.org/ui"
javax.faces.component.UIComponent
Setup:
org.primefaces:primefaces:jar:6.1.2
org.glassfish:javax.faces:jar:2.2.14
Tomcat 8.5.28
Proprietary OR Mapper
#Property
private Calendar cal;
public Calendar getCal() {
return ORMapper.getProperty(this, "cal");
}
Functional setup:
p:commandButton that during its action executes code (that leads to the error) and then opens a dialogue which contains
<p:calendar id="cal" value="#{bean.model.cal}" />
Execution:
During Invoke Application of the commandButton (action) we traverse a section of the DOM tree that does NOT contain the dialogue (i.e. that dialogue is not a child of component) using
component.visitTree(
VisitContext.createVisitContext(
FacesContext.getCurrentInstance(),
null,
Collections.singleton(VisitHint.SKIP_UNRENDERED)),
(context, component) -> {
...
return VisitResult.ACCEPT;
}
);
Before the tree execution
model.cal = value
model.getCal() = value
Errorneous behaviour:
After the tree execution
model.cal = null
model.getCal() = value
This then leads to an error when validating the model which has a violation on e.g. #NotNull on the property (not the getter)
Notes:
Using IntelliJ attempts where made to find the change: Field Access/Modification breakpoints, calls to get/setter breakpoints, EL Method Invoker breakpoints
Removing the visitTree call fixes the Problem
Removing the element from the dialogue fixes the problem
Later on the value is set again, when couldnt be figured out either

change label value using value stored at session

i have two jsf pages (home.jsf and employees.jsf) ,
home page has a button that navigates to employees page,
while navigating i store value in session scope
at (Managed bean)
public void putSessionAL(ActionEvent actionEvent) {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("key","value");
}
public String navigate() {
return "employees";
}
i want to change Label at employees viewObject from UIHints tab depending on value stored at session using the following groovy expression
adf.context.sessionScope.key
and changed trustMode to trusted but it fires the following exception
oracle.jbo.script.ExprScriptException: JBO-29114 ADFContext is not setup to process messages for this exception. Use the exception stack trace and error code to investigate the root cause of this exception. Root cause error code is JBO-25188. Error message parameters are {0=Employees.FirstName, 1=, 2=oracle.jbo.script.ExprSecurityException}
at oracle.jbo.script.ExprScriptException.throwException(ExprScriptException.java:316)
at oracle.jbo.script.ExprScriptException.throwExceptionWithExprDef(ExprScriptException.java:387)
at oracle.jbo.ExprEval.processScriptException(ExprEval.java:599)
at oracle.jbo.ExprEval.doEvaluate(ExprEval.java:697)
at oracle.jbo.ExprEval.evaluate(ExprEval.java:508)
at oracle.jbo.ExprEval.evaluate(ExprEval.java:487)
at oracle.jbo.common.NamedObjectImpl.resolvePropertyRaw(NamedObjectImpl.java:680)
at oracle.jbo.server.DefObject.resolvePropertyRaw(DefObject.java:366)
One way to do it at the VO UIHint attribute label level will be programmaticaly by doing as follow :
In your VO go to the java tab and add the RowImpl java class
In the VORowImpl Add the following function
public String getMySessionLabel() {
return (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("key");
}
In the Label add the following EL expression :
adf.object.getMySessionLabel()
This technique allow you more control than pure EL, if you want to do more than getting from session for example. In your case pure EL, as you did, should work as well. (Would need to check what is wrong with yours, maybe just missing the
#{adf.context.sessionScope.key}
If you attempt to get your label from a method in viewRowImpl. So this will be executed at least once for each row. I think this solution isn't fit for your case.
anyway ADF as a framework added strong policy and validations in EL in general and especially in version 12.2.x.
The solution for you case as following:
Create new class in model layer which extends oracle.jbo.script.ExprSecurityPolicy class
Override checkProperty method.
#Override
public boolean checkProperty(Object object, String string, Boolean b) {
if (object.getClass().getName().equals("oracle.adf.share.http.ServletADFContext") && string.equals("sessionScope")) {
return true;
}
return super.checkProperty(object, string, b);
}
Open adf-config.xml source and in startup tag set your class ExprSecurityPolicy property.
like:
<startup ExprSecurityPolicy="model.CustomExprSecurityPolicy">

How to map an int to a boolean

I'm using AutoMapper 5.2. I currently have a mapping statement that looks as follows:
CreateMap<JeffreysOnline.Data.Customer, JeffreysOnline.Entities.Customer>()
.ForMember(s => s.CustomerWant, t => t.Ignore());
Both the Customer table and Customer entity have a field named BadChecks. In the database it's an int. I recently changed the type to a bool in my entity. AutoMapper is now giving me the following error:
Unable to create a map expression from Customer.BadChecks (System.Int16) to Customer.BadChecks (System.Boolean) Mapping types: Customer -> Customer JeffreysOnline.Data.Customer -> JeffreysOnline.Entities.Customer Type Map configuration: Customer -> Customer JeffreysOnline.Data.Customer -> JeffreysOnline.Entities.Customer Property: BadChecks
It seems AutoMapper doesn't know how to map from an int to a boolean. Is it possible for me to help AutoMapper with this?
It may be helpful to know that in my DAL, I'm using ProjectTo() to pass an IQueryable to another method that is attempting to access the data, and therefore the mapping is occurring (an error being generated). My DAL code looks like this:
return entityList.OrderBy(row => row.LastName).ProjectTo<Entities.Customer>();
Automapper 6.0.2 - works without any ForMember... null, 0 = false, values >= 1 are mapped to true.
In Automapper 6.0.2 - other way:
class nnnProfile : Profile
{
CreateMap<src, dst>()
.ForMember(d => d.Decision, opt => opt.ResolveUsing<CustomBoolResolver>());
}
Resolver:
public class CustomBoolResolver : IValueResolver<src, dst, bool>
{
public bool Resolve(src source, dst destination, bool destMember,
ResolutionContext context)
{
return source.Decision == 1;
}
}
but this is per Destination, so not much flexible.
According to this page:
http://taswar.zeytinsoft.com/automapper-mapping-objects-part-5-of-7-customresolver/
In past you could write a custom resolver with just Source and target type.
I don't think I would know how to map from int to a boolean.
If you do figure out how that should happen, you'll need to create a mapping from int to boolean.:
CreateMap<int, bool>().ProjectUsing(src => src != 0);
Completely guessing there. But since you're using ProjectTo, you'll need to use ProjectUsing so that the expression makes it allllll the way down to your DAL.
Remember, when using ProjectUsing, AutoMapper isn't actually executing the mapping. It's creating a LINQ "Select" expression that it passes down to your query provider (EF maybe?). So you'll need to make sure that whatever you use in your projection expression, EF can support translating that eventually into SQL.

Configuring notification tag for Azure Function

I'm using an Azure function to pick up messages from an event hub and send out notifications via the Azure notification hub. Works great! Now I wanted to see whether I could add tags to those messages in order to allow user targeting via those tags.
The output for the notification hub has a "tag expression" parameter which you can configure. But this seems to be a static text. I need to dynamically set these tags based on the message received from the event hub instead. I'm not sure whether you can somehow put dynamic content in there?
I also found that the constructor of the GcmNotification object that I'm using has an overload which allows a tag string to be used. But when I try that I get a warning on compile time stating this is deprecated and when the function fires it shows an error because the Tag property should be empty.
So I'm not clear on a) whether this is at all possible and b) how to do it when it is. Any ideas?
Update: as suggested I tried creating a POCO object to map to my input string. The string is as follows:
[{"deviceid":"repsaj-neptune-win10pi","readingtype":"temperature1","reading":22.031614503139451,"threshold":23.0,"time":"2016-06-22T09:38:54.1900000Z"}]
The POCO object:
public class RuleMessage
{
public string deviceid;
public string readingtype;
public object reading;
public double threshold;
public DateTime time;
}
For the function I now tried both RuleMessage[] and List<RuleMessage> as parameter types, but the function complains it cannot convert the input:
2016-06-24T18:25:16.830 Exception while executing function: Functions.submerged-function-ruleout. Microsoft.Azure.WebJobs.Host: Exception binding parameter 'inputMessage'. Microsoft.Azure.WebJobs.Host: Binding parameters to complex objects (such as 'RuleMessage') uses Json.NET serialization.
1. Bind the parameter type as 'string' instead of 'RuleMessage' to get the raw values and avoid JSON deserialization, or
2. Change the queue payload to be valid json. The JSON parser failed: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Submission#0+RuleMessage' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Function code:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Microsoft.Azure.NotificationHubs;
public static void Run(List<RuleMessage> inputEventMessage, string inputBlob, out Notification notification, out string outputBlob, TraceWriter log)
{
if (inputEventMessage == null || inputEventMessage.Count != 1)
{
log.Info($"The inputEventMessage array was null or didn't contain exactly one item.");
notification = null;
outputBlob = inputBlob;
return;
}
log.Info($"C# Event Hub trigger function processed a message: {inputEventMessage[0]}");
if (String.IsNullOrEmpty(inputBlob))
inputBlob = DateTime.MinValue.ToString();
DateTime lastEvent = DateTime.Parse(inputBlob);
TimeSpan duration = DateTime.Now - lastEvent;
if (duration.TotalMinutes >= 0) {
notification = GetGcmMessage(inputMessage.First());
log.Info($"Sending notification message: {notification.Body}");
outputBlob = DateTime.Now.ToString();
}
else {
log.Info($"Not sending notification message because of timer ({(int)duration.TotalMinutes} minutes ago).");
notification = null;
outputBlob = inputBlob;
}
}
private static Notification GetGcmMessage(RuleMessage input)
{
string message;
if (input.readingtype == "leakage")
message = String.Format("[FUNCTION GCM] Leakage detected! Sensor {0} has detected a possible leak.", input.reading);
else
message = String.Format("[FUNCTION GCM] Sensor {0} is reading {1:0.0}, threshold is {2:0.0}.", input.readingtype, input.reading, input.threshold);
message = "{\"data\":{\"message\":\""+message+"\"}}";
return new GcmNotification(message);
}
public class RuleMessage
{
public string deviceid;
public string readingtype;
public object reading;
public double threshold;
public DateTime time;
}
Update 28-6-2016: I've not managed to get it working by switching ASA output to line seperated to that it doesn't generate a JSON array any more. This is a temp. fix because the Function binding now fails as soon as there is more than one line in the output (which can happen).
Anyways, I now proceeded to set the tagExpression, as per instruction I changed it to:
{
"type": "notificationHub",
"name": "notification",
"hubName": "repsaj-neptune-notifications",
"connection": "repsaj-neptune-notifications_NOTIFICATIONHUB",
"direction": "out",
"tagExpression": "deviceId:{deviceid}"
}
Where {deviceid} equals the deviceid property on my RuleMessage POCO. Unfortunately this doesn't work, when I call the function it outputs:
Exception while executing function: Functions.submerged-function-ruleout. Microsoft.Azure.WebJobs.Host: Exception binding parameter 'notification'. Microsoft.Azure.WebJobs.Host: No value for named parameter 'deviceid'.
Which is not true, I know for sure the property has been set as I've logged it to the ouput window. I also tried something like {inputEventMessage.deviceid}, but that doesn't work either (as I didn't get how the runtime would map {deviceid} to the correct input object when there's more than one.
The tagExpression binding property supports binding parameters coming from trigger input properties. For example, assume your incoming Event Hub event has properties A and B. You can use these properties in your tagExpression using the parens syntax, e.g.: My Tag {A}-{B}.
In general, most of the properties across all the binding types support binding parameters in this way.

Resources