问题
I keep running into an issue when making a spring-data-solr query from within my play framework v2.3.8 application. The query seems to succeed but then the class loader spring is using can't find the class files for my data model classes and throws a NoClassDefFoundError
. Here is some relevant code.
First the data model in package 'model':
package model;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.annotation.Id;
public class Theatre
{
@Id
String id;
@Field
String docType;
// other fields, getters, and setters omitted for brevity
}
Here is the repository:
package model;
import java.util.List;
import org.springframework.data.solr.repository.SolrCrudRepository;
public interface TheatreRepository extends SolrCrudRepository<Theatre, String>
{
List<Theatre> findByDocType(String doc__type);
void delete(Theatre deleted);
List<Theatre> findAll();
Theatre findOne(String id);
@SuppressWarnings("unchecked")
Theatre save(Theatre persisted);
}
The Application class in play framework:
package controllers;
import java.util.ArrayList;
import java.util.List;
import model.Theatre;
import model.TheatreRepository;
import play.*;
import plugins.solr.SolrPlugin;
import views.html.*;
public class Application extends Controller {
public static Result index() {
TheatreRepository repository = Play.application().plugin( SolrPlugin.class ).getTheatreRepository();
if( repository != null )
{
List<Theatre> allTheatres = null;
try
{
allTheatres = repository.findByDocType("theatre");
Logger.info( "List of theatres found: " + allTheatres.toString() );
for(Theatre theatre: allTheatres)
{
Logger.info( "This theatre is: " + theatre.toString() );
}
}
catch( NoClassDefFoundError | ClassNotFoundException e )
{
Logger.error( "Classpath: " + System.getProperty( "java.class.path" ) );
}
}
else
{
Logger.error( "TheatreRepository is NULL!" );
}
return ok(index.render("Your new application is ready."));
}
}
And the ApplicationContext for spring:
package plugins.solr;
import play.Plugin;
import model.TheatreRepository;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import play.Logger;
import play.Play;
import play.api.Application;
public class SolrPlugin extends Plugin
{
private TheatreRepository theatreRepository;
ApplicationContext ctx;
public SolrPlugin(Application app)
{
}
public TheatreRepository getTheatreRepository()
{
return theatreRepository;
}
@Override
public void onStart()
{
super.onStart();
Logger.info( "Solr plugin started" );
ctx = new AnnotationConfigApplicationContext(SolrConfig.class);
theatreRepository = ctx.getBean( TheatreRepository.class );
}
}
Here is the JavaConfig based spring configuration:
package plugins.solr;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.solr.core.SolrOperations;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.repository.config.EnableSolrRepositories;
import org.springframework.data.solr.server.support.HttpSolrServerFactory;
import play.Play;
@ComponentScan
@Configuration
@EnableSolrRepositories(basePackages = { "model" })
public class SolrConfig
{
public SolrConfig()
{
}
@Bean
public SolrServer solrServer()
{
HttpSolrServerFactory factory = new HttpSolrServerFactory( new HttpSolrServer( Play.application().configuration().getString( "ems.solr.url" ) ) );
return factory.getSolrServer();
}
@Bean
public SolrOperations solrTemplate()
{
return new SolrTemplate( this.solrServer() );
}
}
So at run time Application.index()
gets called when reloading the browser window and as a result the call to theatreRepository.findByDocType("theatre");
gets executed. This is the stack trace from that call:
java.lang.NoClassDefFoundError: model/Theatre
at model.Theatre_Instantiator_982rn.newInstance(Unknown Source) ~[na:na]
at org.springframework.data.convert.BytecodeGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(BytecodeGeneratingEntityInstantiator.java:193) ~[spring-data-commons-1.10.0.RELEASE.jar:na]
at org.springframework.data.convert.BytecodeGeneratingEntityInstantiator.createInstance(BytecodeGeneratingEntityInstantiator.java:76) ~[spring-data-commons-1.10.0.RELEASE.jar:na]
at org.springframework.data.solr.core.convert.MappingSolrConverter.read(MappingSolrConverter.java:127) ~[spring-data-solr-1.4.0.RELEASE.jar:na]
at org.springframework.data.solr.core.convert.MappingSolrConverter.read(MappingSolrConverter.java:119) ~[spring-data-solr-1.4.0.RELEASE.jar:na]
Caused by: java.lang.ClassNotFoundException: model.Theatre
at java.lang.ClassLoader.findClass(ClassLoader.java:531) ~[na:1.7.0_80]
at java.lang.ClassLoader.loadClass(ClassLoader.java:425) ~[na:1.7.0_80]
at java.lang.ClassLoader.loadClass(ClassLoader.java:358) ~[na:1.7.0_80]
at model.Theatre_Instantiator_982rn.newInstance(Unknown Source) ~[na:na]
at org.springframework.data.convert.BytecodeGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(BytecodeGeneratingEntityInstantiator.java:193) ~[spring-data-commons-1.10.0.RELEASE.jar:na]
What I have noticed is that the model.Theatre
class files are in play frameworks class loader but not in the system class loader. However, this doesn't seem like it should be a problem because spring was able to find the TheatreRespoitory
class file when it needed to instantiate an instance of that class. I figure there must be something simple that I'm doing wrong but after trying a few things and doing research I'm still at a loss.
I did find the following post but it doesn't seem to be relevant since it is referencing a difference between versions of spring-data-mongodb.
MongoDB NoClassDefFoundError
Also, I just tried using the play frameworks class loader in the spring application context by doing the following in SolrPlugin.onStart()
ctx = new AnnotationConfigApplicationContext(SolrConfig.class);
((AnnotationConfigApplicationContext)ctx).setClassLoader( Play.application().classloader() );
This resulted in the same NoClassDefFoundError
and stack trace. I also tried making the query in this same function, right after setting the class loader, and received the same error again.
回答1:
A workaround is to force ReflectionEntityInstantiator
in MappingMongoConverter
which reverts to pre 1.7.0.RELEASE instantiation strategy based on reflection. For example:
mappingConverter.setInstantiators(
new EntityInstantiators(ReflectionEntityInstantiator.INSTANCE));
来源:https://stackoverflow.com/questions/30632240/noclassdeffounderror-when-making-a-query-in-spring-data-solr-within-a-play-frame