OSGI Blueprint Conditional Reference (ServiceMix) - reference

I have two beans, b1 and b2, and a boolean property myFlag. Both b1 and b2 need to be constructed as they are used in different parts of the application. The class hierarchies of b1 and b2 are as follows:
interface MyFirstInterface { ... }
class B1Class implements MyFirstInterface { ... }
interface MySecondInterface extends MyFirstInterface { ... }
class B2Class implements MySecondInterface { ... }
In my first blueprint file, I have:
<service id="b1" interface="com.company.MyFirstInterface">
<bean class="com.company.B1Class" />
</service>
<service id="b2" interface="com.company.MySecondInterface">
<bean class="com.company.B2Class" />
</service>
In my second blueprint file, I have:
<cm:property-placeholder persistent-id="com.company.settings" update-strategy="reload">
<cm:default-properties>
<cm:property name="myFlag"/>
</cm:default-properties>
</cm:property-placeholder>
<reference id="myBean" interface="com.company.MyFirstInterface">
Assume that I want:
myBean to refer to b1 if and only if myFlag is false, and
myBean to refer to b2 if and only if myFlag is true.
Is this possible given the above configuration? If so, what needs to be done to allow this to work?
Note: I would like to avoid adding additional properties. It is not a death sentence if I really need to add one; I need a solution to this problem regardless of convenience. myFlag just happens to be an existing property in a production system so if I can get away with sticking with this one property (which is directly related to which bean should be picked), that would preferred.

In case anyone else encounters this, let me post the solution we came up with.
Create your two beans
<bean id="b1" class="com.company.B1Class" />
<bean id="b2" class="com.company.B2Class" />
Create a factory class
public class MyFactoryClass {
private boolean myFlag;
private B1Class b1;
private B2Class b2;
public MyFactoryClass(boolean myFlag, B1Class b1, B2Class b2) {
this.myFlag = myFlag;
this.b1 = b1;
this.b2 = b2;
}
public MyFirstInterface buildIt() {
return myFlag ? b2 : b1;
}
}
Define the factory class as a bean and use it to select the class
<bean id="myFactory" class="com.company.MyFactoryClass">
<argument value="${myFlag}"/>
<argument ref="b1"/>
<argument ref="b2"/>
</bean>
<bean id="myBean" factory-ref="myFactory" factory-method="buildIt" />
<service id="myBean" interface="com.company.MyFirstInterface" />
The full blueprints
First Blueprint:
<cm:property-placeholder persistent-id="com.company.settings" update-strategy="reload">
<cm:default-properties>
<cm:property name="myFlag"/>
</cm:default-properties>
</cm:property-placeholder>
<bean id="b1" class="com.company.B1Class" />
<bean id="b2" class="com.company.B2Class" />
<bean id="myFactory" class="com.company.MyFactoryClass">
<argument value="${myFlag}"/>
<argument ref="b1"/>
<argument ref="b2"/>
</bean>
<bean id="myBean" factory-ref="myFactory" factory-method="buildIt" />
<service id="myBean" interface="com.company.MyFirstInterface" />
Second Blueprint:
<reference id="myBean" interface="com.company.MyFirstInterface" />

Related

How to create a date range as a condition of a promotion in hybris?

is it possible to create a date condition for promotion in hybris?
I am trying to do this implementation. my first problem is how to map the parameter value
<bean id="dateRuleParameterValueMapperDefinition" class="de.hybris.platform.ruleengineservices.rule.strategies.impl.RuleParameterValueMapperDefinition">
<property name="mapper" ref="dateRuleParameterValueMapper" />
<property name="type" value="java.util.Date" />
</bean>
in this mapping, I have an exception that the type is not supported (Caused by: de.hybris.platform.ruleengineservices.rule.strategies.RuleParameterValueMapperException:)
if so, can I resolve this error .. is it possible to create a date condition in the RuleConditionTranslator?
hybris version: 6.5
If you want to add any new RuleParameterValueMapperDefinition, you have to implement 'RuleParameterValueMapper' and override 'toString' and 'fromString' methods in your mapper implementation.
public class MediaTypeRuleParameterValueMapper implements RuleParameterValueMapper<MediaModel>
{
#Resource
MediaService mediaService;
#Override
public String toString(final MediaModel mediaModel)
{
Preconditions.checkArgument(Objects.nonNull(mediaModel), "mediaModel must not be null!");
return mediaModel.getCode();
}
#Override
public MediaModel fromString(final String mediaCode)
{
try
{
return mediaService.getMedia(mediaCode);
}
catch (UnknownIdentifierException e)
{
e.message()
}
return null;
}
}
Now create a custom mapper definition (RuleParameterValueMapperDefinition)
<bean id="mediaTypeRuleParameterValueMapper" class="com.hybris.MediaTypeRuleParameterValueMapper"/>
<bean id="mediaTypeRuleParameterValueMapperDefinition" class="de.hybris.platform.ruleengineservices.rule.strategies.impl.RuleParameterValueMapperDefinition">
<property name="mapper" ref="mediaTypeRuleParameterValueMapper"/>
<property name="type" value="ItemType(Media)"/>
</bean>
Now you are ready to use
RuleConditionDefinitionParameter.type=ItemType(Media)
I don't believe that you need to map a date in that way, it should be treated the same as a String or Integer. So for reference look at the way any of the String or Integer parameters are handled.
My basis for saying this is the following from ruleengineservices-spring-rule.xml:
<alias name="defaultRuleParameterSupportedTypes" alias="ruleParameterSupportedTypes" />
<util:set id="defaultRuleParameterSupportedTypes" value-type="java.lang.String">
<value>java.lang.Boolean</value>
<value>java.lang.Character</value>
<value>java.lang.String</value>
<value>java.lang.Byte</value>
<value>java.lang.Short</value>
<value>java.lang.Integer</value>
<value>java.lang.Long</value>
<value>java.lang.Float</value>
<value>java.lang.Double</value>
<value>java.math.BigInteger</value>
<value>java.math.BigDecimal</value>
<value>java.util.Date</value>
<value>java.lang.Enum</value>
<value>java.util.List</value>
<value>java.util.Map</value>
</util:set>

spring integration dynamic filter based on the current Date

I am trying to use ftp-inbound-adapter to poll files based on the the current date.And my inbound adapter uses a filter which refers to bean myfilter .The problem here is the current date is initailized at the startup and is not been processed dynamically..I would like to get the current date for every new message
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpsClientFactory"
filter="myFilter"
</int-ftp:inbound-channel-adapter>
<bean id="myFilter" class="org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter" scope="prototype">
<constructor-arg value="#{T(java.time.LocalDateTime).now().format(T(java.time.format.DateTimeFormatter).ofPattern('MMddyy'))}.(xls|xlsx)"/>
<aop:scoped-proxy/>
</bean>
UPDATE
I changed from this
<bean id="currentDate" class="java.util.Date" factory-bean="fastDateFormat"
scope="prototype" factory-method="format" >
<aop:scoped-proxy/>
<constructor-arg>
<bean class="java.util.Date" />
</constructor-arg>
</bean>
<bean id="myFilter" class="org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter" scope="prototype">
<constructor-arg value="#{currentDate}.(xls|xlsx)"/>
</bean>
And my inbound adapter uses a filter which refers to bean myFilter .. The problem here is the current date is initailized at the startup and is not been processed dynamically..I would like to get the current date for every new message
This impossible with your current configuration because that filter is just a singleton bean created only once at start up pulling that your currentDate for injection only once as well.
You may try to add <aop:scoped-proxy/> into your currentDate bean definition, though: https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#beans-factory-scopes-other-injection, but I would suggest to inject BeanFactorty into your filter and call getBean("currentDate", Date.class) every time you need a new instance of that prototype.
UPDATE
You inject BeanFactory into your filter instead of that currentDate bean. And then when filter logic is called you do Date currentDate = this.beanFactory.getBean("currentDate", Date.class);.
UPDATE2
Here is what I think should work for you:
public class DynamicRegexPatternFilter extends AbstractFileListFilter<File> {
#Autowired
private BeanFactory beanFactory;
#Override
public boolean accept(File file) {
return Pattern.compile(this.beanFactory.getBean("currentDate", String.class) + ".(xls|xlsx)")
.matcher(file.getName())
.matches();
}
}
You can make custom filter. Implement FileListFilter and override filterFiles method which takes input argument as array of LsEntry. LsEntry is nothing but a small metadata object of a File present at (S)FTP. It comprises of File Name and the date on which it was modified.
#Override
public List<LsEntry> filterFiles(LsEntry[] files) {
List<LsEntry> result = new ArrayList<LsEntry>();
Vector<LsEntry> list = new Vector<LsEntry>();
Collections.addAll(list, files);
ChannelSftp.LsEntry lastModifiedEntry = Collections.max(list,
(Comparator.comparingInt(entry -> entry.getAttrs().getMTime())));
result.add(lastModifiedEntry);
return result;
}
Reference: https://stackoverflow.com/a/60635859/11325128

Spring Integration Bridge - Request Headers

I have a SI application that uses Bridge to bridge some channels. I want the request headers to be copied through the bridge. When I debugged the application, I found that the BridgeHandler has a method which sets copyRequestHeaders() to false.
public class BridgeHandler extends AbstractReplyProducingMessageHandler {
#Override
public String getComponentType() {
return "bridge";
}
#Override
protected Object handleRequestMessage(Message<?> requestMessage) {
return requestMessage;
}
#Override
protected boolean shouldCopyRequestHeaders() {
return false;
}
}
I want to change this to keep it true. I looked up Bridge's XSD and it doesnt seem to support any entry to alter this, nor does it take a reference to a custom bridge handler bean.
What are my options now? Any suggestions would be helpful.
spring-config.xml:
<beans:import resource="classpath*:spring/rabbit.xml" />
<beans:import resource="classpath*:spring/router.xml" />
<beans:import resource="classpath*:spring/file_persister.xml" />
<beans:import resource="classpath*:spring/transformer.xml" />
<!-- Rabbit to Router -->
<int:bridge id="rabbitBridge" input-channel="rabbitInboundChannel" output-
channel="routerInputChannel" />
rabbit.xml:
<int:channel id="rabbitInboundChannel" />
<rabbit:connection-factory id="amqpConnectionFactoryInbound"
host="${rabbit.host}" port="${rabbit.port}"
username="${rabbit.username}" password="${rabbit.password}" channel-cache-
size="${rabbit.channelCacheSize}"
connection-factory="rabbitConnectionFactoryInbound" />
<beans:bean id="rabbitConnectionFactoryInbound"
class="com.rabbitmq.client.ConnectionFactory">
<beans:property name="requestedHeartbeat"
value="${rabbit.requestedHeartBeat}" />
</beans:bean>
<!-- Inbound Adapter to AMQP RabbitMq -->
<int-amqp:inbound-channel-adapter id="rabbitMQInboundChannelAdapter"
channel="rabbitInboundChannel" concurrent-
consumers="${rabbit.concurrentConsumers}" task-executor="rabbit-
executor" connection-factory="amqpConnectionFactoryInbound"
message-converter="byteArrayToStringConverter" queue-
names="${rabbit.queue}" error-channel="errorChannelId"
prefetch-count="${rabbit.prefetchCount}" />
<header-enricher input-channel="rabbitInboundChannel" output-
channel="rabbitLoggingChannel">
<int:header name="Operation" value="${operation.rabbit}" overwrite="true" />
**<int:header name="GUID" expression="#{ 'T(java.util.UUID).randomUUID().toString()' }" />**
<int:header name="operationStartTime" expression="#{ 'T(java.lang.System).currentTimeMillis()' }" />
</header-enricher>
<int:channel id="rabbitLoggingChannel">
<int:interceptors>
<int:wire-tap channel="loggerChannel" />
</int:interceptors>
</int:channel>
<task:executor id="rabbit-executor" rejection-policy="CALLER_RUNS" pool-
size="${rabbit.poolSize}" queue-capacity="${rabbit.queueSize}" />
router.xml:
<int:channel id="routerInputChannel" />
<int:header-enricher input-channel="routerInputChannel" output-
channel="routerLoggingChannel">
<int:header name="Operation" value="${operation.router}" overwrite="true"
/>
<int:header name="file_name" expression="headers['GUID'] + '.xml'" />
<int:header name="operationStartTime" expression="#{
'T(java.lang.System).currentTimeMillis()' }" overwrite="true" />
<int:error-channel ref="errorChannelId" />
</int:header-enricher>
<int:recipient-list-router id="recipientListRouter" input-
channel="routerInputChannel">
<int:recipient channel="filePersistChannel" selector-expression="new
String(payload).length()>0" />
<int:recipient channel="transformerInputChannel" />
</int:recipient-list-router>
<int:channel id="routerLoggingChannel">
<int:interceptors>
<int:wire-tap channel="loggerChannel" />
</int:interceptors>
</int:channel>
Logging O/P:
2017-04-24 13:26:33,360 [rabbit-executor-4] INFO
Operation="RABBIT_INBOUND_ADAPTER" Status="Success" DurationMs="0"
GUID="8d5c67c8-a0fb-4a7e-99dc-f545159dde7e"
2017-04-24 13:26:33,361 [rabbit-executor-4] INFO Operation="ROUTER"
Status="Success" DurationMs="0" GUID=" "
2017-04-24 13:26:33,364 [rabbit-executor-4] INFO Operation="FILE_PERSISTER"
Status="Success" DurationMs="3" GUID=" "
2017-04-24 13:26:33,381 [rabbit-executor-5] INFO
Operation="ORDER_EVENT_TRANSFORMER" Status="Success" DurationMs="27"
GUID=" "
If you notice, the GUID header which gets set in rabbit.xml is not propogated to router.xml, hence the log has empty GUID. Without the bridge, the GUID gets printed.
You don't need to do that. The <bridge> is based on the BridgeHandler which logic is very transparent:
#Override
protected Object handleRequestMessage(Message<?> requestMessage) {
return requestMessage;
}
So, it is obvious - your request headers are transferred, just because the whole requestMessage travels to the outputMessage.
The logic behind that method is like:
protected Message<?> createOutputMessage(Object output, MessageHeaders requestHeaders) {
AbstractIntegrationMessageBuilder<?> builder = null;
if (output instanceof Message<?>) {
if (!this.shouldCopyRequestHeaders()) {
return (Message<?>) output;
}
builder = this.getMessageBuilderFactory().fromMessage((Message<?>) output);
}
else if (output instanceof AbstractIntegrationMessageBuilder) {
builder = (AbstractIntegrationMessageBuilder<?>) output;
}
else {
builder = this.getMessageBuilderFactory().withPayload(output);
}
if (this.shouldCopyRequestHeaders()) {
builder.copyHeadersIfAbsent(requestHeaders);
}
return builder.build();
}
Since our output is Message and we don't copyRequestHeaders(), nothing is changed and everything is good for you.
I wonder what makes you to come to us with such an issue, because there is just no issue.
confused

Spring integration message-store rolled back before error handler

I need to handle certain error conditions within my spring integration flow. My flow is using a message store and setting the error channel on the poller. I had thought that if I handled the message in the error handler that the rollback would not occur, but the messageStore remove (delete) is being rolled back before the error flow is even executed.
Here is a pseudo-flow that duplicates my issue.
<int:channel id="rollbackTestInput" >
<int:queue message-store="messageStore"/>
</int:channel>
<int:bridge input-channel="rollbackTestInput" output-channel="createException" >
<int:poller fixed-rate="50"
error-channel="myErrorChannel">
<int:transactional />
</int:poller>
</int:bridge>
<int:transformer input-channel="createException" output-channel="infoLogger"
expression="T(GarbageToForceException).doesNotExist()" />
<int:channel id="myErrorChannel">
<int:queue/>
</int:channel>
<!-- JDBC Message Store -->
<bean id="messageStore" class="org.springframework.integration.jdbc.JdbcMessageStore">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource"/>
This flow will result in an infinite rollback/poll loop. How can I handle the error and not rollback?
That's correct behavior. The error-channel logic is accepted around the TX advice for the polling task.
The code looks like:
#Override
public void run() {
taskExecutor.execute(new Runnable() {
#Override
public void run() {
int count = 0;
while (initialized && (maxMessagesPerPoll <= 0 || count < maxMessagesPerPoll)) {
try {
if (!pollingTask.call()) {
break;
}
count++;
}
catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
else {
throw new MessageHandlingException(new ErrorMessage(e), e);
}
}
}
}
});
}
Where TX Advice is on the pollingTask.call(), but error handling is done from the taskExecutor:
this.taskExecutor = new ErrorHandlingTaskExecutor(this.taskExecutor, this.errorHandler);
Where your error-channel is configured on that errorHandler as MessagePublishingErrorHandler.
To reach your requirements you can try to follow with synchronization-factory on the <poller>:
<int:transaction-synchronization-factory id="txSyncFactory">
<int:after-rollback channel="myErrorChannel" />
</int:transaction-synchronization-factory>
Or supply your <int:transformer> with <request-handler-advice-chain>:
<int:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onFailureExpression" value="#exception" />
<property name="failureChannel" value="myErrorChannel" />
<property name="trapException" value="true" />
</bean>
</int:request-handler-advice-chain>
I have found a different solution that meets my requirements and is less invasive.
Instead of using transactional on the poller, I can use an advice chain that that specifies which exceptions I should rollback and which I shouldn't. For my case, I do not want to rollback most exceptions. So I rollback Throwable and list any specific exceptions I want to rollback.
<int:bridge input-channel="rollbackTestInput"
output-channel="createException">
<int:poller fixed-rate="50"
error-channel="myErrorChannel">
<int:advice-chain>
<int:ref bean="txAdvice"/>
</int:advice-chain>
</int:poller>
</int:bridge>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" rollback-for="javax.jms.JMSException"
no-rollback-for="java.lang.Throwable" />
</tx:attributes>
</tx:advice>

RESTEasy + Spring + Jackson + Jaxb

I am trying to serialize JAXB annotated class with Jackson using RESTEasy. By default ResteasyJacksonProvider is configured to use JACKSON annotation only. Is there a way to configure ResteasyJacksonProvider to use JAXB annotation using spring? There are couple of programmatic ways but would prefer if there is some spring configuration.
Couple of ways I am thinking of
Use ContextResolver for ObjectMapper type to return ObjectMapper configured to use JaxbAnnotationIntrospector instead of JacksonAnnotationIntrospector.
Extend ResteasyJacksonProvider and pass JAXB annotation during construction.
Any other way?
Well the first option of using ContextResolver works but I still think there should be an easier way to do this just by some configuration.
You can get this from the configuration only, no need to program anything special.
Here's how to:
First set your configuration right, I use Jackson + JAXB, both set under ContentNegotiatingViewResolver bean:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1"/>
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound name="com.shay.dashboard.data.structure.page.PageObject" />
<oxm:class-to-be-bound name="com.shay.dashboard.data.structure.tab.TabObject" />
<oxm:class-to-be-bound name="com.shay.dashboard.data.structure.section.SectionObject" />
<oxm:class-to-be-bound name="com.shay.dashboard.data.structure.element.nonembedded.ElementObject"/>
<oxm:class-to-be-bound name="com.shay.dashboard.data.structure.element.embedded.EmbeddedElementObject"/>
<oxm:class-to-be-bound name="com.shay.dashboard.data.structure.chart.common.ChartManager"/>
</oxm:jaxb2-marshaller>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
</list>
</property>
</bean>
Notice that under the marshaller I set the oxm:class-to-be-bound - those are the classes to be bound by JAXB.
Now for the module, I used ordinary annotation package (javax.xml.bind.annotation), non marshaller specific. Jackson Json and JAXB both know how to read it.
For example:
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name="page")
public class PageObject implements ComponentTypeObject{
#XmlAttribute(name="name")
private String name;
#XmlAttribute(name="id",required=true)
private String id;
#XmlElements({#XmlElement(name="tab", type=TabXmlAdapter.class)})
private List<TabXmlAdapter> tabRef;
Finally the controller for your MVC needs to return a model and view:
#RequestMapping(value="/get_page", method = RequestMethod.GET)
public ModelAndView initPage()
{
ModelAndView mav = null;
try
{
PageObject myPage = (PageObject) Utilities.getUtilities().loadObjectFromFile(XmlComponentType.page);
mav = new ModelAndView("page","page",myPage);
}
catch (Exception e)
{
e.getMessage();
}
return mav;
}
Now while calling your URL ending with .json you'd get the JSON representation, and with .xml - and XML. Both are translated by the viewer, provided you gave the correct mapping when annotating the module.

Resources