Mix Spring Data Solr and SolrJ in Solr Cloud 5


We are migrating from Solr 4(part of DataStax) to Solr Cloud 5(without Datastax). 
In previous code, we are using Spring Data Solr to talk with Solr, we are using SolrCrudRepository and also map closed set value to java enum class(not use solr.EnumField), map multiple value field to Set.

As Spring Data Solr doesn't work with Solr Cloud 5, we have to replace the DAO layer(SolrCrudRepository) to Use SolrJ directly.
But SolrJ doesn't support map field to Java Enum or map multiple value field to Set.

We don't want to change the Enum to String, change set to List, as if so, we need change a lot of code. 

We want keep the Model class untouched. So I decided to mix them: Use Spring Data Solr to convert between SolrInputDocument and Java object, use SolrJ to update or query Solr Server.
public abstract class XModelRepository<T extends XModel> implements IXModelRepository<T> {
    @Value("${solr.server.core.collection_name}")
    private String collection;

    @Autowired
    protected SolrClient solrServer;

    @Autowired
    protected SolrConverter converter;

    public final void save(final XModel model) {
        try {
            final Iterable<SolrInputDocument> docs = converter.write(Lists.newArrayList(model));
            final UpdateResponse response = solrServer.add(getCollection(), docs.iterator());
            softCommit();
        } catch (IOException | SolrServerException e) {
            throw new WrappedException(ErrorCode.INTERNAL_ERROR, e);
        }
    }
    public XModel findOne(final String id) {
        try {
            final SolrDocument doc = solrServer.getById(collection, id);
            return converter.read(XModel.class, doc);
        } catch (SolrServerException | IOException e) {
            throw new WrappedException(ErrorCode.INTERNAL_ERROR, e);
        }
    }
} 

This works in most case. But there is one issue: when it stores enum to solr, it saves something like xx.enums.UserType:TypeA, when it reads from solr server, tries to convert the string to enum, it fails with  the following error.
Java.lang.IllegalArgumentException: No enum constant xx.enums.UserType:TypeA
at java.lang.Enum.valueOf(Enum.java:236)


To fix this, I have to copy Spring org.springframework.core.convert.support.EnumToStringConverter and register it in SolrConverter in the configuration class like below:
    @Bean
    public static SolrConverter mappingSolrConverter() {
        final MappingContext<? extends SolrPersistentEntity<?>, SolrPersistentProperty> mappingContext =
                new SimpleSolrMappingContext();
        final MappingSolrConverter converter = new MappingSolrConverter(mappingContext);

        final List<Object> converters = new ArrayList<Object>();
        final DefaultConversionService conversionService = new DefaultConversionService();

        converters.add(new EnumToStringConverter(conversionService));

        final CustomConversions customConversions = new CustomConversions(converters) {
            @Override
            public boolean isSimpleType(final Class<?> clazz) {
                if (Enum.class.equals(clazz) || Enum.class.isAssignableFrom(clazz)) {
                    return false;
                }
                return super.isSimpleType(clazz);
            }

        };
        converter.setCustomConversions(customConversions);
        return converter;
    }
 // Copied from Spring org.springframework.core.convert.support.EnumToStringConverter 
 final class EnumToStringConverter implements Converter<Enum<?>, String>, ConditionalConverter {
     private final ConversionService conversionService;

     public EnumToStringConverter(final ConversionService conversionService) {
         this.conversionService = conversionService;
     }

     public boolean matches(final TypeDescriptor sourceType, final TypeDescriptor targetType) {
         for (@SuppressWarnings("rawtypes")
         final Class interfaceType : ClassUtils.getAllInterfacesForClass(sourceType.getType())) {
             if (this.conversionService.canConvert(TypeDescriptor.valueOf(interfaceType), targetType)) {
                 return false;
             }
         }
         return true;
     }

     public String convert(final Enum<?> source) {
         return source.name();
     }
 }

Labels

adsense (5) Algorithm (69) Algorithm Series (35) Android (7) ANT (6) bat (8) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) Java (186) JavaScript (27) JSON (7) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) regex (5) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) xml (5)