Skip to content

Commit

Permalink
Merge aee25e0 into a5c3bfc
Browse files Browse the repository at this point in the history
  • Loading branch information
wluyima committed Aug 24, 2019
2 parents a5c3bfc + aee25e0 commit 3e8f23e
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 5 deletions.
16 changes: 11 additions & 5 deletions api/src/main/java/org/openmrs/FullTextSessionFactory.java
Expand Up @@ -3,7 +3,7 @@
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
Expand All @@ -13,7 +13,9 @@
import org.hibernate.SessionFactory;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.openmrs.api.db.DelegatingFullTextSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

/**
Expand All @@ -22,22 +24,26 @@
* to advise the factory. It is highly recommended to use this factory to create instances of the
* {@link FullTextSession} rather than directly calling {@link Search#getFullTextSession(Session)}
* for proper functionality.
*
* @since 2.2.1
*
* @since 2.3.0
*/
@Component("fullTextSessionFactory")
public class FullTextSessionFactory {

@Autowired
private SessionFactory sessionFactory;

@Autowired
private ApplicationEventPublisher eventPublisher;

/**
* Obtains a {@link FullTextSession} instance.
*
*
* @return {@link FullTextSession} object
*/
public FullTextSession getFullTextSession() {
return Search.getFullTextSession(sessionFactory.getCurrentSession());
FullTextSession delegateSession = Search.getFullTextSession(sessionFactory.getCurrentSession());
return new DelegatingFullTextSession(delegateSession, eventPublisher);
}

}
137 changes: 137 additions & 0 deletions api/src/main/java/org/openmrs/api/db/DelegatingFullTextSession.java
@@ -0,0 +1,137 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.api.db;

import java.io.Serializable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.search.Query;
import org.hibernate.engine.spi.SessionDelegatorBaseImpl;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.FullTextSharedSessionBuilder;
import org.hibernate.search.MassIndexer;
import org.hibernate.search.SearchFactory;
import org.openmrs.api.APIException;
import org.springframework.context.ApplicationEventPublisher;

/**
* Custom implementation of the {@link FullTextSession} interface that acts a wrapper around a
* target FullTextSession instance, it actually delegates all the method calls directly to the
* target except for the {@link FullTextSession#createFullTextQuery(Query, Class[])} method where is
* first notifies registered listeners of creation event before returning the newly created
* {@link FullTextQuery} object. The newly created query object is passed to the listeners. <br>
* <br>
* An example use case is that a listener can enable/disable filters on the newly created query
* object.
*/
public class DelegatingFullTextSession extends SessionDelegatorBaseImpl implements FullTextSession {

private static final Log log = LogFactory.getLog(DelegatingFullTextSession.class);

private FullTextSession delegate;

private ApplicationEventPublisher eventPublisher;

public DelegatingFullTextSession(FullTextSession delegate, ApplicationEventPublisher eventPublisher) {
super((SessionImplementor) delegate, delegate);
this.delegate = delegate;
this.eventPublisher = eventPublisher;
}

/**
* @see FullTextSession#createFullTextQuery(Query, Class[])
*/
@Override
public FullTextQuery createFullTextQuery(Query luceneQuery, Class<?>... entities) {
if (entities.length > 1) {
throw new APIException("Can't create FullTextQuery for multiple persistent classes");
}

if (log.isDebugEnabled()) {
log.debug("Creating new FullTextQuery instance");
}

Class<?> entityClass = entities[0];
FullTextQuery query = delegate.createFullTextQuery(luceneQuery, entityClass);

if (log.isDebugEnabled()) {
log.debug("Notifying FullTextQueryCreated listeners...");
}

//Notify listeners, note that we intentionally don't catch any exception from a listener
//so that failure should just halt the entire creation operation, this is possible because
//the default ApplicationEventMulticaster in spring fires events serially in the same thread
//but has the downside of where a rogue listener can block the entire application.
FullTextQueryAndEntityClass queryAndClass = new FullTextQueryAndEntityClass(query, entityClass);
eventPublisher.publishEvent(new FullTextQueryCreatedEvent(queryAndClass));

return query;
}

/**
* @see FullTextSession#index(Object)
*/
@Override
public <T> void index(T entity) {
delegate.index(entity);
}

/**
* @see FullTextSession#getSearchFactory()
*/
@Override
public SearchFactory getSearchFactory() {
return delegate.getSearchFactory();
}

/**
* @see FullTextSession#purge(Class, Serializable)
*/
@Override
public <T> void purge(Class<T> entityType, Serializable id) {
delegate.purge(entityType, id);
}

/**
* @see FullTextSession#purgeAll(Class)
*/
@Override
public <T> void purgeAll(Class<T> entityType) {
delegate.purgeAll(entityType);
}

/**
* @see FullTextSession#flushToIndexes()
*/
@Override
public void flushToIndexes() {
delegate.flushToIndexes();
}

/**
* @see FullTextSession#createIndexer(Class[])
*/
@Override
public MassIndexer createIndexer(Class<?>... types) {
return delegate.createIndexer(types);
}

/**
* @see FullTextSession#sessionWithOptions()
*/
@Override
public FullTextSharedSessionBuilder sessionWithOptions() {
return delegate.sessionWithOptions();
}

}
@@ -0,0 +1,50 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.api.db;

import org.hibernate.search.FullTextQuery;

/**
* Wrapper class around a {@link FullTextQuery} object and the Type of the entities to be returned
* by the query. An instance of this class is set as the source of a
* {@link FullTextQueryCreatedEvent} objects.
*
* @since 2.3.0
*/
public class FullTextQueryAndEntityClass {

private FullTextQuery query;

private Class<?> entityClass;

public FullTextQueryAndEntityClass(FullTextQuery query, Class<?> entityClass) {
this.query = query;
this.entityClass = entityClass;
}

/**
* Gets the query
*
* @return the query
*/
public FullTextQuery getQuery() {
return query;
}

/**
* Gets the entityClass
*
* @return the entityClass
*/
public Class<?> getEntityClass() {
return entityClass;
}

}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.api.db;

import org.springframework.context.ApplicationEvent;

/**
* Represents an event object raised whenever a {@link org.hibernate.search.FullTextQuery} object is
* created. Events are fired via the spring application event mechanism so listeners have to
* implement {@link org.springframework.context.ApplicationListener} and set the Type parameter
* value to FullTextQueryCreatedEvent, it also implies that listeners MUST be registered as spring
* beans in order to be discovered.
*
* @see FullTextQueryAndEntityClass
* @since 2.3.0
*/
public class FullTextQueryCreatedEvent extends ApplicationEvent {

/**
* @see ApplicationEvent#ApplicationEvent(java.lang.Object)
*/
public FullTextQueryCreatedEvent(FullTextQueryAndEntityClass queryAndClass) {
super(queryAndClass);
}

}

0 comments on commit 3e8f23e

Please sign in to comment.