Spring Integration JPA - Retrieving Outbound Gateway with Entity Graph - spring-data-jpa

Is it possible to use entity graph in Jpa Retrieving Outbound Gateway?
#Bean
public IntegrationFlow findContract() {
return f -> f
.handle(Jpa.retrievingGateway(this.entityManagerFactory)
.entityClass(Contract.class)
//.parameterExpression("contractNumber", "payload"))
// set entityGraph name
;
}
I would like to use approach similar to Spring Data Jpa
public interface ContractRepository extends JpaRepository<Contract, Long> {
#EntityGraph(value = "contract.documents", type = EntityGraphType.LOAD)
Contract findByContractNumber(String contractNumber);
}

This is just not implemented in the Spring Integration.
Feel free to raise an appropriate JIRA for such an improvement. Of course, Contribution is welcome.
Meanwhile as a workaround you can use a #ServiceActivator (.handle() in Java DSL) to call that ContractRepository.findByContractNumber() method.

Related

Migrate Spring cloud stream listener (kafka) from declarative to functional model

I'm trying to migrate an implementation of spring cloud streams (kafka) declarative way to the recommended functional model
In this blog post they say :
...a functional programming model in Spring Cloud Stream (SCSt). It’s
less code, less configuration. Most importantly, though, your code is
completely decoupled and independent from the internals of SCSt
My current implementation:
Declaring the MessageChanel
#Input(PRODUCT_INPUT_TOPIC)
MessageChannel productInputChannel();
Using #StreamListener which is deprecated now
#StreamListener(StreamConfig.PRODUCT_INPUT_TOPIC)
public void addProduct(#Payload Product product, #Header Long header1, #Header String header2)
Here it is
#Bean
public Consumer<Product> addProduct() {
return product -> {
// your code
};
}
I am not sure what is the value of PRODUCT_INPUT_TOPIC, but let's assume input.
So the s-c-stream will automatically create a binding for you with name addProduct-in-0. Here are the details. You can use it as is, but if you still want to use the custom name, you can use spring.cloud.stream.function.bindings.addProduct-in-0=input. - see more here.
If you need access to headers, you can just pass a Message as input argument
Here it is
#Bean
public Consumer<Message<Product>> addProduct() {
return message -> {
Product product = message.getPayload();
// your code
};
}

How to add custom spring cloud gateway filter in Java config?

I've created a custom filter in Spring Cloud Gateway by extending the class with "LoggingGlobalPreFilter"
I have the following routes.
return builder.routes().route(r -> r.path("/first/**").uri("http://localhost:8222"))
.route(r -> r.path("/seconf/**").uri("http://localhost:8333")).build();
Not sure how to add custom filter there. All the articles on the internet are talking about configuring filters in .yml file.
Thanks!
#Component
public class CustomFilter implements GlobalFilter, Ordered {
}

Change Spring Data REST exposing link

By default, Spring Data REST will expose search resources to urls under {resource_name}/search. For example, findBySubject_Id in the following code will be exposed to {baseUrl}/questions/search/findBySubject_Id?subjectId={subjectId}.
public interface QuestionRepository extends PagingAndSortingRepository<Question, String> {
Page<Question> findBySubject_Id(#Param("subjectId")String subjectId, Pageable pageable);
}
For compatiblility reasons, I need the exposing link to be {baseUrl}/questions?subjectId={subjectId}. Is there any way to do it?
You can not override /search url. If you really need a link without search, you should override response handler for the entity via writing your own controller with annotation #RepositoryRestController

Olingo OData V2 Read Property not implemented

I implemented an OData V2 Service with Apache Olingo V2 in connectin with JPA using EclipseLink. All requests are working fine, but when it comes to the point, where I want to access a single property via GET request from an entity set like for the following URL:
http://localhost:8080/MyODataService/XXXXXX.svc/EntitySet(12345)/Property
the response in return is:
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code/>
<message xml:lang="de-DE">Not implemented</message>
</error>
The class which extends the ODataJPASeviceFactory looks as follows:
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext;
import org.apache.olingo.odata2.jpa.processor.api.ODataJPAServiceFactory;
import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPARuntimeException;
public class JPAODataServiceFactory extends ODataJPAServiceFactory
{
private static final String PERSISTENCE_UNIT_NAME = "MyPersistenceUnitName";
#Override
public ODataJPAContext initializeODataJPAContext() throws ODataJPARuntimeException
{
ODataJPAContext oDatJPAContext = this.getODataJPAContext();
try
{
EntityManagerFactory emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
oDatJPAContext.setEntityManagerFactory(emf);
oDatJPAContext.setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
return oDatJPAContext;
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
My question now is:
How do I implement the functionality, so that I can do GET and POST requests not only for a whole entity set, but also for a single property of an entity set like I tried with the mentioned URL?
Accessing a single property from one entity is currently not implemented if you use the Olingo JPA Extension.
If you want to support this behaviour you can register a custom processor and only override the "readEntityComplexProperty" and "readEntitySimpleProperty" methods. There you can have your custom code where you specifically get back the value.
Every method you don`t override will result in the standard Olingo functionality being executed.
Here is a tutorial on how to register a custom JPA processor: http://olingo.apache.org/doc/odata2/tutorials/CustomODataJPAProcessor.html
Here is the example on how your code can look like if you implement the functionality yourself: https://github.com/apache/olingo-odata2/blob/597465569fdd15976d0486711d4a38f93a7c6696/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ListsProcessor.java#L592
You need to create an association between your entity sets.
For example, to access the following URL: http://localhost:8080/myService.svc/Cars('6')/Manufacturer, you need to create an assocation between your car and your manufacturer association sets.
Have a look at the documentation: https://olingo.apache.org/doc/odata2/tutorials/basicread.html
Hegg,
you can use
http://localhost:8080/MyODataService/XXXXXX.svc/EntitySet(12345)/?$select=Property
Bye
Domenico

Spring DefaultMessageListenerContainer/SimpleMessageListenerContainer (JMS/AMQP) Annotation configuration

So I'm working on a project where many teams are using common services and following a common architecture. One of the services in use is messaging, currently JMS with ActiveMQ. Pretty much all teams are required to follow a strict set of rules for creating and sending messages, namely, everything is pub-subscribe and the messages that are sent are somewhat like the following:
public class WorkDTO {
private String type;
private String subtype;
private String category;
private String jsonPayload; // converted custom Java object
}
The 'jsonPayload' comes from a base class that all teams extend from so it has common attributes.
So basically in JMS, everyone is always sending the same kind of message, but to different ActiveMQ Topics. When the message (WorkDTO) is sent via JMS, first it is converted into a JSON object then it is sent in a TextMessage.
Whenever a team wishes to create a subscriber for a topic, they create a DefaultMessageListenerContainer and configure it appropriately to receive messages (We are using Java-based Spring configuration). Basically every DefaultMessageListenerContainer that a team defines is pretty much the same except for maybe the destination from which to receive messages and the message handler.
I was wondering how anyone would approach further abstracting the messaging configuration via annotations in such a case? Meaning, since everyone is pretty much required to follow the same requirements, could something like the following be useful:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Listener {
String destination();
boolean durable() default false;
long receiveTimeout() default -1; // -1 use JMS default
String defaultListenerMethod() default "handleMessage";
// more config details here
}
#Listener(destination="PX.Foo", durable=true)
public class FooListener {
private ObjectMapper mapper = new ObjectMapper(); // converts JSON Strings to Java Classes
public void handleMessage(TextMessage message){
String text = message.getText();
WorkDTO dto = mapper.readValue(text, WorkDto.class);
String payload = dto.getPayload();
String type = dto.getType();
String subType = dto.getSubType();
String category = dto.getCategory();
}
}
Of course I left out the part on how to configure the DefaultMessageListenerContainer by use of the #Listener annotation. I started looking into a BeanFactoryPostProcessor to create the necessary classes and add them to the application context, but I don't know how to do all that.
The reason I ask the question is that we are switching to AMQP/RabbitMQ from JMS/ActiveMQ and would like to abstract the messaging configuration even further by use of annotations. I know AMQP is not like JMS so the configuration details would be slightly different. I don't believe we will be switching from AMQP to something else.
Here teams only need to know the name of the destination and whether they want to make their subscription durable.
This is just something that popped into my head just recently. Any thoughts on this?
I don't want to do something overly complicated though so the other alternative is to create a convenience method that returns a pre-configured DefaultMessageListenerContainer given a destination and a message handler:
#Configuration
public class MyConfig{
#Autowired
private MessageConfigFactory configFactory;
#Bean
public DefaultMessageListenerContainer fooListenerContainer(){
return configFactory.getListenerContainer("PX.Foo", new FooListener(), true);
}
}
class MessageConfigFactory {
public DefaultMessageListenerContainer getListener(String destination, Object listener, boolean durable) {
DefaultMessageListenerContainer l = new DefaultMessageListenerContainer();
// configuration details here
return l;
}
}