Spring Data Solr ConverterNotFoundException - java

I'm trying to configure Solr (with Multicore Support) in my application and I get a ConverterNotFoundException whenever I try and register converters.
I've stepped through and can see the query being executed and documents being returned. Just the converters not being found.
I followed the example from the official docs here.
Hopefully someone can shed some light on what's going on as examples are hard to find and the docs aren't overly clear about adding converters when using multicoreSupport=true.
#Configuration
#EnableSolrRepositories(
multicoreSupport = true,
basePackages = {"uk.co.foo.bar.repository"})
public class SolrConfig {
#Resource
private Environment environment;
#Bean
public SolrClient solrClient(HttpClient httpClient) {
String solrHost = environment.getRequiredProperty("solr.host");
return new HttpSolrClient(solrHost, httpClient);
}
#Bean
public HttpClient httpClient() {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set(HttpClientUtil.PROP_BASIC_AUTH_USER, "user");
params.set(HttpClientUtil.PROP_BASIC_AUTH_PASS, "pass");
return HttpClientUtil.createClient(params);
}
#Bean
public SolrConverter solrConverter(CustomConversions customConversions){
MappingSolrConverter mappingSolrConverter= new MappingSolrConverter(new SimpleSolrMappingContext());
mappingSolrConverter.setCustomConversions(customConversions);
return mappingSolrConverter;
}
#Bean
public CustomConversions customConversions(){
return new CustomConversions(Arrays.asList(new fooConverter(), new barConverter()));
}
#Bean
public SolrTemplate solrTemplate(SolrClient solrClient, SolrConverter solrConverter){
SolrTemplate solrTemplate = new SolrTemplate(solrClient);
solrTemplate.setSolrConverter(solrConverter);
return solrTemplate;
}
}

Having multicore support enabled currently does not allow to register global CustomConverters. Unfortunately there's no workaround available. I'll take care of DATASOLR-173 to get this fixed.

Related

Update: Spring Boot JMS static reply queue on IBM MQ Series

In my use case I need to do request-reply call to a remote system via managed queues. Using Spring Boot and IBM's MQ starter I have the problem that the application wants to create dynamic/temporary reply queues instead of using the already existing managed queue.
Configuration is set up here
#EnableJms
#Configuration
public class QueueConfiguration {
#Bean
public MQQueueConnectionFactory connectionFactory() throws JMSException {
MQQueueConnectionFactory factory = new MQQueueConnectionFactory();
factory.setTransportType(CT_WMQ); // is 1
factory.setHostName(queueProperties.getHost());
factory.setPort(queueProperties.getPort());
factory.setChannel(queueProperties.getChannel()); // combo of ${queueManager}%${channel}
return factory;
}
#Bean
public JmsMessagingTemplate messagingTemplate(ConnectionFactory connectionFactory) {
JmsMessagingTemplate jmt = new JmsMessagingTemplate(connectionFactory);
jmt.setDefaultDestinationName(queueProperties.getQueueName());
return jmt;
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.foo.model");
return marshaller;
}
#Bean
public MessageConverter messageConverter(Jaxb2Marshaller marshaller) {
MarshallingMessageConverter converter = new MarshallingMessageConverter();
converter.setMarshaller(marshaller);
converter.setUnmarshaller(marshaller);
return converter;
}
}
Usage is pretty straight forward: Take the object convert and send it. Wait for response receive
and convert it.
#Component
public class ExampleSenderReceiver {
#Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
#Override
#SneakyThrows
public ResponseExample sendAndReceive(RequestExample request, String correlationId) {
MessagePostProcessor mpp = message -> {
message = MessageBuilder.fromMessage(message)
.setHeader(JmsHeaders.CORRELATION_ID, correlationId)
// .setHeader(JmsHeaders.REPLY_TO, "DEV.QUEUE.3") this triggers queue creation
.build();
return message;
};
String destination = Objects.requireNonNull(jmsMessagingTemplate.getDefaultDestinationName());
return jmsMessagingTemplate.convertSendAndReceive(destination, request, ResponseExample.class, mpp);
}
I read already a lot of IBM documentation and think, I need to set the message type to "MQMT_REQUEST" but I do not find the right spot to do so.
Update
Added Spring Integration as Gary proposed and added a configuration for JmsOutboundGateway
#Bean
public MessageChannel requestChannel() {
return new DirectChannel();
}
#Bean
public QueueChannel responseChannel() {
return new QueueChannel();
}
#Bean
#ServiceActivator(inputChannel = "requestChannel" )
public JmsOutboundGateway jmsOutboundGateway( ConnectionFactory connectionFactory) {
JmsOutboundGateway gateway = new JmsOutboundGateway();
gateway.setConnectionFactory(connectionFactory);
gateway.setRequestDestinationName("REQUEST");
gateway.setReplyDestinationName("RESPONSE");
gateway.setReplyChannel(responseChannel());
gateway.setCorrelationKey("JMSCorrelationID*");
gateway.setIdleReplyContainerTimeout(2, TimeUnit.SECONDS);
return gateway;
}
And adapted my ExampleSenderReceiver class
#Autowired
#Qualifier("requestChannel")
private MessageChannel requestChannel;
#Autowired
#Qualifier("responseChannel")
private QueueChannel responseChannel;
#Override
#SneakyThrows
public ResponseExample sendAndReceive(RequestExample request, String correlationId) {
String xmlContent = "the marshalled request object";
Map<String, Object> header = new HashMap<>();
header.put(JmsHeaders.CORRELATION_ID, correlationId);
GenericMessage<String> message1 = new GenericMessage<>(xmlContent, header);
requestChannel.send(message1);
log.info("send done" );
Message<?> receive = responseChannel.receive(1500);
if(null != receive){
log.info("incoming: {}", receive.toString());
}
}
The important part is gateway.setCorrelationKey("JMSCorrelationID*");
Without that line the correlationId was not propagated correct.
Next step is re-adding MessageConverters and make it nice again.
Thank you.
The default JmsTemplate (used by the JmsMessagingTemplate) always uses a temporary reply queue. You can subclass it and override doSendAndReceive(Session session, Destination destination, MessageCreator messageCreator) to use your managed queue instead.
However, it will only work if you have one request outstanding at a time (e.g. all run on a single thread). You will also have to add code for discarding "late" arrivals by checking the correlation id.
You can use async sends instead and handle replies on a listener container and correlate the replies to the requests.
Consider using spring-integration-jms and its outbound gateway instead - it has much more flexibility in reply queue handling (and does all the correlation for you).
https://docs.spring.io/spring-integration/reference/html/jms.html#jms-outbound-gateway
You are missing the queue manager.
ibm:
mq:
queueManager: QM1
channel: chanel
connName: localhost(1414)
user: admin
password: admin

Is there a way to deactivate the connection between the Java application and the mongo database to see what exceptions are thrown at run time?

I am trying to understand how I can shutdown the connection to mongo while my java application runs for tesing purposes. I am trying to see how my application would behave in case mongo is unavailable. Is there any way this accomplished at the application level without touching the mongo database?. This is the class I use to create the mongo connection
#Configuration
#EnableMongoAuditing
public class MongoConfiguration {
#Autowired
private MongoProperties mongoProperties;
#Bean
public MongoClient mongo(){
ConnectionString connectionString = new ConnectionString(mongoProperties.getUri());
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
return MongoClients.create(mongoClientSettings);
}
// to use #Transactional -- supporting spring data transaction support in MongoDB
#Bean
MongoTransactionManager transactionManager(MongoDbFactory dbFactory){
return new MongoTransactionManager(dbFactory);
}
#Primary
#Bean(name = "mongoTemplate")
public MongoTemplate mongoTemplate(){
return new MongoTemplate(mongo(), mongoProperties.getDatabase());
}
}
You can use fail points. See https://github.com/mongodb/specifications/tree/master/source/transactions/tests#failcommand for examples and you need to start the server with --setParameter enableTestCommands=1.

Spring Boot 2.3.1.Release and Cassandra DriverTimeout issue

I am upgrading to Spring boot version 2.3.1.Release. So, there is a major change in Spring Data Cassandra due to Java driver version upgrade to v4. I am stuck on application startup because there is a DriverTimeout exception being thrown:
com.datastax.oss.driver.api.core.DriverTimeoutException: [s0|control|id: 0x8572a9d7, L:/My_IPv4:Random_Port - R:/Cassandra_Server:Port] Protocol initialization request, step 1 (OPTIONS): timed out after 500 ms
My cassandra configuration:
#Bean(name = "mySession")
#Primary
public CqlSession session() {
String containerIpAddress = getContactPoints();
int containerPort = getPort();
InetSocketAddress containerEndPoint = new InetSocketAddress(containerIpAddress, containerPort);
return CqlSession.builder().withLocalDatacenter(getLocalDataCenter())
.addContactPoint(containerEndPoint)
.withAuthCredentials(dbProperties.getCassandraUserName(), dbProperties.getCassandraPassword())
.withKeyspace(getKeyspaceName()).build();
}
I have also tried using DriverConfigLoader option by explicitly setting connection timeout as:
#Bean(name = "mySession")
#Primary
public CqlSession session() {
String containerIpAddress = getContactPoints();
int containerPort = getPort();
InetSocketAddress containerEndPoint = new InetSocketAddress(containerIpAddress, containerPort);
DriverConfigLoader loader =
DriverConfigLoader.programmaticBuilder()
.withDuration(DefaultDriverOption.CONNECTION_CONNECT_TIMEOUT, Duration.ofSeconds(5))
.build();
return CqlSession.builder().withLocalDatacenter(getLocalDataCenter())
.withConfigLoader(loader)
.addContactPoint(containerEndPoint)
.withAuthCredentials(dbProperties.getCassandraUserName(), dbProperties.getCassandraPassword())
.withKeyspace(getKeyspaceName()).build();
}
, but to no avail and same exception is being thrown. My current Spring boot version is 2.2.0.Release and I am not even specifying any timeout there and its working fine. How can this issue be fixed?
Assuming that you are using spring-data-cassandra. You can either configure it entirely through your application properties.
Alternatively you can go the route that you're going and build out your own session. If you're doing it that way, you may want to turn off Spring's autoconfiguration.
#EnableAutoConfiguration(exclude = {
CassandraDataAutoConfiguration.class })
and then instead of building the CqlSession, use the CqlSessionFactory like:
#Primary
#Bean("mySessionFactory")
public CqlSessionFactoryBean getCqlSession() {
CqlSessionFactoryBean factory = new CqlSessionFactoryBean();
factory.setUsername(userName);
factory.setPassword(password);
factory.setPort(port);
factory.setKeyspaceName(keyspaceName);
factory.setContactPoints(hostList);
factory.setLocalDatacenter(datacenter);
return factory;
}

How to properly configure a TCP inboundAdapter with QueueChannel and ServiceActivator

I am trying to configure a TCP socket that receives data in the format name,value in distinct messages. Those messages arrive on average every second, sometimes faster or sometimes slower.
I was able to set up a working configuration but I am lacking a basic understanding of what actually is happening in Spring Integration.
My configuration file looks like this:
#Configuration
#EnableIntegration
public class TCPSocketServerConfig
{
#Bean
public IntegrationFlow server(
final CSVProcessingService csvProcessingService,
#Value("${tcp.socket.server.port}") final int port
)
{
return IntegrationFlows.from(
Tcp.inboundAdapter(
Tcp.nioServer(port)
.deserializer(serializer())
.leaveOpen(true)
)
.autoStartup(true)
.outputChannel(queueChannel())
).transform(new ObjectToStringTransformer())
.handle(csvProcessingService)
.get();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller()
{
return Pollers.fixedDelay(50, TimeUnit.MILLISECONDS).get();
}
#Bean
public MessageChannel queueChannel()
{
return MessageChannels.queue("queue", 50).get();
}
#Bean
public ByteArrayLfSerializer serializer()
{
final ByteArrayLfSerializer serializer = new ByteArrayLfSerializer();
serializer.setMaxMessageSize(10240);
return serializer;
}
}
And the CSVProcessingService looks like this (abbreviated):
#Slf4j
#Service
public class CSVProcessingService
{
#ServiceActivator
public void process(final String message)
{
log.debug("DATA RECEIVED: \n" + message);
final CsvMapper csvMapper = new CsvMapper();
final CsvSchema csvSchema = csvMapper.schemaFor(CSVParameter.class);
if (StringUtils.contains(message, StringUtils.LF))
{
processMultiLineInput(message, csvMapper, csvSchema);
}
else
{
processSingleLineInput(message, csvMapper, csvSchema);
}
}
}
My goals for this configuration are the following:
receive messages on the configured port
withstand a higher load without losing messages
deserialize the messages
put them into the queue channel
(ideally also log errors)
the queue channel is polled every 50 ms and the message from the queue channel passed to the ObjectToStringTransformer
after the transformer the converted message is passed to the CSVProcessingService for further processing
Did I achieve all those goals correctly or did I make a mistake because I misunderstood Spring Integration? Would it be possible to combine the Poller and the #ServiceActivator somehow?
Futhermore, I have a problem visualizing how my configured IntegrationFlow actually "flows", maybe somebody can help me to better understand this.
EDIT:
I reworked my configuration after Artems comment. It now look like this:
#Configuration
#EnableIntegration
public class TCPSocketServerConfig
{
#Value("${tcp.socket.server.port}") int port;
#Bean
public IntegrationFlow server(
final CSVProcessingService csvProcessingService
)
{
return IntegrationFlows.from(
Tcp.inboundAdapter(
tcpNioServer()
)
.autoStartup(true)
.errorChannel(errorChannel())
)
.transform(new ObjectToStringTransformer())
.handle(csvProcessingService)
.get();
}
#Bean
public AbstractServerConnectionFactory tcpNioServer()
{
return Tcp.nioServer(port)
.deserializer(serializer())
.leaveOpen(true)
.taskExecutor(
new ThreadPoolExecutor(0, 20,
30L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new DefaultThreadFactory("TCP-POOL"))
).get();
}
#Bean
public MessageChannel errorChannel()
{
return MessageChannels.direct("errors").get();
}
#Bean
public IntegrationFlow errorHandling()
{
return IntegrationFlows.from(errorChannel()).log(LoggingHandler.Level.DEBUG).get();
}
#Bean
public ByteArrayLfSerializer serializer()
{
final ByteArrayLfSerializer serializer = new ByteArrayLfSerializer();
serializer.setMaxMessageSize(10240);
return serializer;
}
}
I also removed the #ServiceActivator annotation form the CSVProcessingService#process method.
Not sure what confuses you, but your configuration and logic looks good.
You may miss the fact that you don't need a QueueChannel in between, since an AbstractConnectionFactory.processNioSelections() is already multi-threaded and it schedules a task to read a message from the socket. So, you only need is to configure an appropriate Executor for Tcp.nioServer(). Although it is an Executors.newCachedThreadPool() by default anyway.
On the other hand with in-memory QueueChannel you indeed may lose messages because they are already read from the network.
When you do Java DSL, you should consider to use poller() option on the endpoint. The #Poller will work on the #ServiceActivator if you have inputChannel attribute over there, but using the same in the handle() will override that inputChannel, so your #Poller won't be applied. Don't confuse yourself with mixing Java DSL and annotation configuration!
Everything else is good in your configuration.

Spring SOAP Endpoint - Cache JaxbContext

I wrote a simple SOAP endpoint essentially following the Spring tutorial found here: https://spring.io/guides/gs/producing-web-service/
Below is the class which is used to intercept the requests (assume repository object injected):
#Endpoint
public class SampleEndpoint {
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "SampleRequest")
public
#ResponsePayload
JAXBElement<SampleResponseType> sampleQuery(
#RequestPayload JAXBElement<SampleRequestType> request) {
ObjectFactory factory = new ObjectFactory();
SampleResponseType response = repository.query(request.getValue());
JAXBElement<SampleResponseType> jaxbResponse = factory.createSampleResponse(response);
return jaxbResponse;
}
}
The service performs correctly. One issue I'm running into is performance, particularly with unmarshalling the response. On average it's taking seconds to unmarshall the object into an XML response. Is there a way to cache/inject the JaxbContext Spring is using for this process to improve on this time?
Here's the web service configuration file I'm using for this endpoint. I've tried alternating between Saaj and Axiom message factories but didn't see much performance change:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public SaajSoapMessageFactory soap12MessageFactory() {
SaajSoapMessageFactory factory = new SaajSoapMessageFactory();
factory.setSoapVersion(SoapVersion.SOAP_12);
return factory;
}
#Bean
public AxiomSoapMessageFactory axiomSoapMessageFactory() {
AxiomSoapMessageFactory factory = new AxiomSoapMessageFactory();
factory.setSoapVersion(SoapVersion.SOAP_12);
factory.setPayloadCaching(false);
return factory;
}
#Bean
public ServletRegistrationBean dispatcherServlet(
ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
servlet.setMessageFactoryBeanName("soap12MessageFactory");
return new ServletRegistrationBean(servlet, "/ws/*");
}
#Bean(name = "wsdlname")
public DefaultWsdl11Definition xcpdDefaultXcpdWsdl11Definition(XsdSchema
schema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setCreateSoap11Binding(false);
wsdl11Definition.setCreateSoap12Binding(true);
wsdl11Definition.setPortTypeName("xcpdPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition
.setTargetNamespace("http://somenamespace.org/");
wsdl11Definition.setSchema(schema);
return wsdl11Definition;
}
#Bean
public XsdSchema schema() {
return new SimpleXsdSchema(
new ClassPathResource(
"schema.xsd"));
}
}
We obtained a solution to the issue two days ago. The first issue we saw was that the client version of the JVM was installed on the server. I installed the server build of the JVM and changed Tomcat to use that instance. The performance of some of the web services improved dramatically. Previously simple requests were taking three seconds, now they are taking 250 ms. However when testing the Spring service I wrote, I didn't see much of an improvement.
To resolve this last issue, I added the following to the Java options for Tomcat:
-Dcom.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.fastBoot=true
After restarting the Tomcat instance, the response times of the services are now under one second each. Previously requests were taking up to 30 seconds to complete. The server JRE we used is the following:
http://www.oracle.com/technetwork/java/javase/downloads/server-jre8-downloads-2133154.html

Categories

Resources