07、Solr源码:SolrSPI

Solr4.8.0源码分析(7)之Solr SPI

查看Solr源码时候会发现,每一个package都会由对应的resources. 如下图所示:

*

一时对这玩意好奇了,看了文档以后才发现,这个services就是java SPI机制。首先介绍下java SPI机制,然后再结合Solr谈一下SPI。

1. JAVA SPI

当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。

jdk提供服务实现查找的一个工具类:java.util.ServiceLoader

假设有一个内容搜索系统,分为展示和搜索两个模块。展示和搜索基于接口编程。搜索的实现可能是基于文件系统的搜索,也可能是基于数据库的搜索。实例代码如下:

Search.java: 搜索接口

1 package search;
2 
3 import java.util.List;
4 
5 import definition.Doc;
6 
7 public interface Search {
8     List<Doc> search(String keyword);
9 }

FileSearch.java:文件系统的搜索实现

 1 package search;
 2 
 3 import java.util.List;
 4 
 5 import definition.Doc;
 6 
 7 public class FileSearch implements Search {
 8 
 9     @Override
10     public List<Doc> search(String keyword) {
11         System.out.println("now use file system search. keyword:" + keyword);
12         return null;
13     }
14 
15 }

DatabaseSearch.java

 1 package search;
 2 
 3 import java.util.List;
 4 
 5 import definition.Doc;
 6 
 7 public class DatabaseSearch implements Search {
 8 
 9     @Override
10     public List<Doc> search(String keyword) {
11         System.out.println("now use database search. keyword:" + keyword);
12         return null;
13     }
14 
15 }

SearchTest.java

 1 package search;
 2 
 3 import java.util.Iterator;
 4 import java.util.ServiceLoader;
 5 
 6 public class SearchTest {
 7 
 8     public static void main(String[] args) {
 9         ServiceLoader<Search> s = ServiceLoader.load(Search.class);
10         Iterator<Search> searchs = s.iterator();
11         if (searchs.hasNext()) {
12             Search curSearch = searchs.next();
13             curSearch.search("test");
14         }
15     }
16 }

最后创建在META-INF/searvices/search.Search文件。

当search.Search文件内容是"search.FileSearch"时,程序输出是:

nowuse file system search. keyword:test

当search.Search文件内容是"search.DatabaseSearch"时,程序输出是:

nowuse database search. keyword:test
可以看出SearchTest里没有任何和具体实现有关的代码,而是基于spi的机制去查找服务的实现。

2. Solr SPI

以Codec类为例,查看resources/META-INF/services/org.apache.lucene.codecs.Codec:可以看出Codec服务接口具有以下具体的实现类。这就很好的解释了Solrconfig.xml里面的LuceneVersion的配置,也为Lucene的向前兼容提供了保障。

 1 #  Licensed to the Apache Software Foundation (ASF) under one or more
 2 #  contributor license agreements.  See the NOTICE file distributed with
 3 #  this work for additional information regarding copyright ownership.
 4 #  The ASF licenses this file to You under the Apache License, Version 2.0
 5 #  (the "License"); you may not use this file except in compliance with
 6 #  the License.  You may obtain a copy of the License at
 7 #
 8 #       http://www.apache.org/licenses/LICENSE-2.0
 9 #
10 #  Unless required by applicable law or agreed to in writing, software
11 #  distributed under the License is distributed on an "AS IS" BASIS,
12 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 #  See the License for the specific language governing permissions and
14 #  limitations under the License.
15 
16 org.apache.lucene.codecs.lucene40.Lucene40Codec
17 org.apache.lucene.codecs.lucene3x.Lucene3xCodec
18 org.apache.lucene.codecs.lucene41.Lucene41Codec
19 org.apache.lucene.codecs.lucene42.Lucene42Codec
20 org.apache.lucene.codecs.lucene45.Lucene45Codec
21 org.apache.lucene.codecs.lucene46.Lucene46Codec

接下来可以看下Codec服务接口的实现代码

  1 package org.apache.lucene.codecs;
  2 
  3 /*
  4  * Licensed to the Apache Software Foundation (ASF) under one or more
  5  * contributor license agreements.  See the NOTICE file distributed with
  6  * this work for additional information regarding copyright ownership.
  7  * The ASF licenses this file to You under the Apache License, Version 2.0
  8  * (the "License"); you may not use this file except in compliance with
  9  * the License.  You may obtain a copy of the License at
 10  *
 11  *     http://www.apache.org/licenses/LICENSE-2.0
 12  *
 13  * Unless required by applicable law or agreed to in writing, software
 14  * distributed under the License is distributed on an "AS IS" BASIS,
 15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 import java.util.Set;
 21 import java.util.ServiceLoader; // javadocs
 22 
 23 import org.apache.lucene.index.IndexWriterConfig; // javadocs
 24 import org.apache.lucene.util.NamedSPILoader;
 25 
 26 /**
 27  * Encodes/decodes an inverted index segment.
 28  * <p>
 29  * Note, when extending this class, the name ({@link #getName}) is 
 30  * written into the index. In order for the segment to be read, the
 31  * name must resolve to your implementation via {@link #forName(String)}.
 32  * This method uses Java's 
 33  * {@link ServiceLoader Service Provider Interface} (SPI) to resolve codec names.
 34  * <p>
 35  * If you implement your own codec, make sure that it has a no-arg constructor
 36  * so SPI can load it.
 37  * @see ServiceLoader
 38  */
 39 public abstract class Codec implements NamedSPILoader.NamedSPI {
 40   
 41   private static final NamedSPILoader<Codec> loader =
 42     new NamedSPILoader<>(Codec.class);
 43 
 44   private final String name;
 45 
 46   /**
 47    * Creates a new codec.
 48    * <p>
 49    * The provided name will be written into the index segment: in order to
 50    * for the segment to be read this class should be registered with Java's
 51    * SPI mechanism (registered in META-INF/ of your jar file, etc).
 52    * @param name must be all ascii alphanumeric, and less than 128 characters in length.
 53    */
 54   protected Codec(String name) {
 55     NamedSPILoader.checkServiceName(name);
 56     this.name = name;
 57   }
 58   
 59   /** Returns this codec's name */
 60   @Override
 61   public final String getName() {
 62     return name;
 63   }
 64   /**
 65    * 以下几个Format跟Lucene的索引文件格式有关
 66    * */
 67   /** Encodes/decodes postings */
 68   public abstract PostingsFormat postingsFormat();
 69 
 70   /** Encodes/decodes docvalues */
 71   public abstract DocValuesFormat docValuesFormat();
 72   
 73   /** Encodes/decodes stored fields */
 74   public abstract StoredFieldsFormat storedFieldsFormat();
 75   
 76   /** Encodes/decodes term vectors */
 77   public abstract TermVectorsFormat termVectorsFormat();
 78   
 79   /** Encodes/decodes field infos file */
 80   public abstract FieldInfosFormat fieldInfosFormat();
 81   
 82   /** Encodes/decodes segment info file */
 83   public abstract SegmentInfoFormat segmentInfoFormat();
 84   
 85   /** Encodes/decodes document normalization values */
 86   public abstract NormsFormat normsFormat();
 87 
 88   /** Encodes/decodes live docs */
 89   public abstract LiveDocsFormat liveDocsFormat();
 90   
 91   /**
 92    * 根据名字在已有的Codec实例中寻找符合
 93    * */
 94   /** looks up a codec by name */
 95   public static Codec forName(String name) {
 96     if (loader == null) {
 97       throw new IllegalStateException("You called Codec.forName() before all Codecs could be initialized. "+
 98           "This likely happens if you call it from a Codec's ctor.");
 99     }
100     return loader.lookup(name);
101   }
102   
103   /**
104    * 返回有效的Codecs实例
105    * */
106   /** returns a list of all available codec names */
107   public static Set<String> availableCodecs() {
108     if (loader == null) {
109       throw new IllegalStateException("You called Codec.availableCodecs() before all Codecs could be initialized. "+
110           "This likely happens if you call it from a Codec's ctor.");
111     }
112     return loader.availableServices();
113   }
114   
115   /**
116    * 更新Codec实例列表,Codec实例列表只能添加,不能删除与更改。
117    * */
118   /** 
119    * Reloads the codec list from the given {@link ClassLoader}.
120    * Changes to the codecs are visible after the method ends, all
121    * iterators ({@link #availableCodecs()},...) stay consistent. 
122    * 
123    * <p><b>NOTE:</b> Only new codecs are added, existing ones are
124    * never removed or replaced.
125    * 
126    * <p><em>This method is expensive and should only be called for discovery
127    * of new codecs on the given classpath/classloader!</em>
128    */
129   public static void reloadCodecs(ClassLoader classloader) {
130     loader.reload(classloader);
131   }
132   
133   /**
134    * 默认为Lucene46,也就是说默认调用的是org.apache.lucene.codecs.lucene46.Lucene46Codec
135    * */
136   private static Codec defaultCodec = Codec.forName("Lucene46");
137   
138   /**
139    * 返回默认的Codec实例
140    * */
141   /** expert: returns the default codec used for newly created
142    *  {@link IndexWriterConfig}s.
143    */
144   // TODO: should we use this, or maybe a system property is better?
145   public static Codec getDefault() {
146     return defaultCodec;
147   }
148   
149   /**
150    * 设置默认的Codec实例
151    * */
152   /** expert: sets the default codec used for newly created
153    *  {@link IndexWriterConfig}s.
154    */
155   public static void setDefault(Codec codec) {
156     defaultCodec = codec;
157   }
158 
159   /**
160    * returns the codec's name. Subclasses can override to provide
161    * more detail (such as parameters).
162    */
163   @Override
164   public String toString() {
165     return name;
166   }
167 }

代码比较简单明了,接下来再看下NamedSPILoader.NamedSPI,它封装了JAVA SPI的实现:

  1 package org.apache.lucene.util;
  2 
  3 /*
  4  * Licensed to the Apache Software Foundation (ASF) under one or more
  5  * contributor license agreements.  See the NOTICE file distributed with
  6  * this work for additional information regarding copyright ownership.
  7  * The ASF licenses this file to You under the Apache License, Version 2.0
  8  * (the "License"); you may not use this file except in compliance with
  9  * the License.  You may obtain a copy of the License at
 10  *
 11  *     http://www.apache.org/licenses/LICENSE-2.0
 12  *
 13  * Unless required by applicable law or agreed to in writing, software
 14  * distributed under the License is distributed on an "AS IS" BASIS,
 15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 import java.util.Collections;
 21 import java.util.Iterator;
 22 import java.util.Map;
 23 import java.util.LinkedHashMap;
 24 import java.util.Set;
 25 import java.util.ServiceConfigurationError;
 26 
 27 /**
 28  * Helper class for loading named SPIs from classpath (e.g. Codec, PostingsFormat).
 29  * @lucene.internal
 30  */
 31 public final class NamedSPILoader<S extends NamedSPILoader.NamedSPI> implements Iterable<S> {
 32 
 33   /**
 34    * SPI service Map,存放服务对应的实例类。
 35    * */
 36   private volatile Map<String,S> services = Collections.emptyMap();
 37   private final Class<S> clazz;
 38 
 39   public NamedSPILoader(Class<S> clazz) {
 40     this(clazz, Thread.currentThread().getContextClassLoader());
 41   }
 42   
 43   public NamedSPILoader(Class<S> clazz, ClassLoader classloader) {
 44     this.clazz = clazz;
 45     // if clazz' classloader is not a parent of the given one, we scan clazz's classloader, too:
 46     final ClassLoader clazzClassloader = clazz.getClassLoader();
 47     if (clazzClassloader != null && !SPIClassIterator.isParentClassLoader(clazzClassloader, classloader)) {
 48       reload(clazzClassloader);
 49     }
 50     reload(classloader);
 51   }
 52   
 53   /**
 54    * 更新SPI MAP services。遍历META-INF/services文件,如果services MAP没有该实例,则新建实例,并放入services MAP
 55    * */
 56   /** 
 57    * Reloads the internal SPI list from the given {@link ClassLoader}.
 58    * Changes to the service list are visible after the method ends, all
 59    * iterators ({@link #iterator()},...) stay consistent. 
 60    * 
 61    * <p><b>NOTE:</b> Only new service providers are added, existing ones are
 62    * never removed or replaced.
 63    * 
 64    * <p><em>This method is expensive and should only be called for discovery
 65    * of new service providers on the given classpath/classloader!</em>
 66    */
 67   public synchronized void reload(ClassLoader classloader) {
 68     final LinkedHashMap<String,S> services = new LinkedHashMap<>(this.services);
 69     final SPIClassIterator<S> loader = SPIClassIterator.get(clazz, classloader);
 70     while (loader.hasNext()) {
 71       final Class<? extends S> c = loader.next();
 72       try {
 73         final S service = c.newInstance();
 74         final String name = service.getName();
 75         // only add the first one for each name, later services will be ignored
 76         // this allows to place services before others in classpath to make 
 77         // them used instead of others
 78         if (!services.containsKey(name)) {
 79           checkServiceName(name);
 80           services.put(name, service);
 81         }
 82       } catch (Exception e) {
 83         throw new ServiceConfigurationError("Cannot instantiate SPI class: " + c.getName(), e);
 84       }
 85     }
 86     this.services = Collections.unmodifiableMap(services);
 87   }
 88   
 89   /**
 90    * Validates that a service name meets the requirements of {@link NamedSPI}
 91    */
 92   public static void checkServiceName(String name) {
 93     // based on harmony charset.java
 94     if (name.length() >= 128) {
 95       throw new IllegalArgumentException("Illegal service name: '" + name + "' is too long (must be < 128 chars).");
 96     }
 97     for (int i = 0, len = name.length(); i < len; i++) {
 98       char c = name.charAt(i);
 99       if (!isLetterOrDigit(c)) {
100         throw new IllegalArgumentException("Illegal service name: '" + name + "' must be simple ascii alphanumeric.");
101       }
102     }
103   }
104   
105   /**
106    * Checks whether a character is a letter or digit (ascii) which are defined in the spec.
107    */
108   private static boolean isLetterOrDigit(char c) {
109     return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9');
110   }
111   
112   /**
113    * 在Services MAP里面查找是否已有name的实例
114    * */
115   public S lookup(String name) {
116     final S service = services.get(name);
117     if (service != null) return service;
118     throw new IllegalArgumentException("A SPI class of type "+clazz.getName()+" with name '"+name+"' does not exist. "+
119      "You need to add the corresponding JAR file supporting this SPI to your classpath."+
120      "The current classpath supports the following names: "+availableServices());
121   }
122 
123   public Set<String> availableServices() {
124     return services.keySet();
125   }
126   
127   @Override
128   public Iterator<S> iterator() {
129     return services.values().iterator();
130   }
131   
132   /**
133    * Interface to support {@link NamedSPILoader#lookup(String)} by name.
134    * <p>
135    * Names must be all ascii alphanumeric, and less than 128 characters in length.
136    */
137   public static interface NamedSPI {
138     String getName();
139   }
140   
141 }

接下来看看Solr是怎么获取services的实例信息的

  1 package org.apache.lucene.util;
  2 
  3 /*
  4  * Licensed to the Apache Software Foundation (ASF) under one or more
  5  * contributor license agreements.  See the NOTICE file distributed with
  6  * this work for additional information regarding copyright ownership.
  7  * The ASF licenses this file to You under the Apache License, Version 2.0
  8  * (the "License"); you may not use this file except in compliance with
  9  * the License.  You may obtain a copy of the License at
 10  *
 11  *     http://www.apache.org/licenses/LICENSE-2.0
 12  *
 13  * Unless required by applicable law or agreed to in writing, software
 14  * distributed under the License is distributed on an "AS IS" BASIS,
 15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 import java.io.IOException;
 21 import java.io.InputStream;
 22 import java.io.BufferedReader;
 23 import java.io.InputStreamReader;
 24 import java.net.URL;
 25 import java.nio.charset.StandardCharsets;
 26 import java.util.ArrayList;
 27 import java.util.Collections;
 28 import java.util.Enumeration;
 29 import java.util.Iterator;
 30 import java.util.Locale;
 31 import java.util.NoSuchElementException;
 32 import java.util.ServiceConfigurationError;
 33 
 34 /**
 35  * Helper class for loading SPI classes from classpath (META-INF files).
 36  * This is a light impl of {@link java.util.ServiceLoader} but is guaranteed to
 37  * be bug-free regarding classpath order and does not instantiate or initialize
 38  * the classes found.
 39  *
 40  * @lucene.internal
 41  */
 42 public final class SPIClassIterator<S> implements Iterator<Class<? extends S>> {
 43   //service路径
 44   private static final String META_INF_SERVICES = "META-INF/services/";
 45 
 46   private final Class<S> clazz;
 47   private final ClassLoader loader;
 48   private final Enumeration<URL> profilesEnum;
 49   private Iterator<String> linesIterator;
 50   
 51   public static <S> SPIClassIterator<S> get(Class<S> clazz) {
 52     return new SPIClassIterator<>(clazz, Thread.currentThread().getContextClassLoader());
 53   }
 54   
 55   public static <S> SPIClassIterator<S> get(Class<S> clazz, ClassLoader loader) {
 56     return new SPIClassIterator<>(clazz, loader);
 57   }
 58   
 59   /** Utility method to check if some class loader is a (grand-)parent of or the same as another one.
 60    * This means the child will be able to load all classes from the parent, too. */
 61   public static boolean isParentClassLoader(final ClassLoader parent, ClassLoader child) {
 62     while (child != null) {
 63       if (child == parent) {
 64         return true;
 65       }
 66       child = child.getParent();
 67     }
 68     return false;
 69   }
 70   
 71   /**
 72    * 解析META-INF/services/clazz.getname文件
 73    * */
 74   private SPIClassIterator(Class<S> clazz, ClassLoader loader) {
 75     this.clazz = clazz;
 76     try {
 77       final String fullName = META_INF_SERVICES + clazz.getName();
 78       this.profilesEnum = (loader == null) ? ClassLoader.getSystemResources(fullName) : loader.getResources(fullName);
 79     } catch (IOException ioe) {
 80       throw new ServiceConfigurationError("Error loading SPI profiles for type " + clazz.getName() + " from classpath", ioe);
 81     }
 82     this.loader = (loader == null) ? ClassLoader.getSystemClassLoader() : loader;
 83     this.linesIterator = Collections.<String>emptySet().iterator();
 84   }
 85   
 86   /**
 87    * 获取META-INF/services/clazz.getname的clazz服务实例
 88    * */
 89   private boolean loadNextProfile() {
 90     ArrayList<String> lines = null;
 91     while (profilesEnum.hasMoreElements()) {
 92       if (lines != null) {
 93         lines.clear();
 94       } else {
 95         lines = new ArrayList<>();
 96       }
 97       final URL url = profilesEnum.nextElement();
 98       try {
 99         final InputStream in = url.openStream();
100         IOException priorE = null;
101         try {
102           final BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
103           String line;
104           while ((line = reader.readLine()) != null) {
105             final int pos = line.indexOf('#');
106             if (pos >= 0) {
107               line = line.substring(0, pos);
108             }
109             line = line.trim();
110             if (line.length() > 0) {
111               lines.add(line);
112             }
113           }
114         } catch (IOException ioe) {
115           priorE = ioe;
116         } finally {
117           IOUtils.closeWhileHandlingException(priorE, in);
118         }
119       } catch (IOException ioe) {
120         throw new ServiceConfigurationError("Error loading SPI class list from URL: " + url, ioe);
121       }
122       if (!lines.isEmpty()) {
123         this.linesIterator = lines.iterator();
124         return true;
125       }
126     }
127     return false;
128   }
129   
130   @Override
131   public boolean hasNext() {
132     return linesIterator.hasNext() || loadNextProfile();
133   }
134   
135   @Override
136   public Class<? extends S> next() {
137     // hasNext() implicitely loads the next profile, so it is essential to call this here!
138     if (!hasNext()) {
139       throw new NoSuchElementException();
140     }
141     assert linesIterator.hasNext();
142     final String c = linesIterator.next();
143     try {
144       // don't initialize the class (pass false as 2nd parameter):
145       return Class.forName(c, false, loader).asSubclass(clazz);
146     } catch (ClassNotFoundException cnfe) {
147       throw new ServiceConfigurationError(String.format(Locale.ROOT, "A SPI class of type %s with classname %s does not exist, "+
148         "please fix the file '%s%1$s' in your classpath.", clazz.getName(), c, META_INF_SERVICES));
149     }
150   }
151   
152   @Override
153   public void remove() {
154     throw new UnsupportedOperationException();
155   }
156   
157 }

由此可见SOLR SPI的流程是如下的:以Codec为例

1、 SPIClassIterator获取所有META-INF/services/org.apache.lucene.codecs.Codec的实例类信息;

2、 NamedSPILoader实例化所有META-INF/services/org.apache.lucene.codecs.Codec的实例类,并放入servicesMAP里面;

3、 Codec默认为Lucene46,从servicesMAP获取Lucene46的实例类org.apache.lucene.codecs.lucene46.Lucene46Codec;

  1 package org.apache.lucene.codecs.lucene46;
  2 
  3 /*
  4  * Licensed to the Apache Software Foundation (ASF) under one or more
  5  * contributor license agreements.  See the NOTICE file distributed with
  6  * this work for additional information regarding copyright ownership.
  7  * The ASF licenses this file to You under the Apache License, Version 2.0
  8  * (the "License"); you may not use this file except in compliance with
  9  * the License.  You may obtain a copy of the License at
 10  *
 11  *     http://www.apache.org/licenses/LICENSE-2.0
 12  *
 13  * Unless required by applicable law or agreed to in writing, software
 14  * distributed under the License is distributed on an "AS IS" BASIS,
 15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 import org.apache.lucene.codecs.Codec;
 21 import org.apache.lucene.codecs.DocValuesFormat;
 22 import org.apache.lucene.codecs.FieldInfosFormat;
 23 import org.apache.lucene.codecs.FilterCodec;
 24 import org.apache.lucene.codecs.LiveDocsFormat;
 25 import org.apache.lucene.codecs.NormsFormat;
 26 import org.apache.lucene.codecs.PostingsFormat;
 27 import org.apache.lucene.codecs.SegmentInfoFormat;
 28 import org.apache.lucene.codecs.StoredFieldsFormat;
 29 import org.apache.lucene.codecs.TermVectorsFormat;
 30 import org.apache.lucene.codecs.lucene40.Lucene40LiveDocsFormat;
 31 import org.apache.lucene.codecs.lucene41.Lucene41StoredFieldsFormat;
 32 import org.apache.lucene.codecs.lucene42.Lucene42NormsFormat;
 33 import org.apache.lucene.codecs.lucene42.Lucene42TermVectorsFormat;
 34 import org.apache.lucene.codecs.perfield.PerFieldDocValuesFormat;
 35 import org.apache.lucene.codecs.perfield.PerFieldPostingsFormat;
 36 
 37 /**
 38  * Implements the Lucene 4.6 index format, with configurable per-field postings
 39  * and docvalues formats.
 40  * <p>
 41  * If you want to reuse functionality of this codec in another codec, extend
 42  * {@link FilterCodec}.
 43  *
 44  * @see org.apache.lucene.codecs.lucene46 package documentation for file format details.
 45  * @lucene.experimental
 46  */
 47 // NOTE: if we make largish changes in a minor release, easier to just make Lucene46Codec or whatever
 48 // if they are backwards compatible or smallish we can probably do the backwards in the postingsreader
 49 // (it writes a minor version, etc).
 50 public class Lucene46Codec extends Codec {
 51   private final StoredFieldsFormat fieldsFormat = new Lucene41StoredFieldsFormat();
 52   private final TermVectorsFormat vectorsFormat = new Lucene42TermVectorsFormat();
 53   private final FieldInfosFormat fieldInfosFormat = new Lucene46FieldInfosFormat();
 54   private final SegmentInfoFormat segmentInfosFormat = new Lucene46SegmentInfoFormat();
 55   private final LiveDocsFormat liveDocsFormat = new Lucene40LiveDocsFormat();
 56   
 57   private final PostingsFormat postingsFormat = new PerFieldPostingsFormat() {
 58     @Override
 59     public PostingsFormat getPostingsFormatForField(String field) {
 60       return Lucene46Codec.this.getPostingsFormatForField(field);
 61     }
 62   };
 63   
 64   private final DocValuesFormat docValuesFormat = new PerFieldDocValuesFormat() {
 65     @Override
 66     public DocValuesFormat getDocValuesFormatForField(String field) {
 67       return Lucene46Codec.this.getDocValuesFormatForField(field);
 68     }
 69   };
 70 
 71   /** Sole constructor. */
 72   public Lucene46Codec() {
 73     super("Lucene46");
 74   }
 75   
 76   @Override
 77   public final StoredFieldsFormat storedFieldsFormat() {
 78     return fieldsFormat;
 79   }
 80   
 81   @Override
 82   public final TermVectorsFormat termVectorsFormat() {
 83     return vectorsFormat;
 84   }
 85 
 86   @Override
 87   public final PostingsFormat postingsFormat() {
 88     return postingsFormat;
 89   }
 90   
 91   @Override
 92   public final FieldInfosFormat fieldInfosFormat() {
 93     return fieldInfosFormat;
 94   }
 95   
 96   @Override
 97   public final SegmentInfoFormat segmentInfoFormat() {
 98     return segmentInfosFormat;
 99   }
100   
101   @Override
102   public final LiveDocsFormat liveDocsFormat() {
103     return liveDocsFormat;
104   }
105 
106   /** Returns the postings format that should be used for writing 
107    *  new segments of <code>field</code>.
108    *  
109    *  The default implementation always returns "Lucene41"
110    */
111   public PostingsFormat getPostingsFormatForField(String field) {
112     return defaultFormat;
113   }
114   
115   /** Returns the docvalues format that should be used for writing 
116    *  new segments of <code>field</code>.
117    *  
118    *  The default implementation always returns "Lucene45"
119    */
120   public DocValuesFormat getDocValuesFormatForField(String field) {
121     return defaultDVFormat;
122   }
123   
124   @Override
125   public final DocValuesFormat docValuesFormat() {
126     return docValuesFormat;
127   }
128 
129   private final PostingsFormat defaultFormat = PostingsFormat.forName("Lucene41");
130   private final DocValuesFormat defaultDVFormat = DocValuesFormat.forName("Lucene45");
131 
132   private final NormsFormat normsFormat = new Lucene42NormsFormat();
133 
134   @Override
135   public final NormsFormat normsFormat() {
136     return normsFormat;
137   }
138 }

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: