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

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"时,程序输出是:

now use file system search. keyword:test

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

now use 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的实例类,并放入services MAP里面

3.Codec默认为Lucene46,从services MAP获取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 }
时间: 2024-10-11 00:45:39

Solr4.8.0源码分析(7)之Solr SPI的相关文章

Solr4.9.0源码分析(1)之Solr的Servlet

Solr是作为一个Servlet运行在Tomcat里面的,可以查看Solr的web.xml. 1.web.xml配置 由web.xml可以看出,基本上所有Solr的操作都是在SolrDispatchFilter中实现的.当输入http://localhost:8080/solr/前缀的URL就会触发SolrDispatchFilter. 1 <filter> 2 <filter-name>SolrRequestFilter</filter-name> 3 <fil

Solr4.9.0源码分析(2)之Solr的启动(一)

上文写到Solr的启动过程是在SolrDispatchFilter的init()里实现,当Tomcat启动时候会自动调用init(); Solr的启动主要在 this.cores = createCoreContainer();语句中实现. /** *初始化,当tomcat启动时候开始初始化,其中主要调用createCoreContainer来实现Solr的初始化 */ public void init(FilterConfig config) throws ServletException {

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三)

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及PeerSync策略.本文以及后续的文章将重点介绍Replication策略.Replication策略不但可以在SolrCloud中起到leader到replica的数据同步,也可以在用多个单独的Solr来实现主从同步.本文先介绍在SolrCloud的leader到replica的数据同步,下一篇

Solr4.8.0源码分析(10)之Lucene的索引文件(3)

Solr4.8.0源码分析(10)之Lucene的索引文件(3) 1. .si文件 .si文件存储了段的元数据,主要涉及SegmentInfoFormat.java和Segmentinfo.java这两个文件.由于本文介绍的Solr4.8.0,所以对应的是SegmentInfoFormat的子类Lucene46SegmentInfoFormat. 首先来看下.si文件的格式 头部(header) 版本(SegVersion) doc个数(SegSize) 是否符合文档格式(IsCompoundF

Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建

Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建 由于公司里的Solr调试都是用远程jpda进行的,但是家里只有一台电脑所以不能jpda进行调试,这是因为jpda的端口冲突.所以只能在Eclipse 搭建Solr的环境,折腾了一小时终于完成了. 1. JDPA远程调试 搭建换完成Solr环境后,对${TOMCAT_HOME}/bin/startup.sh 最后一行进行修改,如下所示: 1 set JPDA_ADDRESS=7070 2 exec "$PRGDIR"

Solr4.8.0源码分析(25)之SolrCloud的Split流程

Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大致也了解split的原理了,所以也就有了这篇文章.本系列有两篇文章,第一篇为core split,第二篇为collection split. 1. 简介 这里首先需要介绍一个比较容易混淆的概念,其实Solr的HTTP API 和 SolrCloud的HTTP API是不一样,如果接受到的是Solr的

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了.本文主要介绍Solr的主从同步复制.它与前文<Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)>略有不同,前文讲到的是SolrCloud的leader与replica之间的同步,不需要通过配置solrconfig.xml来实现.而本文主要介绍单机模式下,利用so

Solr4.8.0源码分析(19)之缓存机制(二)

Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深入的来介绍下Solr的四种缓存类型,以及两种SolrCache接口实现类. 1.SolrCache接口实现类 前文已经提到SolrCache有两种接口实现类:solr.search.LRUCache 和 solr.search.LRUCache. 那么两者具体有啥区别呢? 1.1 solr.search.LR

Solr4.8.0源码分析(17)之SolrCloud索引深入(4)

Solr4.8.0源码分析(17)之SolrCloud索引深入(4) 前面几节以add为例已经介绍了solrcloud索引链建索引的三步过程,delete以及deletebyquery跟add过程大同小异,这里暂时就不介绍了.由于commit流程较为特殊,那么本节主要简要介绍下commit的流程. 1. SolrCloud的commit流程 SolrCloud的commit流程同样分为三步,本节主要简单介绍下三步过程. 1.1 LogUpdateProcessor LogUpdateProces