Refactor ItemCounter and ItemCountDAO to act like other DSpace beans. Also ensure they do not cache a Context object.

This commit is contained in:
Tim Donohue
2024-05-15 09:22:35 -05:00
parent 1517e8cd0f
commit 78f1e4190e
15 changed files with 77 additions and 268 deletions

View File

@@ -13,26 +13,17 @@ import org.dspace.core.Context;
/**
* Interface for data access of cached community and collection item count
* information
*
* @author Richard Jones
*/
public interface ItemCountDAO {
/**
* Set the DSpace Context to use during data access
*
* @param context DSpace Context
* @throws ItemCountException if count error
*/
public void setContext(Context context) throws ItemCountException;
/**
* Get the number of items in the given DSpaceObject container. This method will
* only succeed if the DSpaceObject is an instance of either a Community or a
* Collection. Otherwise it will throw an exception.
*
* @param context DSpace context
* @param dso Dspace Object
* @return count
* @throws ItemCountException if count error
*/
public int getCount(DSpaceObject dso) throws ItemCountException;
int getCount(Context context, DSpaceObject dso);
}

View File

@@ -1,67 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.browse;
import java.lang.reflect.InvocationTargetException;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Factory class to allow us to load the correct DAO for registering
* item count information
*
* @author Richard Jones
* @author Ivan Masár
*/
public class ItemCountDAOFactory {
/**
* Default constructor
*/
private ItemCountDAOFactory() { }
/**
* Get an instance of ItemCountDAO which supports the correct storage backend
* for the specific DSpace instance.
*
* @param context DSpace Context
* @return DAO
* @throws ItemCountException if count error
*/
public static ItemCountDAO getInstance(Context context)
throws ItemCountException {
/** Log4j logger */
ItemCountDAO dao = null;
ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
String className = configurationService.getProperty("ItemCountDAO.class");
// SOLR implementation is the default since DSpace 4.0
if (className == null) {
dao = new ItemCountDAOSolr();
} else {
try {
dao = (ItemCountDAO) Class.forName(className.trim())
.getDeclaredConstructor()
.newInstance();
} catch (ClassNotFoundException | IllegalAccessException
| InstantiationException | NoSuchMethodException
| SecurityException | IllegalArgumentException
| InvocationTargetException e) {
throw new ItemCountException("The configuration for ItemCountDAO is invalid: " + className, e);
}
}
dao.setContext(context);
return dao;
}
}

View File

@@ -24,14 +24,12 @@ import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Discovery (Solr) driver implementing ItemCountDAO interface to look up item
* count information in communities and collections. Caching operations are
* intentionally not implemented because Solr already is our cache.
*
* @author Ivan Masár, Andrea Bollini
*/
public class ItemCountDAOSolr implements ItemCountDAO {
/**
@@ -39,11 +37,6 @@ public class ItemCountDAOSolr implements ItemCountDAO {
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemCountDAOSolr.class);
/**
* DSpace context
*/
private Context context;
/**
* Hold the communities item count obtained from SOLR after the first query. This only works
* well if the ItemCountDAO lifecycle is bound to the request lifecycle as
@@ -61,41 +54,28 @@ public class ItemCountDAOSolr implements ItemCountDAO {
/**
* Solr search service
*/
SearchService searcher = DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName(SearchService.class.getName(), SearchService.class);
/**
* Set the dspace context to use
*
* @param context DSpace Context
* @throws ItemCountException if count error
*/
@Override
public void setContext(Context context) throws ItemCountException {
this.context = context;
}
@Autowired
protected SearchService searchService;
/**
* Get the count of the items in the given container.
*
* @param dso Dspace Context
* @param context DSpace context
* @param dso DspaceObject
* @return count
* @throws ItemCountException if count error
*/
@Override
public int getCount(DSpaceObject dso) throws ItemCountException {
loadCount();
Integer val;
public int getCount(Context context, DSpaceObject dso) {
loadCount(context);
Integer val = null;
if (dso instanceof Collection) {
val = collectionsCount.get(String.valueOf(((Collection) dso).getID()));
val = collectionsCount.get(dso.getID().toString());
} else if (dso instanceof Community) {
val = communitiesCount.get(String.valueOf(((Community) dso).getID()));
} else {
throw new ItemCountException("We can only count items in Communities or Collections");
val = communitiesCount.get(dso.getID().toString());
}
if (val != null) {
return val.intValue();
return val;
} else {
return 0;
}
@@ -105,15 +85,15 @@ public class ItemCountDAOSolr implements ItemCountDAO {
* make sure that the counts are actually fetched from Solr (if haven't been
* cached in a Map yet)
*
* @throws ItemCountException if count error
* @param context DSpace Context
*/
private void loadCount() throws ItemCountException {
private void loadCount(Context context) {
if (communitiesCount != null || collectionsCount != null) {
return;
}
communitiesCount = new HashMap<String, Integer>();
collectionsCount = new HashMap<String, Integer>();
communitiesCount = new HashMap<>();
collectionsCount = new HashMap<>();
DiscoverQuery query = new DiscoverQuery();
query.setFacetMinCount(1);
@@ -127,9 +107,9 @@ public class ItemCountDAOSolr implements ItemCountDAO {
query.addFilterQueries("NOT(discoverable:false)"); // only discoverable
query.setMaxResults(0);
DiscoverResult sResponse = null;
DiscoverResult sResponse;
try {
sResponse = searcher.search(context, query);
sResponse = searchService.search(context, query);
List<FacetResult> commCount = sResponse.getFacetResult("location.comm");
List<FacetResult> collCount = sResponse.getFacetResult("location.coll");
for (FacetResult c : commCount) {
@@ -139,8 +119,7 @@ public class ItemCountDAOSolr implements ItemCountDAO {
collectionsCount.put(c.getAsFilterQuery(), (int) c.getCount());
}
} catch (SearchServiceException e) {
log.error("caught exception: ", e);
throw new ItemCountException(e);
log.error("Could not initialize Community/Collection Item Counts from Solr: ", e);
}
}
}

View File

@@ -1,32 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.browse;
/**
* Exception type to handle item count specific problems
*
* @author Richard Jones
*/
public class ItemCountException extends Exception {
public ItemCountException() {
}
public ItemCountException(String message) {
super(message);
}
public ItemCountException(Throwable cause) {
super(cause);
}
public ItemCountException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -13,26 +13,18 @@ import org.apache.logging.log4j.Logger;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.web.ContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
/**
* This class provides a standard interface to all item counting
* operations for communities and collections. It can be run from the
* command line to prepare the cached data if desired, simply by
* running:
* operations for communities and collections.
*
* java org.dspace.browse.ItemCounter
*
* It can also be invoked via its standard API. In the event that
* the data cache is not being used, this class will return direct
* In the event that the data cache is not being used, this class will return direct
* real time counts of content.
*
* @author Richard Jones
*/
public class ItemCounter {
/**
@@ -40,57 +32,15 @@ public class ItemCounter {
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemCounter.class);
/**
* DAO to use to store and retrieve data
*/
private ItemCountDAO dao;
/**
* DSpace Context
*/
private Context context;
/**
* This field is used to hold singular instance of a class.
* Singleton pattern is used but this class should be
* refactored to modern DSpace approach (injectible service).
*/
private static ItemCounter instance;
@Autowired
protected ItemService itemService;
@Autowired
protected ConfigurationService configurationService;
private boolean showStrengths;
private boolean useCache;
/**
* Construct a new item counter which will use the given DSpace Context
*
* @param context current context
* @throws ItemCountException if count error
* Construct a new item counter
*/
public ItemCounter(Context context) throws ItemCountException {
this.context = context;
this.dao = ItemCountDAOFactory.getInstance(this.context);
this.itemService = ContentServiceFactory.getInstance().getItemService();
this.configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
this.showStrengths = configurationService.getBooleanProperty("webui.strengths.show", false);
this.useCache = configurationService.getBooleanProperty("webui.strengths.cache", true);
}
/**
* Get the singular instance of a class.
* It creates a new instance at the first usage of this method.
*
* @return instance af a class
* @throws ItemCountException when error occurs
*/
public static ItemCounter getInstance() throws ItemCountException {
if (instance == null) {
instance = new ItemCounter(ContextUtil.obtainCurrentRequestContext());
}
return instance;
protected ItemCounter() {
}
/**
@@ -103,17 +53,24 @@ public class ItemCounter {
* If it is equal to 'false' it will count the number of items
* in the container in real time.
*
* @param context DSpace Context
* @param dso DSpaceObject
* @return count
* @throws ItemCountException when error occurs
* @return count (-1 is returned if count could not be determined or is disabled)
*/
public int getCount(DSpaceObject dso) throws ItemCountException {
public int getCount(Context context, DSpaceObject dso) {
boolean showStrengths = configurationService.getBooleanProperty("webui.strengths.show", false);
boolean useCache = configurationService.getBooleanProperty("webui.strengths.cache", true);
if (!showStrengths) {
return -1;
}
if (useCache) {
return dao.getCount(dso);
// NOTE: This bean is NOT Autowired above because it's a "prototype" bean which we want to reload
// occasionally. Each time the bean reloads it will update the cached item counts.
ItemCountDAO dao =
DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName("itemCountDAO",
ItemCountDAO.class);
return dao.getCount(context, dso);
}
// if we make it this far, we need to manually count
@@ -121,8 +78,8 @@ public class ItemCounter {
try {
return itemService.countItems(context, (Collection) dso);
} catch (SQLException e) {
log.error("caught exception: ", e);
throw new ItemCountException(e);
log.error("Error counting number of Items in Collection {} :", dso.getID(), e);
return -1;
}
}
@@ -130,8 +87,8 @@ public class ItemCounter {
try {
return itemService.countItems(context, ((Community) dso));
} catch (SQLException e) {
log.error("caught exception: ", e);
throw new ItemCountException(e);
log.error("Error counting number of Items in Community {} :", dso.getID(), e);
return -1;
}
}

View File

@@ -28,7 +28,6 @@ import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import org.dspace.authorize.AuthorizeException;
import org.dspace.browse.ItemCountException;
import org.dspace.content.comparator.NameAscendingComparator;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
@@ -335,18 +334,4 @@ public class Collection extends CacheableDSpaceObject implements DSpaceObjectLeg
}
return collectionService;
}
/**
* return count of the collection items
*
* @return int
*/
public int countArchivedItems() {
try {
return collectionService.countArchivedItems(this);
} catch (ItemCountException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -31,7 +31,6 @@ import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.browse.ItemCountException;
import org.dspace.browse.ItemCounter;
import org.dspace.content.dao.CollectionDAO;
import org.dspace.content.service.BitstreamService;
@@ -122,6 +121,9 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired
protected ItemCounter itemCounter;
protected CollectionServiceImpl() {
super();
}
@@ -1112,12 +1114,12 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
/**
* Returns total collection archived items
*
* @param context DSpace Context
* @param collection Collection
* @return total collection archived items
* @throws ItemCountException
*/
@Override
public int countArchivedItems(Collection collection) throws ItemCountException {
return ItemCounter.getInstance().getCount(collection);
public int countArchivedItems(Context context, Collection collection) {
return itemCounter.getCount(context, collection);
}
}

View File

@@ -24,7 +24,6 @@ import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.dspace.browse.ItemCountException;
import org.dspace.content.comparator.NameAscendingComparator;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CommunityService;
@@ -264,17 +263,4 @@ public class Community extends CacheableDSpaceObject implements DSpaceObjectLega
}
return communityService;
}
/**
* return count of the community items
*
* @return int
*/
public int countArchivedItems() {
try {
return communityService.countArchivedItems(this);
} catch (ItemCountException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -24,7 +24,6 @@ import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.browse.ItemCountException;
import org.dspace.browse.ItemCounter;
import org.dspace.content.dao.CommunityDAO;
import org.dspace.content.service.BitstreamService;
@@ -78,6 +77,8 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
protected IdentifierService identifierService;
@Autowired(required = true)
protected SubscribeService subscribeService;
@Autowired
protected ItemCounter itemCounter;
protected CommunityServiceImpl() {
super();
@@ -719,12 +720,12 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
/**
* Returns total community archived items
*
* @param context DSpace context
* @param community Community
* @return total community archived items
* @throws ItemCountException
*/
@Override
public int countArchivedItems(Community community) throws ItemCountException {
return ItemCounter.getInstance().getCount(community);
public int countArchivedItems(Context context, Community community) {
return itemCounter.getCount(context, community);
}
}

View File

@@ -15,7 +15,6 @@ import java.util.Map;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException;
import org.dspace.browse.ItemCountException;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
@@ -487,9 +486,9 @@ public interface CollectionService
/**
* Returns total collection archived items
*
* @param context DSpace context
* @param collection Collection
* @return total collection archived items
* @throws ItemCountException
*/
int countArchivedItems(Collection collection) throws ItemCountException;
int countArchivedItems(Context context, Collection collection);
}

View File

@@ -14,7 +14,6 @@ import java.util.List;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException;
import org.dspace.browse.ItemCountException;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
@@ -297,9 +296,9 @@ public interface CommunityService extends DSpaceObjectService<Community>, DSpace
/**
* Returns total community archived items
*
* @param context DSpace context
* @param community Community
* @return total community archived items
* @throws ItemCountException
*/
int countArchivedItems(Community community) throws ItemCountException;
int countArchivedItems(Context context, Community community);
}

View File

@@ -9,8 +9,11 @@ package org.dspace.app.rest.converter;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.content.Collection;
import org.dspace.content.service.CollectionService;
import org.dspace.discovery.IndexableObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
@@ -23,10 +26,14 @@ import org.springframework.stereotype.Component;
public class CollectionConverter extends DSpaceObjectConverter<Collection, CollectionRest>
implements IndexableObjectConverter<Collection, CollectionRest> {
@Autowired
CollectionService collectionService;
@Override
public CollectionRest convert(Collection collection, Projection projection) {
CollectionRest resource = super.convert(collection, projection);
resource.setArchivedItemsCount(collection.countArchivedItems());
resource.setArchivedItemsCount(
collectionService.countArchivedItems(ContextUtil.obtainCurrentRequestContext(), collection));
return resource;
}

View File

@@ -9,8 +9,11 @@ package org.dspace.app.rest.converter;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
import org.dspace.discovery.IndexableObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
@@ -24,9 +27,14 @@ public class CommunityConverter
extends DSpaceObjectConverter<Community, CommunityRest>
implements IndexableObjectConverter<Community, CommunityRest> {
@Autowired
CommunityService communityService;
public CommunityRest convert(Community community, Projection projection) {
CommunityRest resource = super.convert(community, projection);
resource.setArchivedItemsCount(community.countArchivedItems());
resource.setArchivedItemsCount(
communityService.countArchivedItems(ContextUtil.obtainCurrentRequestContext(), community));
return resource;
}

View File

@@ -1095,15 +1095,6 @@ webui.preview.brand.fontpoint = 12
# webui.strengths.cache = true
###### ItemCounter Configuration ######
#
# Define the DAO class to use. This must correspond to your choice of
# storage for the browse system (Solr is only option at this time).
# By default, the Solr implementation is used.
#
# Solr:
# ItemCountDAO.class = org.dspace.browse.ItemCountDAOSolr
###### Browse Configuration ######
#
# Define the DAO class to use this must meet your storage choice for

View File

@@ -31,6 +31,9 @@
<bean class="org.dspace.authority.AuthorityValueServiceImpl"/>
<bean class="org.dspace.authority.AuthorityServiceImpl"/>
<bean class="org.dspace.browse.ItemCounter"/>
<bean id="itemCountDAO" class="org.dspace.browse.ItemCountDAOSolr" scope="prototype"/>
<bean class="org.dspace.checker.ChecksumHistoryServiceImpl"/>
<bean class="org.dspace.checker.ChecksumResultServiceImpl"/>
<bean class="org.dspace.checker.MostRecentChecksumServiceImpl"/>