No Association Found Error while indexing data to Solr using Spring-Data-Solr - solr

I am trying out a sample service application of spring data mongoDB + spring data solr where MongoDB is used to persist the data and solr for indexing and searching.
The save operation to MongoDB happens successfully in the service class. But on calling the SolrOperation save() method the service crashes with the error log as below:
SEVERE [com.sun.jersey.spi.container.ContainerResponse] (defaulttask-1)The
RuntimeException could not be mapped to a response, re-throwing the HTTP
container:org.springframework.data.solr.UncategorizedSolrException:No
association fond!; nested exception is java.lang.IllegalStateException: No
association found! at org.springframework.data.solr.core.SolrTemplate.execute(SolrTemplate.java:171)
As I analyse the log further deep it says:
Caused by: java.lang.IllegalStateException:No association found!
at org.springframework.data.mapping.PersistentProperty.getRequiredAssociation(PersistentProperty.java:166)
The line getConverter().write(bean, document) inside convertBeanToSolrInputDocument () inside SolrTemplate is throwing the error.
The DAO method
public String addToRepo(MyEntity myEntity){
mongoOperation.save(myEntity); //works fine data saved to MongoDB
solrOperation.save("collectionName",myEntity); //generates above exception
return "success";
}
I am using Spring 5 + solrj-6.1.0 + spring-data-solr-4.0.2.
The solroperation has been correctly loaded as:
ApplicationContext SOLR_CONFIG_APP_CTX = new AnnotationConfigApplicationContext(SpringSolrConfig.class);
SolrOperations solrOperation = (SolrOperations)ctx.getBean("solrTemplate");
public static final SolrOperations SOLR_OPS=
(SolrOperations)SOLR_CONFIG_APP_CTX.getBean("solrTemplate");
SpringSolrConfig.java
#Configuration
public class SpringSolrConfig extends AbstractSolrConfig {
public SolrClientFactory solrClientFactory (){
SolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
HttpSolrClientFactory solrClientFactory = new HttpSolrClientFactory (solrClient);
return solrClientFactory;
}
}
The SpringConfig.xml file looks like this:
<mongo:mongo host="195.168.1.140" port="27017"/>
<mongo:dbfactory dbname="myDB"/>
<bean id="mongoTemplate"
class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg-name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
<repositories base-package="sample.package.repositories"/>
<bean id="myEntityRepo" class="sample.package..repositories.MyEntityRepositoryInterface"/>
<solr:repositories base-package="sample.package.repositories"/>
<solr:sorl-server id="solrServer" url="http://localhost:8983/solr"/>
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg index="0" ref="solrServer"/>
</bean>
Thanks in advance for helping me troubleshoot this!

I updated my SpringSolrConfig file as below to fix the problem. Courtesy: https://jira.spring.io/browse/DATASOLR-394
#Configuration
public class SpringSolrConfig extends AbstractSolrConfig {
String solrUrl = "http://localhost:8983/solr/"; // TODO read this ideally from spring-configuration.xml file
public SolrClientFactory solrClientFactory (){
SolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
HttpSolrClientFactory solrClientFactory = new HttpSolrClientFactory (solrClient);
return solrClientFactory;
}
#Bean
public SolrTemplate solrTemplate () {
SolrTemplate solrTemplateObj = new SolrTemplate(solrClientFactory));
// This ensures that the default MappingSolrConverter.java is not used for converting the bean to a Solr Document before indexing
solrTemplateObj.setSolrConverter(new SolrJConverter());
return solrTemplateObj;
}
}

Related

MediaType is always application/json in MessageBodyWriter in Quarkus with quarkus-resteasy-reactive

Im migrating a JAX-RS application to Quarkus using the resteasy-reactive-jackson extension. One of the resource methods should return an Excel document if the Accept-header is application/vnd.ms-excel. The Excel document is created in a MessageBodyWriter<>. This works as expected in the old application (KumuluzEE, Jersey).
My requests are successfully routed to the resource method, the Accept-header is present but when the response entity arrives at my implementation of isWriteable in the MessageBodyWriter<> the mediaType parameter is always application/json. I have tried implementing a ServerMessageBodyWriter<> but that did not make any difference.
Any ideas of whats going on?
Im using Quarkus v2.2.
Edit 2:
The service interface is in it's own Maven module:
#Path("")
#Produces(MediaType.APPLICATION_JSON)
#RegisterRestClient
#RegisterClientHeaders
public interface MyResource {
#GET
#Path("{id}")
// #Produces({"application/vnd.ms-excel", MediaType.APPLICATION_JSON}) // Works
#Produces({MediaType.APPLICATION_JSON, "application/vnd.ms-excel"}) // Does not work
Response getData(#PathParam("id") Long id);
}
The resource implementation and MessageBodyWriter:
public class MyResourceImpl implements MyResource {
#Context
HttpHeaders httpHeaders; // getAcceptableMediaTypes() returns mediatypes
// matching Accept-header as expected
#Override
public Response getData(#PathParam("id") Long id) {
return Response.ok().entity(new MyData()).build();
}
}
#Provider
#Produces({"application/vnd.ms-excel"})
public class ExcelMessageBodyWriter implements MessageBodyWriter<MyData> {
#Override
public boolean isWriteable(Class<?> aClass, Type type,
Annotation[] annotations, MediaType mediaType) {
// mediaType is always MediaType.APPLICATION_JSON_TYPE when JSON
// is listed first in #Produces in service interface
return aClass == MyData.class && mediaType.getType().equals("application")
&& mediaType.getSubtype().equals("vnd.ms-excel");
}
...
}
Changing #Produces({MediaType.APPLICATION_JSON, "application/vnd.ms-excel"}) on the resource method to #Produces({"application/vnd.ms-excel", MediaType.APPLICATION_JSON}) solved my problem. This can't be the expected behaviour?

Spring Boot JPA: How to avoid creating multiple repositories for identical databases?

My spring boot mvc project interacts with a database via a repository interface, which works nicely using Spring boot default configurations:
spring:
datasource:
url: jdbc:mysql://localhost/some_schema
username:
...
#Configuration
#EnableJpaRepositories(basePackages = {"my.path.to.repository"})
public class Application extends WebMvcConfigurerAdapter {
....
Now depending on some runtime condition, I need to interact with an identical second database (same schema) in a separate location. The solutions I found all point to creating a separate repository package per datasource.
Since the databases are identical, however, is there an elegant way to avoid duplicating the repository package for each added datasource?
You can accomplish this with a Spring AbstractRoutingDataSource.
Roughly:
public class ChooseOneDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
if (***some runtime condition***) {
return "dataSource1";
} else {
return "dataSource2";
}
}
}
And in your conguration:
#Bean
#ConfigurationProperties(prefix = "dataSource1")
DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "dataSource2")
DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
#Bean
DataSource dataSource() {
AbstractRoutingDataSource dataSource = new ChooseOneDataSource();
Map<Object,Object> resolvedDataSources = new HashMap<>();
resolvedDataSource.put("dataSource1", dataSource1());
resolvedDataSource.put("dataSource2", dataSource2());
dataSource.setDefaultTargetDataSource(dataSource1()); // << default
dataSource.setTargetDataSources(resolvedDataSources);
return dataSource;
}
For more info/examples:
http://fizzylogic.nl/2016/01/24/Make-your-Spring-boot-application-multi-tenant-aware-in-2-steps/
https://spring.io/blog/2007/01/23/dynamic-datasource-routing/

Error Validating Signed MTOM Message CXF 3.0.6 and up

I created a simple web service using CXF that has MTOM enabled, it also expects a time stamp and the body to be signed, it configured like this:
#ComponentScan(basePackageClasses={MyService.class})
#Configuration
#ImportResource({ "classpath:META-INF/cxf/cxf.xml" })
public class CXFConfig {
#Autowired
Bus cxfBus;
#Autowired
MyService ws;
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(cxfBus, ws);
endpoint.publish("/MyService");
SOAPBinding binding = (SOAPBinding)endpoint.getBinding();
binding.setMTOMEnabled(true);
Map<String, Object> inProps = new HashMap<String, Object>();
inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.SIGNATURE+" "+WSHandlerConstants.TIMESTAMP);
inProps.put(WSHandlerConstants.SIG_PROP_FILE, "wsserver.properties");
WSS4JInInterceptor inc = new WSS4JInInterceptor(inProps);
endpoint.getInInterceptors().add(inc);
return endpoint;
}
}
My Service Interface is:
#WebService
#Component
public interface MyService {
#WebMethod(action="doStuff")
public String doStuff(#WebParam(name="FileData") MTOMMessage message) throws IOException;
}
My Data Type is:
#XmlType
#XmlAccessorType(XmlAccessType.FIELD)
public class MTOMMessage {
#XmlElement(name = "data", required = true)
#XmlMimeType("text/xml")
protected DataHandler data;
#XmlElement(name = "FileName", required = true)
protected String fileName;
//Getters and Setters
}
I then have a client to call it:
public static void main(String[] args) throws IOException {
String xmlLoc = "classpath:com/avum/dasn/ws/test/client-context.xml";
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(xmlLoc);
MyService svc = ctx.getBean(MyService.class);
MTOMMessage msg = new MTOMMessage();
msg.setXmlData(new DataHandler(getURLForTestFile()));
msg.setFileName("TestFileName");
System.out.println(svc.doStuff(msg));
}
The client-context.xml looks like this:
<jaxws:properties>
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="Signature Timestamp"/>
<entry key="signaturePropFile" value="wsclient.properties"/>
<entry key="user" value="ws-security" />
<entry key="passwordCallbackClass" value="com.co.test.PasswordCallbackHandler"/>
</map>
</constructor-arg>
</bean>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
</jaxws:outInterceptors>
If I’m using CXF version 3.0.5 or lower this works fine. However if I use 3.0.6 or later I get “A security error was encountered when verifying the message.”. On the server I’m getting messages like “Couldn't validate the References”. This is because the server doesn’t get the same DigestValue that comes across in the ds:DigestValue element.
I think it has something to do with the way MTOM message are handled by the server side code because if I disable MTOM (on the client and server) then it works fine. I’m not sure how to get this working in later versions of CXF. Does anyone have any ideas what I’m doing wrong?
Thanks
David

Spring-boot application not finding index.html

I am wiring a AngularJS and spring-boot application together by hand for the first time. The issues I am running into is my #RestController is not returning the index page:
#RestController
public class IndexController {
#RequestMapping("/")
public String index(){
System.out.println("Looking in the index controller.........");
return "index";
}
}
Directory:
It keeps rendering the default 404 error page:
----------------UPDATE 1------------------
I have added a configuration file:
#Configuration
public class IndexPageConfiguration {
#Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/app/");
resolver.setSuffix(".html");
return resolver;
}
}
RestController
#RestController
public class IndexController {
#RequestMapping("/")
public String index(){
System.out.println("Looking in the index controller.........");
return "index";
}
}
main class:
#SpringBootApplication(scanBasePackages = { "com.serviceImpl","com.service","com.config" },exclude = { ErrorMvcAutoConfiguration.class })
public class SpringCrudApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCrudApplication.class, args);
}
}
The above main class is still returning the default 404 error page.
On the other hand, Spring will automatically look for the index.html page if you put it directly under webapp folder. So you don't need any configuration.
This is just another way to do it.
You need to configure InternalRosourceViewResolver to let the spring know your jsp location
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/app/");
resolver.setSuffix(".html");
return resolver;
}
So Spring will append and append location and suffix to your View returned.
I think it is good idea to keep your views separately in any other folder and configure your folder location according to it.
If you want to continue with your current set up
you should return "/app/index.html" from your controller.
Spring boot provides White label error page to hide your stack trace when a Server side error/ exception occurs, this will help us from protecting our code from intruders.
If you want to get rid of white label error.
In your #SpringBootApplication specify excludes ErrorMvcAutoConfiguration.class
#SpringBootApplication(scanBasePackages = { "com.ekart.app" }, exclude = { ErrorMvcAutoConfiguration.class })
If you are not using #SpringBootApplication annotatio, you should supply same same excludes in #EnableAutoConfiguration annotation

How to configure Spring and Angular to work together

I have a spring REST server (v3.2) and AngularJS for the client code.
From my understanding in the basic scenario the user navigates to the base domain .com, index.html is being sent back and
and from that point Angular manages the communication.
My questions are:
1. How to set Spring to return the Angular file.
2. How to handle a situation where the user does not go though the base domain and just navigates to
.com/books/moby-dick which currently returns a JSON representation of the Moby-Dick book that was suppose
to be rendered by the client
A good tutorial will be highly appreciated.
This is my web initialzer class:
public class WebAppInitializer implements WebApplicationInitializer {
private static Logger LOG = LoggerFactory.getLogger(WebAppInitializer.class);
#Override
public void onStartup(ServletContext servletContext) {
WebApplicationContext rootContext = createRootContext(servletContext);
configureSpringMvc(servletContext, rootContext);
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", CORSFilter.class);
corsFilter.addMappingForUrlPatterns(null, false, "/*");
// configureSpringSecurity(servletContext, rootContext);
}
private WebApplicationContext createRootContext(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
// rootContext.register(CoreConfig.class, SecurityConfig.class);
rootContext.register(CoreConfig.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
return rootContext;
}
private void configureSpringMvc(ServletContext servletContext, WebApplicationContext rootContext) {
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
mvcContext.register(MVCConfig.class);
mvcContext.setParent(rootContext);
ServletRegistration.Dynamic appServlet = servletContext.addServlet(
"webservice", new DispatcherServlet(mvcContext));
appServlet.setLoadOnStartup(1);
Set<String> mappingConflicts = appServlet.addMapping("/");
if (!mappingConflicts.isEmpty()) {
for (String s : mappingConflicts) {
LOG.error("Mapping conflict: " + s);
}
throw new IllegalStateException(
"'webservice' cannot be mapped to '/'");
}
}
This is my MVC configuration file:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.yadazing.rest.controller"})
public class MVCConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
(disclaimer: I am the author of JHipster)
You can have a look at JHipster which will generate such an application for you, with a Spring backend and an AngularJS frontend.
As the generator goes far beyond what you need (security, etc), you can also have a look at our sample application.
How about this then for #1:
registry.addResourceHandler("/index.html").addResourceLocations("/index.html");

Resources