diff --git a/pom.xml b/pom.xml
index 531d159c7c60a2e8940065ea59a2c853daf2de88..3c312e61cb5aeefbe14a7fa44520e4d0b15eec6a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1969,27 +1969,27 @@
 		<dependency>
 			<groupId>org.apache.lucene</groupId>
 			<artifactId>lucene-core</artifactId>
-			<version>4.1.0</version>
+			<version>4.5.1</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.lucene</groupId>
 			<artifactId>lucene-highlighter</artifactId>
-			<version>4.1.0</version>
+			<version>4.5.1</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.lucene</groupId>
 			<artifactId>lucene-queryparser</artifactId>
-			<version>4.1.0</version>
+			<version>4.5.1</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.lucene</groupId>
 			<artifactId>lucene-analyzers-common</artifactId>
-			<version>4.1.0</version>
+			<version>4.5.1</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.lucene</groupId>
 			<artifactId>lucene-suggest</artifactId>
-			<version>4.1.0</version>
+			<version>4.5.1</version>
 		</dependency>
 		
 		<!-- End lucene dependencies -->
diff --git a/src/main/java/org/olat/search/SearchService.java b/src/main/java/org/olat/search/SearchService.java
index 5193efe651598d14209172b6d7f55e0ffbae8ac3..85230d4d5bdc161c1fa04c2284e83ef5f8576f21 100644
--- a/src/main/java/org/olat/search/SearchService.java
+++ b/src/main/java/org/olat/search/SearchService.java
@@ -40,7 +40,7 @@ import org.olat.core.id.Roles;
  */
 public interface SearchService {
 	
-	public static final Version OO_LUCENE_VERSION = Version.LUCENE_41;
+	public static final Version OO_LUCENE_VERSION = Version.LUCENE_45;
 	
   /**
    * 
diff --git a/src/main/java/org/olat/search/_spring/searchContext.xml b/src/main/java/org/olat/search/_spring/searchContext.xml
index c73cbda1615c93a93be79686c32c98a24df3372a..22c7c3a1daa34124cdb294a126dfd00444d93f33 100644
--- a/src/main/java/org/olat/search/_spring/searchContext.xml
+++ b/src/main/java/org/olat/search/_spring/searchContext.xml
@@ -23,6 +23,17 @@
 		<constructor-arg index="3" ref="schedulerFactoryBean"/>
 		<property name="lifeIndexer" ref="jmsIndexer"/>
 		<property name="metadataFields" ref="SearchMetadataFieldsProvider" />
+		<property name="searchExecutor" ref="searchExecutor" />
+	</bean>
+	
+		<bean id="searchExecutor" class="org.springframework.core.task.support.ExecutorServiceAdapter">
+		<constructor-arg index="0" ref="searchSpringExecutor" />
+	</bean>
+	
+	<bean id="searchSpringExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
+		<property name="corePoolSize" value="2" />
+		<property name="maxPoolSize" value="3" />
+		<property name="queueCapacity" value="100" />
 	</bean>
 
 	<bean id="org.olat.search.service.disabled" class="org.olat.search.service.SearchServiceDisabled"
diff --git a/src/main/java/org/olat/search/service/SearchCallable.java b/src/main/java/org/olat/search/service/SearchCallable.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c0aa5bcdbaa767ceb088a75acdd6b1cf6924f39
--- /dev/null
+++ b/src/main/java/org/olat/search/service/SearchCallable.java
@@ -0,0 +1,101 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.search.service;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.TopDocs;
+import org.olat.core.commons.persistence.DBFactory;
+import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.search.SearchResults;
+import org.olat.search.ServiceNotAvailableException;
+import org.olat.search.service.searcher.SearchResultsImpl;
+
+/**
+ * 
+ * Initial date: 24.10.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+class SearchCallable implements Callable<SearchResults> {
+	
+	private static final OLog log = Tracing.createLoggerFor(SearchCallable.class);
+	
+	private String queryString;
+	private List<String> condQueries;
+	private Identity identity;
+	private Roles roles;
+	private int firstResult;
+	private int maxResults;
+	private boolean doHighlighting;
+	private SearchServiceImpl searchService;
+	
+	public SearchCallable(String queryString, List<String> condQueries, Identity identity, Roles roles,
+			int firstResult, int maxResults, boolean doHighlighting, SearchServiceImpl searchService) {
+		this.queryString = queryString;
+		this.condQueries = condQueries;
+		this.identity = identity;
+		this.roles = roles;
+		this.firstResult = firstResult;
+		this.maxResults = maxResults;
+		this.doHighlighting = doHighlighting;
+		this.searchService = searchService;
+	}
+	
+	@Override
+	public SearchResults call() {
+		try {
+			boolean debug = log.isDebug();
+			
+			if (!searchService.existIndex()) {
+				log.warn("Index does not exist, can't search for queryString: "+queryString);
+				throw new ServiceNotAvailableException("Index does not exist");
+			}
+			
+			if(debug) log.debug("queryString=" + queryString);
+			IndexSearcher searcher = searchService.getIndexSearcher();
+			BooleanQuery query = searchService.createQuery(queryString, condQueries);
+			if(debug) log.debug("query=" + query);
+			
+	    long startTime = System.currentTimeMillis();
+	    int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits();
+	
+	    TopDocs docs = searcher.search(query, n);
+	    long queryTime = System.currentTimeMillis() - startTime;
+	    if(debug) log.debug("hits.length()=" + docs.totalHits);
+	    SearchResultsImpl searchResult = new SearchResultsImpl(searchService.getMainIndexer(), searcher, docs, query, searchService.getAnalyzer(), identity, roles, firstResult, maxResults, doHighlighting, false);
+	    searchResult.setQueryTime(queryTime);
+	    searchResult.setNumberOfIndexDocuments(docs.totalHits);
+
+			return searchResult;
+		} catch (Exception naex) {
+			log.error("", naex);
+			return null;
+		} finally {
+			DBFactory.getInstance().commitAndCloseSession();
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/search/service/SearchOrderByCallable.java b/src/main/java/org/olat/search/service/SearchOrderByCallable.java
new file mode 100644
index 0000000000000000000000000000000000000000..7207af19b6a163f6c6fd074e418ba2c8b33618eb
--- /dev/null
+++ b/src/main/java/org/olat/search/service/SearchOrderByCallable.java
@@ -0,0 +1,115 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.search.service;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.TopDocs;
+import org.olat.core.commons.persistence.DBFactory;
+import org.olat.core.commons.persistence.SortKey;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
+import org.olat.search.ServiceNotAvailableException;
+import org.olat.search.model.AbstractOlatDocument;
+
+/**
+ * 
+ * Initial date: 24.10.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+class SearchOrderByCallable implements Callable<List<Long>> {
+	
+	private static final OLog log = Tracing.createLoggerFor(SearchOrderByCallable.class);
+	
+	private String queryString;
+	private List<String> condQueries;
+	private SortKey[] orderBy;
+	private int firstResult;
+	private int maxResults;
+	private SearchServiceImpl searchService;
+	
+	public SearchOrderByCallable(String queryString, List<String> condQueries, SortKey[] orderBy,
+			int firstResult, int maxResults,  SearchServiceImpl searchService) {
+		this.queryString = queryString;
+		this.condQueries = condQueries;
+		this.orderBy = orderBy;
+		this.firstResult = firstResult;
+		this.maxResults = maxResults;
+		this.searchService = searchService;
+	}
+	
+	@Override
+	public List<Long> call() {
+		try {
+			if (!searchService.existIndex()) {
+				log.warn("Index does not exist, can't search for queryString: "+queryString);
+				throw new ServiceNotAvailableException("Index does not exist");
+			}
+			
+			log.info("queryString=" + queryString);
+			IndexSearcher searcher = searchService.getIndexSearcher();
+			BooleanQuery query = searchService.createQuery(queryString, condQueries);
+
+	    int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits();
+	    TopDocs docs;
+	    if(orderBy != null && orderBy.length > 0 && orderBy[0] != null) {
+	    	SortField[] sortFields = new SortField[orderBy.length];
+	    	for(int i=0; i<orderBy.length; i++) {
+	    		sortFields[i] = new SortField(orderBy[i].getKey(), SortField.Type.STRING_VAL, orderBy[i].isAsc());
+	    	}
+	    	Sort sort = new Sort(sortFields);
+	    	docs = searcher.search(query, n, sort);
+	    } else {
+	    	docs = searcher.search(query, n);
+	    }
+
+			int numOfDocs = Math.min(n, docs.totalHits);
+			Set<String> retrievedFields = new HashSet<String>();
+			retrievedFields.add(AbstractOlatDocument.DB_ID_NAME);
+			
+			List<Long> res = new ArrayList<Long>();
+	    for (int i=firstResult; i<numOfDocs && res.size() < maxResults; i++) {
+	    	Document doc = searcher.doc(docs.scoreDocs[i].doc, retrievedFields);
+	    	String dbKeyStr = doc.get(AbstractOlatDocument.DB_ID_NAME);
+	    	if(StringHelper.containsNonWhitespace(dbKeyStr)) {
+	    		res.add(Long.parseLong(dbKeyStr));
+	    	}
+	    }
+
+			return res;
+		} catch (Exception naex) {
+			log.error("", naex);
+			return null;
+		} finally {
+			DBFactory.getInstance().commitAndCloseSession();
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/search/service/SearchServiceImpl.java b/src/main/java/org/olat/search/service/SearchServiceImpl.java
index 3bd79e59f5f6fbb8eac2f5002f3bdb2838256fed..6fe5b13e8b2e8d6142e38edba29fd9315a67d180 100644
--- a/src/main/java/org/olat/search/service/SearchServiceImpl.java
+++ b/src/main/java/org/olat/search/service/SearchServiceImpl.java
@@ -30,13 +30,13 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.document.Document;
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.MultiReader;
 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
@@ -46,9 +46,6 @@ import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
-import org.apache.lucene.search.Sort;
-import org.apache.lucene.search.SortField;
-import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.FSDirectory;
 import org.olat.core.CoreSpringFactory;
@@ -73,7 +70,6 @@ import org.olat.search.service.indexer.Index;
 import org.olat.search.service.indexer.LifeFullIndexer;
 import org.olat.search.service.indexer.MainIndexer;
 import org.olat.search.service.searcher.JmsSearchProvider;
-import org.olat.search.service.searcher.SearchResultsImpl;
 import org.olat.search.service.spell.SearchSpellChecker;
 import org.quartz.JobDetail;
 import org.quartz.Scheduler;
@@ -104,6 +100,8 @@ public class SearchServiceImpl implements SearchService {
 	/** Counts number of search queries since last restart. */
 	private long queryCount = 0;
 	private Object createIndexSearcherLock = new Object();
+	
+	private ExecutorService searchExecutor;
 
 	private String fields[] = {
 			AbstractOlatDocument.TITLE_FIELD_NAME, AbstractOlatDocument.DESCRIPTION_FIELD_NAME,
@@ -133,6 +131,10 @@ public class SearchServiceImpl implements SearchService {
 		searchProvider.setSearchService(this);
 	}
 	
+	public void setSearchExecutor(ExecutorService searchExecutor) {
+		this.searchExecutor = searchExecutor;
+	}
+
 	/**
 	 * [user by Spring]
 	 * @param lifeIndexer
@@ -141,6 +143,14 @@ public class SearchServiceImpl implements SearchService {
 		this.lifeIndexer = lifeIndexer;
 	}
 	
+	protected MainIndexer getMainIndexer() {
+		return mainIndexer;
+	}
+	
+	protected Analyzer getAnalyzer() {
+		return analyzer;
+	}
+	
 	/**
 	 * Start the job indexer
 	 */
@@ -189,6 +199,7 @@ public class SearchServiceImpl implements SearchService {
 		searchSpellChecker.setIndexPath(searchModuleConfig.getFullIndexPath());
 		searchSpellChecker.setSpellDictionaryPath(searchModuleConfig.getSpellCheckDictionaryPath());
 		searchSpellChecker.setSpellCheckEnabled(searchModuleConfig.getSpellCheckEnabled());
+		searchSpellChecker.setSearchExecutor(searchExecutor);
 		
 	  indexer = new Index(searchModuleConfig, searchSpellChecker, mainIndexer, lifeIndexer);
 
@@ -218,35 +229,14 @@ public class SearchServiceImpl implements SearchService {
 			int firstResult, int maxResults, boolean doHighlighting)
 	throws ServiceNotAvailableException, ParseException {
 		try {
-			if (!existIndex()) {
-				log.warn("Index does not exist, can't search for queryString: "+queryString);
-				throw new ServiceNotAvailableException("Index does not exist");
-			}
-			
-			log.info("queryString=" + queryString);
-			IndexSearcher searcher = getIndexSearcher();
-			BooleanQuery query = createQuery(queryString, condQueries);
-			if (log.isDebug()) log.debug("query=" + query);
-			
-	    long startTime = System.currentTimeMillis();
-	    int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits();
-
-	    TopDocs docs = searcher.search(query, n);
-	    long queryTime = System.currentTimeMillis() - startTime;
-	    if (log.isDebug()) log.debug("hits.length()=" + docs.totalHits);
-	    SearchResultsImpl searchResult = new SearchResultsImpl(mainIndexer, searcher, docs, query, analyzer, identity, roles, firstResult, maxResults, doHighlighting, false);
-	    searchResult.setQueryTime(queryTime);
-	    searchResult.setNumberOfIndexDocuments(docs.totalHits);
-	    queryCount++;
-	    return searchResult;
-		} catch (ServiceNotAvailableException naex) {
-			// pass exception 
-			throw new ServiceNotAvailableException(naex.getMessage());
-		} catch (ParseException pex) {
-			throw new ParseException("can not parse query=" + queryString);
-		} catch (Exception ex) {
-			log.warn("Exception in search", ex);
-			throw new ServiceNotAvailableException(ex.getMessage());
+			SearchCallable run = new SearchCallable(queryString,  condQueries, identity, roles, firstResult, maxResults, doHighlighting, this);
+			Future<SearchResults> futureResults = searchExecutor.submit(run);
+			SearchResults results = futureResults.get();
+			queryCount++;
+			return results;
+		} catch (Exception e) {
+			log.error("", e);
+			return null;
 		}
  	}
 	
@@ -255,54 +245,18 @@ public class SearchServiceImpl implements SearchService {
 			int firstResult, int maxResults, SortKey... orderBy)
 	throws ServiceNotAvailableException, ParseException, QueryException {
 		try {
-			if (!existIndex()) {
-				log.warn("Index does not exist, can't search for queryString: "+queryString);
-				throw new ServiceNotAvailableException("Index does not exist");
-			}
-			
-			log.info("queryString=" + queryString);
-			IndexSearcher searcher = getIndexSearcher();
-			BooleanQuery query = createQuery(queryString, condQueries);
-
-	    int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits();
-	    TopDocs docs;
-	    if(orderBy != null && orderBy.length > 0 && orderBy[0] != null) {
-	    	SortField[] sortFields = new SortField[orderBy.length];
-	    	for(int i=0; i<orderBy.length; i++) {
-	    		sortFields[i] = new SortField(orderBy[i].getKey(), SortField.Type.STRING_VAL, orderBy[i].isAsc());
-	    	}
-	    	Sort sort = new Sort(sortFields);
-	    	docs = searcher.search(query, n, sort);
-	    } else {
-	    	docs = searcher.search(query, n);
-	    }
-
-			int numOfDocs = Math.min(n, docs.totalHits);
-			Set<String> retrievedFields = new HashSet<String>();
-			retrievedFields.add(AbstractOlatDocument.DB_ID_NAME);
-			
-			List<Long> res = new ArrayList<Long>();
-	    for (int i=firstResult; i<numOfDocs && res.size() < maxResults; i++) {
-	    	Document doc = searcher.doc(docs.scoreDocs[i].doc, retrievedFields);
-	    	String dbKeyStr = doc.get(AbstractOlatDocument.DB_ID_NAME);
-	    	if(StringHelper.containsNonWhitespace(dbKeyStr)) {
-	    		res.add(Long.parseLong(dbKeyStr));
-	    	}
-	    }
-	    queryCount++;
-	    return res;
-		} catch (ServiceNotAvailableException naex) {
-			// pass exception 
-			throw new ServiceNotAvailableException(naex.getMessage());
-		} catch (ParseException pex) {
-			throw new ParseException("can not parse query=" + queryString);
-		} catch (Exception ex) {
-			log.warn("Exception in search", ex);
-			throw new ServiceNotAvailableException(ex.getMessage());
+			SearchOrderByCallable run = new SearchOrderByCallable(queryString,  condQueries, orderBy, firstResult, maxResults, this);
+			Future<List<Long>> futureResults = searchExecutor.submit(run);
+			List<Long> results = futureResults.get();
+			queryCount++;
+			return results;
+		} catch (Exception e) {
+			log.error("", e);
+			return new ArrayList<Long>(1);
 		}
 	}
 	
-	private BooleanQuery createQuery(String queryString, List<String> condQueries)
+	protected BooleanQuery createQuery(String queryString, List<String> condQueries)
 	throws ParseException {
 		BooleanQuery query = new BooleanQuery();
 		if(StringHelper.containsNonWhitespace(queryString)) {
@@ -383,7 +337,7 @@ public class SearchServiceImpl implements SearchService {
 		return true;
 	}
 	
-	private IndexSearcher getIndexSearcher() throws ServiceNotAvailableException, IOException {
+	protected IndexSearcher getIndexSearcher() throws ServiceNotAvailableException, IOException {
 		if(searcher == null) {
 			synchronized (createIndexSearcherLock) {//o_clusterOK by:fj if service is only configured on one vm, which is recommended way
 				if (searcher == null) {
@@ -454,7 +408,7 @@ public class SearchServiceImpl implements SearchService {
 	 * Check if index exist.
 	 * @return true : Index exists.
 	 */
-	private boolean existIndex()
+	protected boolean existIndex()
 	throws IOException {
 		try {
 			File indexFile = new File(searchModuleConfig.getFullIndexPath());
diff --git a/src/main/java/org/olat/search/service/indexer/IndexWriterHolder.java b/src/main/java/org/olat/search/service/indexer/IndexWriterHolder.java
index e712ae78f5ca17040ddf8b57ffa8d7b5358dfe78..cecb97430f33f23e097d425fc499fca5b472c9c1 100644
--- a/src/main/java/org/olat/search/service/indexer/IndexWriterHolder.java
+++ b/src/main/java/org/olat/search/service/indexer/IndexWriterHolder.java
@@ -24,7 +24,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.store.Directory;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
@@ -39,38 +38,38 @@ public class IndexWriterHolder {
 	private static final OLog log = Tracing.createLoggerFor(IndexWriterHolder.class);
 	
 	private Directory indexPath;
-	private IndexWriterConfig config;
+	private JmsIndexer indexer;
 	
 	private AtomicInteger counter = new AtomicInteger(0);
 	private IndexWriter writerRef;
 
-	public IndexWriterHolder(Directory indexPath, IndexWriterConfig config)
+	public IndexWriterHolder(Directory indexPath, JmsIndexer indexer)
 	throws IOException {
 		this.indexPath = indexPath;
-		this.config = config;
+		this.indexer = indexer;
 	}
 	
 	public synchronized void ensureIndexExists() {
-		if(!DirectoryReader.indexExists(indexPath)) {
-			IndexWriter writer = null;
-			try {
+		IndexWriter writer = null;
+		try {
+			if(!DirectoryReader.indexExists(indexPath)) {
 				writer = getAndLock();
-			} catch (IOException e) {
-				log.error("",  e);
-			} finally {
-				release(writer);
 			}
+		} catch (IOException e) {
+			log.error("",  e);
+		} finally {
+			release(writer);
 		}
 	}
 
 	public synchronized IndexWriter getAndLock() throws IOException {
 		if(writerRef == null) {
 			long start = System.currentTimeMillis();
-			IndexWriter indexWriter = new IndexWriter(indexPath, config);
+			IndexWriter indexWriter = new IndexWriter(indexPath, indexer.newIndexWriterConfig());
 			if(!DirectoryReader.indexExists(indexPath)) {
 				indexWriter.commit();//make sure it exists
 			}
-			System.out.println("Opening writer takes (ms): " + (System.currentTimeMillis() - start));
+			log.info("Opening writer takes (ms): " + (System.currentTimeMillis() - start));
 			writerRef = indexWriter;
 		}
 		counter.incrementAndGet();
@@ -86,7 +85,7 @@ public class IndexWriterHolder {
 					indexWriter.commit();
 					indexWriter.close();
 					writerRef = null;
-					System.out.println("Close writer takes (ms): " + (System.currentTimeMillis() - start));
+					log.info("Close writer takes (ms): " + (System.currentTimeMillis() - start));
 				}
 			} catch (IOException e) {
 				log.error("", e);
diff --git a/src/main/java/org/olat/search/service/indexer/JmsIndexer.java b/src/main/java/org/olat/search/service/indexer/JmsIndexer.java
index 89bbe6c84658f854ed33389b38205cb6dd6cbd00..626bc313d038ec06e414c68fdee86c901a9cf1f1 100644
--- a/src/main/java/org/olat/search/service/indexer/JmsIndexer.java
+++ b/src/main/java/org/olat/search/service/indexer/JmsIndexer.java
@@ -83,7 +83,6 @@ public class JmsIndexer implements MessageListener, LifeFullIndexer {
 	private IndexWriterHolder permanentIndexWriter;
 	
 	private double ramBufferSizeMB;
-	private boolean useCompoundFile;
 	private boolean indexingNode;
 	
 	private List<LifeIndexer> indexers = new ArrayList<LifeIndexer>();
@@ -91,7 +90,6 @@ public class JmsIndexer implements MessageListener, LifeFullIndexer {
 	public JmsIndexer(SearchModule searchModuleConfig) {
 		indexingNode = searchModuleConfig.isSearchServiceEnabled();
     ramBufferSizeMB = searchModuleConfig.getRAMBufferSizeMB();
-    useCompoundFile = searchModuleConfig.getUseCompoundFile();
 		permanentIndexPath = searchModuleConfig.getFullPermanentIndexPath();
 	}
 
@@ -173,7 +171,7 @@ public class JmsIndexer implements MessageListener, LifeFullIndexer {
 			File tempIndexDir = new File(permanentIndexPath);
 			Directory indexPath = FSDirectory.open(tempIndexDir);
 			if(indexingNode) {
-				permanentIndexWriter = new IndexWriterHolder (indexPath, newIndexWriterConfig());
+				permanentIndexWriter = new IndexWriterHolder (indexPath, this);
 				permanentIndexWriter.ensureIndexExists();
 			}
 			reader = DirectoryReader.open(indexPath);
@@ -184,7 +182,6 @@ public class JmsIndexer implements MessageListener, LifeFullIndexer {
 	
 	public LogMergePolicy newLogMergePolicy() {
 		LogMergePolicy logmp = new LogDocMergePolicy();
-		logmp.setUseCompoundFile(useCompoundFile);
 		logmp.setCalibrateSizeByDeletes(true);
 		logmp.setMergeFactor(INDEX_MERGE_FACTOR);
 		return logmp;
diff --git a/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java b/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java
index c342ee884e158d4f2f72bde498611676273b537f..c077e5514386a2cf20dce91e9f214f167c3f40ed 100644
--- a/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java
+++ b/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java
@@ -100,7 +100,6 @@ public class OlatFullIndexer {
 
 	private MainIndexer mainIndexer;
 	private double ramBufferSizeMB;
-	private boolean useCompoundFile;
 	
 	/**
 	 * 
@@ -117,7 +116,6 @@ public class OlatFullIndexer {
     numberIndexWriter = searchModuleConfig.getNumberIndexWriter();
     documentsPerInterval = searchModuleConfig.getDocumentsPerInterval();
     ramBufferSizeMB = searchModuleConfig.getRAMBufferSizeMB();
-    useCompoundFile = searchModuleConfig.getUseCompoundFile();
     fullIndexerStatus = new FullIndexerStatus(numberIndexWriter);    
     stopIndexing = true;
     documentQueue = new Vector<Document>();
@@ -148,7 +146,6 @@ public class OlatFullIndexer {
 	
 	public LogMergePolicy newLogMergePolicy() {
 		LogMergePolicy logmp = new LogDocMergePolicy();
-		logmp.setUseCompoundFile(useCompoundFile);
 		logmp.setCalibrateSizeByDeletes(true);
 		logmp.setMergeFactor(INDEX_MERGE_FACTOR);
 		return logmp;
diff --git a/src/main/java/org/olat/search/service/spell/CheckCallable.java b/src/main/java/org/olat/search/service/spell/CheckCallable.java
new file mode 100644
index 0000000000000000000000000000000000000000..252da6efacf90d5e5d9b1b4cec6898caa83625ec
--- /dev/null
+++ b/src/main/java/org/olat/search/service/spell/CheckCallable.java
@@ -0,0 +1,68 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.search.service.spell;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+
+import org.apache.lucene.search.spell.SpellChecker;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+
+/**
+ * 
+ * Initial date: 24.10.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+class CheckCallable implements Callable<Set<String>> {
+	
+	private static final OLog log = Tracing.createLoggerFor(CheckCallable.class);
+	
+	private final String query;
+	private final SearchSpellChecker spellCheckerService;
+	
+	public CheckCallable(String query, SearchSpellChecker spellCheckerService) {
+		this.query = query;
+		this.spellCheckerService = spellCheckerService;
+	}
+
+	@Override
+	public Set<String> call() throws Exception {
+		try {
+			SpellChecker spellChecker  = spellCheckerService.getSpellChecker();
+  		if (spellChecker != null) {
+  			String[] words = spellChecker.suggestSimilar(query,5);
+  			// Remove dublicate 
+  			Set<String> filteredList = new TreeSet<String>();
+  			for (String word : words) {
+  				filteredList.add(word);
+				}
+			  return filteredList;
+  		}
+		} catch (IOException e) {
+			log.warn("Can not spell check",e);
+		}
+		return new HashSet<String>();
+	}
+}
diff --git a/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java b/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java
index 15059a7de813cafe0b1fe90773fccd286742a455..ade28f3ce892f6292be03f10db3e67268b01f64e 100644
--- a/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java
+++ b/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java
@@ -27,8 +27,10 @@ package org.olat.search.service.spell;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.HashSet;
 import java.util.Set;
-import java.util.TreeSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
@@ -36,13 +38,9 @@ import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.Term;
 import org.apache.lucene.search.spell.Dictionary;
-import org.apache.lucene.search.spell.DirectSpellChecker;
 import org.apache.lucene.search.spell.LuceneDictionary;
 import org.apache.lucene.search.spell.SpellChecker;
-import org.apache.lucene.search.spell.SuggestMode;
-import org.apache.lucene.search.spell.SuggestWord;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.FSDirectory;
 import org.olat.core.logging.OLog;
@@ -68,10 +66,14 @@ public class SearchSpellChecker {
 	private String spellDictionaryPath;
 	private SpellChecker spellChecker;
 	private boolean isSpellCheckEnabled = true;
-	
+	private ExecutorService searchExecutor;
 	
 	public SearchSpellChecker() {
-		//called by Spring
+		//
+	}
+	
+	public void setSearchExecutor(ExecutorService searchExecutor) {
+		this.searchExecutor = searchExecutor;
 	}
 	
 	/**
@@ -82,66 +84,31 @@ public class SearchSpellChecker {
 	 */
   public Set<String> check(String query) {
   	try {
-  		if(spellChecker==null) { //lazy initialization
-  			try {
-  				synchronized(spellDictionaryPath) {//o_clusterOK by:pb if service is only configured on one vm, which is recommended way
-  				  File spellDictionaryFile = new File(spellDictionaryPath);
-  				  Directory spellIndexDirectory = FSDirectory.open(spellDictionaryFile);
-  					if (spellChecker==null && DirectoryReader.indexExists(spellIndexDirectory) && isSpellCheckEnabled ) {
-  					  spellChecker = new SpellChecker(spellIndexDirectory);
-  					  spellChecker.setAccuracy(0.7f);
-  				  }
-  				}
-  	 		} catch (IOException e) {
-  	 			log.warn("Can not initialze SpellChecker",e);
-  			}
-  		} 
-  		if (spellChecker != null) {
-  			String[] words = spellChecker.suggestSimilar(query,5);
-  			// Remove dublicate 
-  			Set<String> filteredList = new TreeSet<String>();
-  			for (String word : words) {
-  				filteredList.add(word);
-				}
-
-			  return filteredList;
-  		}
-  		
-		} catch (IOException e) {
+  		CheckCallable run = new CheckCallable(query, this);
+  		Future<Set<String>> futureResults = searchExecutor.submit(run);
+  		return futureResults.get();
+		} catch (Exception e) {
 			log.warn("Can not spell check",e);
-			return null;
+			return new HashSet<String>();
 		}
-		return null;
   }
   
-
-  public Set<String> directCheck(String query) {
-		try {
-			File indexPathIO = new File(indexPath);
-			Directory spellIndexDirectory = FSDirectory.open(indexPathIO);
-			DirectoryReader indexReader = DirectoryReader.open(spellIndexDirectory);
-
-			String[] fields = new String[]{ OlatDocument.TITLE_FIELD_NAME, OlatDocument.DESCRIPTION_FIELD_NAME, OlatDocument.AUTHOR_FIELD_NAME, OlatDocument.CONTENT_FIELD_NAME};
-			Set<String> filteredList = new TreeSet<String>();
-			for(String field:fields) {
-				Term term = new Term(field, query);
-				DirectSpellChecker spellChecker = new DirectSpellChecker();
-				spellChecker.setAccuracy(0.7f);
-				spellChecker.setMaxInspections(10);
-				spellChecker.setMaxEdits(2);
-				//spellChecker.setDistance(new NGramDistance(4));
-				
-				SuggestWord[] words = spellChecker.suggestSimilar(term, 5, indexReader, SuggestMode.SUGGEST_ALWAYS, 0.7f);
-				for (SuggestWord word : words) {
-					filteredList.add(word.string);
+  protected SpellChecker getSpellChecker() {
+  	if(spellChecker==null) { //lazy initialization
+			try {
+				synchronized(spellDictionaryPath) {//o_clusterOK by:pb if service is only configured on one vm, which is recommended way
+				  File spellDictionaryFile = new File(spellDictionaryPath);
+				  Directory spellIndexDirectory = FSDirectory.open(spellDictionaryFile);
+					if (spellChecker==null && DirectoryReader.indexExists(spellIndexDirectory) && isSpellCheckEnabled ) {
+					  spellChecker = new SpellChecker(spellIndexDirectory);
+					  spellChecker.setAccuracy(0.7f);
+				  }
 				}
+	 		} catch (IOException e) {
+	 			log.warn("Can not initialze SpellChecker",e);
 			}
-			
-			return filteredList;
-		} catch (IOException e) {
-			e.printStackTrace();
-			return null;
 		}
+  	return spellChecker;
   }
   	
   /**
@@ -157,37 +124,43 @@ public class SearchSpellChecker {
 		    if (log.isDebug()) startSpellIndexTime = System.currentTimeMillis();
 	      Directory indexDir = FSDirectory.open(new File(indexPath));
 		    indexReader = DirectoryReader.open(indexDir);
-	      // 1. Create content spellIndex 
+	      
+		    // 1. Create content spellIndex 
 		    File spellDictionaryFile = new File(spellDictionaryPath);
 	      FSDirectory contentSpellIndexDirectory = FSDirectory.open(new File(spellDictionaryPath + CONTENT_PATH));//true
 	      SpellChecker contentSpellChecker = new SpellChecker(contentSpellIndexDirectory);
 	      Dictionary contentDictionary = new LuceneDictionary(indexReader, OlatDocument.CONTENT_FIELD_NAME);
-
 				Analyzer analyzer = new StandardAnalyzer(SearchService.OO_LUCENE_VERSION);
-	      IndexWriterConfig indexWriterConfig = new IndexWriterConfig(SearchService.OO_LUCENE_VERSION, analyzer);
+	      IndexWriterConfig contentIndexWriterConfig = new IndexWriterConfig(SearchService.OO_LUCENE_VERSION, analyzer);
+	      contentSpellChecker.indexDictionary(contentDictionary, contentIndexWriterConfig, true);
 	      
-	      contentSpellChecker.indexDictionary(contentDictionary, indexWriterConfig, true);
 	      // 2. Create title spellIndex 
 	      FSDirectory titleSpellIndexDirectory = FSDirectory.open(new File(spellDictionaryPath + TITLE_PATH));//true
 	      SpellChecker titleSpellChecker = new SpellChecker(titleSpellIndexDirectory);
 	      Dictionary titleDictionary = new LuceneDictionary(indexReader, OlatDocument.TITLE_FIELD_NAME);
-	      titleSpellChecker.indexDictionary(titleDictionary, indexWriterConfig, true);
+	      IndexWriterConfig titleIndexWriterConfig = new IndexWriterConfig(SearchService.OO_LUCENE_VERSION, analyzer);
+	      titleSpellChecker.indexDictionary(titleDictionary, titleIndexWriterConfig, true);
+	      
 	      // 3. Create description spellIndex 
 	      FSDirectory descriptionSpellIndexDirectory = FSDirectory.open(new File(spellDictionaryPath + DESCRIPTION_PATH));//true
 	      SpellChecker descriptionSpellChecker = new SpellChecker(descriptionSpellIndexDirectory);
 	      Dictionary descriptionDictionary = new LuceneDictionary(indexReader, OlatDocument.DESCRIPTION_FIELD_NAME);
-	      descriptionSpellChecker.indexDictionary(descriptionDictionary, indexWriterConfig, true);
+	      IndexWriterConfig descIndexWriterConfig = new IndexWriterConfig(SearchService.OO_LUCENE_VERSION, analyzer);
+	      descriptionSpellChecker.indexDictionary(descriptionDictionary, descIndexWriterConfig, true);
 	      // 4. Create author spellIndex 
+	      
 	      FSDirectory authorSpellIndexDirectory = FSDirectory.open(new File(spellDictionaryPath + AUTHOR_PATH));//true
 	      SpellChecker authorSpellChecker = new SpellChecker(authorSpellIndexDirectory);
 	      Dictionary authorDictionary = new LuceneDictionary(indexReader, OlatDocument.AUTHOR_FIELD_NAME);
-	      authorSpellChecker.indexDictionary(authorDictionary, indexWriterConfig, true);
+	      IndexWriterConfig authorIndexWriterConfig = new IndexWriterConfig(SearchService.OO_LUCENE_VERSION, analyzer);
+	      authorSpellChecker.indexDictionary(authorDictionary, authorIndexWriterConfig, true);
 	      
 	      // Merge all part spell indexes (content,title etc.) to one common spell index
 	      Directory spellIndexDirectory = FSDirectory.open(spellDictionaryFile);//true
 	      
 	      //clean up the main index
-	      IndexWriter merger = new IndexWriter(spellIndexDirectory, indexWriterConfig);
+	      IndexWriterConfig spellIndexWriterConfig = new IndexWriterConfig(SearchService.OO_LUCENE_VERSION, analyzer);
+	      IndexWriter merger = new IndexWriter(spellIndexDirectory, spellIndexWriterConfig);
 	      merger.deleteAll();
 	      merger.commit();