diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAO.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAO.java new file mode 100644 index 0000000000..d841c8d90a --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAO.java @@ -0,0 +1,98 @@ +/* + * ItemCountDAO.java + * + * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package org.dspace.browse; + +import org.dspace.content.Community; +import org.dspace.content.Collection; +import org.dspace.core.Context; +import org.dspace.content.DSpaceObject; + +/** + * 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 + * @throws ItemCountException + */ + public void setContext(Context context) throws ItemCountException; + + /** + * Set the given count as the number of items in the given community + * + * @param community + * @param count + * @throws ItemCountException + */ + public void communityCount(Community community, int count) throws ItemCountException; + + /** + * Set the given count as the number of items in the given collection + * + * @param collection + * @param count + * @throws ItemCountException + */ + public void collectionCount(Collection collection, int count) 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 dso + * @return + * @throws ItemCountException + */ + public int getCount(DSpaceObject dso) throws ItemCountException; + + /** + * Remove any cached data regarding 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 dso + * @throws ItemCountException + */ + public void remove(DSpaceObject dso) throws ItemCountException; +} diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOFactory.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOFactory.java new file mode 100644 index 0000000000..fb5c8fb528 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOFactory.java @@ -0,0 +1,79 @@ +/* + * ItemCountDAOFactory.java + * + * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package org.dspace.browse; + +import org.dspace.core.Context; +import org.dspace.core.ConfigurationManager; + +/** + * Factory class to allow us to load the correct DAO for registering + * item count information + * + * @author Richard Jones + * + */ +public class ItemCountDAOFactory +{ + /** + * Get an instance of ItemCountDAO which supports the correct database + * for the specific DSpace instance. + * + * @param context + * @return + * @throws ItemCountException + */ + public static ItemCountDAO getInstance(Context context) + throws ItemCountException + { + String db = ConfigurationManager.getProperty("db.name"); + ItemCountDAO dao; + if ("postgres".equals(db)) + { + dao = new ItemCountDAOPostgres(); + } + else if ("oracle".equals(db)) + { + dao = new ItemCountDAOOracle(); + } + else + { + throw new ItemCountException("Database type: " + db + " is not currently supported"); + } + + dao.setContext(context); + return dao; + } +} diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOOracle.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOOracle.java new file mode 100644 index 0000000000..75520ea2b6 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOOracle.java @@ -0,0 +1,336 @@ +/* + * ItemCountDAOOracle.java + * + * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package org.dspace.browse; + +import org.apache.log4j.Logger; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; +import org.dspace.core.Context; + +import org.dspace.storage.rdbms.TableRowIterator; +import org.dspace.storage.rdbms.TableRow; +import org.dspace.storage.rdbms.DatabaseManager; + +import java.sql.SQLException; + +/** + * Oracle driver implementing ItemCountDAO interface to cache item + * count information in communities and collections + * + * @author Richard Jones + * + */ +public class ItemCountDAOOracle implements ItemCountDAO +{ + /** Log4j logger */ + private static Logger log = Logger.getLogger(ItemCountDAOOracle.class); + + /** DSpace context */ + private Context context; + + /** SQL to select on a collection id */ + private String collectionSelect = "SELECT * FROM collection_item_count WHERE collection_id = ?"; + + /** SQL to insert a new collection record */ + private String collectionInsert = "INSERT INTO collection_item_count (collection_id, number) VALUES (?, ?)"; + + /** SQL to update an existing collection record */ + private String collectionUpdate = "UPDATE collection_item_count SET number = ? WHERE collection_id = ?"; + + /** SQL to remove a collection record */ + private String collectionRemove = "DELETE FROM collection_item_count WHERE collection_id = ?"; + + /** SQL to select on a community id */ + private String communitySelect = "SELECT * FROM community_item_count WHERE community_id = ?"; + + /** SQL to insert a new community record */ + private String communityInsert = "INSERT INTO community_item_count (community_id, number) VALUES (?, ?)"; + + /** SQL to update an existing community record */ + private String communityUpdate = "UPDATE community_item_count SET number = ? WHERE community_id = ?"; + + /** SQL to remove a community record */ + private String communityRemove = "DELETE FROM community_item_count WHERE community_id = ?"; + + /** + * Store the count of the given collection + * + * @param collection + * @param count + * @throws ItemCountException + */ + public void collectionCount(Collection collection, int count) + throws ItemCountException + { + try + { + // first find out if we have a record + Object[] sparams = { new Integer(collection.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, collectionSelect, sparams); + + if (tri.hasNext()) + { + Object[] params = { new Integer(count), new Integer(collection.getID()) }; + DatabaseManager.updateQuery(context, collectionUpdate, params); + } + else + { + Object[] params = { new Integer(collection.getID()), new Integer(count) }; + DatabaseManager.updateQuery(context, collectionInsert, params); + } + + tri.close(); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Store the count of the given community + * + * @param community + * @param count + * @throws ItemCountException + */ + public void communityCount(Community community, int count) + throws ItemCountException + { + try + { + // first find out if we have a record + Object[] sparams = { new Integer(community.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, communitySelect, sparams); + + if (tri.hasNext()) + { + Object[] params = { new Integer(count), new Integer(community.getID()) }; + DatabaseManager.updateQuery(context, communityUpdate, params); + } + else + { + Object[] params = { new Integer(community.getID()), new Integer(count) }; + DatabaseManager.updateQuery(context, communityInsert, params); + } + + tri.close(); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Set the dspace context to use + * + * @param context + * @throws ItemCountException + */ + public void setContext(Context context) + throws ItemCountException + { + this.context = context; + } + + /** + * get the count of the items in the given container + * + * @param dso + * @return + * @throws ItemCountException + */ + public int getCount(DSpaceObject dso) + throws ItemCountException + { + if (dso instanceof Collection) + { + return getCollectionCount((Collection) dso); + } + else if (dso instanceof Community) + { + return getCommunityCount((Community) dso); + } + else + { + throw new ItemCountException("We can only count items in Communities or Collections"); + } + } + + /** + * remove the cache for the given container + * + * @param dso + * @throws ItemCountException + */ + public void remove(DSpaceObject dso) throws ItemCountException + { + if (dso instanceof Collection) + { + removeCollection((Collection) dso); + } + else if (dso instanceof Community) + { + removeCommunity((Community) dso); + } + else + { + throw new ItemCountException("We can only delete count of items from Communities or Collections"); + } + } + + /** + * remove the cache for the given collection + * + * @param collection + * @throws ItemCountException + */ + private void removeCollection(Collection collection) + throws ItemCountException + { + try + { + Object[] params = { new Integer(collection.getID()) }; + DatabaseManager.updateQuery(context, collectionRemove, params); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Remove the cache for the given community + * + * @param community + * @throws ItemCountException + */ + private void removeCommunity(Community community) + throws ItemCountException + { + try + { + Object[] params = { new Integer(community.getID()) }; + DatabaseManager.updateQuery(context, communityRemove, params); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Get the count for the given collection + * + * @param collection + * @return + * @throws ItemCountException + */ + private int getCollectionCount(Collection collection) + throws ItemCountException + { + try + { + Object[] params = { new Integer(collection.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, collectionSelect, params); + + if (!tri.hasNext()) + { + return 0; + } + + TableRow tr = tri.next(); + + if (tri.hasNext()) + { + throw new ItemCountException("More than one count row in the database"); + } + + tri.close(); + + return tr.getIntColumn("number"); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * get the count for the given community + * + * @param community + * @return + * @throws ItemCountException + */ + private int getCommunityCount(Community community) + throws ItemCountException + { + try + { + Object[] params = { new Integer(community.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, communitySelect, params); + + if (!tri.hasNext()) + { + return 0; + } + + TableRow tr = tri.next(); + + if (tri.hasNext()) + { + throw new ItemCountException("More than one count row in the database"); + } + + tri.close(); + + return tr.getIntColumn("number"); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } +} diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOPostgres.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOPostgres.java new file mode 100644 index 0000000000..44bd9a0ac6 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOPostgres.java @@ -0,0 +1,336 @@ +/* + * ItemCountDAOPostgres.java + * + * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package org.dspace.browse; + +import org.apache.log4j.Logger; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; +import org.dspace.core.Context; + +import org.dspace.storage.rdbms.TableRowIterator; +import org.dspace.storage.rdbms.TableRow; +import org.dspace.storage.rdbms.DatabaseManager; + +import java.sql.SQLException; + +/** + * Postgres driver implementing ItemCountDAO interface to cache item + * count information in communities and collections + * + * @author Richard Jones + * + */ +public class ItemCountDAOPostgres implements ItemCountDAO +{ + /** Log4j logger */ + private static Logger log = Logger.getLogger(ItemCountDAOPostgres.class); + + /** DSpace context */ + private Context context; + + /** SQL to select on a collection id */ + private String collectionSelect = "SELECT * FROM collection_item_count WHERE collection_id = ?"; + + /** SQL to insert a new collection record */ + private String collectionInsert = "INSERT INTO collection_item_count (collection_id, number) VALUES (?, ?)"; + + /** SQL to update an existing collection record */ + private String collectionUpdate = "UPDATE collection_item_count SET number = ? WHERE collection_id = ?"; + + /** SQL to remove a collection record */ + private String collectionRemove = "DELETE FROM collection_item_count WHERE collection_id = ?"; + + /** SQL to select on a community id */ + private String communitySelect = "SELECT * FROM community_item_count WHERE community_id = ?"; + + /** SQL to insert a new community record */ + private String communityInsert = "INSERT INTO community_item_count (community_id, number) VALUES (?, ?)"; + + /** SQL to update an existing community record */ + private String communityUpdate = "UPDATE community_item_count SET number = ? WHERE community_id = ?"; + + /** SQL to remove a community record */ + private String communityRemove = "DELETE FROM community_item_count WHERE community_id = ?"; + + /** + * Store the count of the given collection + * + * @param collection + * @param count + * @throws ItemCountException + */ + public void collectionCount(Collection collection, int count) + throws ItemCountException + { + try + { + // first find out if we have a record + Object[] sparams = { new Integer(collection.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, collectionSelect, sparams); + + if (tri.hasNext()) + { + Object[] params = { new Integer(count), new Integer(collection.getID()) }; + DatabaseManager.updateQuery(context, collectionUpdate, params); + } + else + { + Object[] params = { new Integer(collection.getID()), new Integer(count) }; + DatabaseManager.updateQuery(context, collectionInsert, params); + } + + tri.close(); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Store the count of the given community + * + * @param community + * @param count + * @throws ItemCountException + */ + public void communityCount(Community community, int count) + throws ItemCountException + { + try + { + // first find out if we have a record + Object[] sparams = { new Integer(community.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, communitySelect, sparams); + + if (tri.hasNext()) + { + Object[] params = { new Integer(count), new Integer(community.getID()) }; + DatabaseManager.updateQuery(context, communityUpdate, params); + } + else + { + Object[] params = { new Integer(community.getID()), new Integer(count) }; + DatabaseManager.updateQuery(context, communityInsert, params); + } + + tri.close(); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Set the dspace context to use + * + * @param context + * @throws ItemCountException + */ + public void setContext(Context context) + throws ItemCountException + { + this.context = context; + } + + /** + * get the count of the items in the given container + * + * @param dso + * @return + * @throws ItemCountException + */ + public int getCount(DSpaceObject dso) + throws ItemCountException + { + if (dso instanceof Collection) + { + return getCollectionCount((Collection) dso); + } + else if (dso instanceof Community) + { + return getCommunityCount((Community) dso); + } + else + { + throw new ItemCountException("We can only count items in Communities or Collections"); + } + } + + /** + * remove the cache for the given container + * + * @param dso + * @throws ItemCountException + */ + public void remove(DSpaceObject dso) throws ItemCountException + { + if (dso instanceof Collection) + { + removeCollection((Collection) dso); + } + else if (dso instanceof Community) + { + removeCommunity((Community) dso); + } + else + { + throw new ItemCountException("We can only delete count of items from Communities or Collections"); + } + } + + /** + * remove the cache for the given collection + * + * @param collection + * @throws ItemCountException + */ + private void removeCollection(Collection collection) + throws ItemCountException + { + try + { + Object[] params = { new Integer(collection.getID()) }; + DatabaseManager.updateQuery(context, collectionRemove, params); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Remove the cache for the given community + * + * @param community + * @throws ItemCountException + */ + private void removeCommunity(Community community) + throws ItemCountException + { + try + { + Object[] params = { new Integer(community.getID()) }; + DatabaseManager.updateQuery(context, communityRemove, params); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Get the count for the given collection + * + * @param collection + * @return + * @throws ItemCountException + */ + private int getCollectionCount(Collection collection) + throws ItemCountException + { + try + { + Object[] params = { new Integer(collection.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, collectionSelect, params); + + if (!tri.hasNext()) + { + return 0; + } + + TableRow tr = tri.next(); + + if (tri.hasNext()) + { + throw new ItemCountException("More than one count row in the database"); + } + + tri.close(); + + return tr.getIntColumn("number"); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * get the count for the given community + * + * @param community + * @return + * @throws ItemCountException + */ + private int getCommunityCount(Community community) + throws ItemCountException + { + try + { + Object[] params = { new Integer(community.getID()) }; + TableRowIterator tri = DatabaseManager.query(context, communitySelect, params); + + if (!tri.hasNext()) + { + return 0; + } + + TableRow tr = tri.next(); + + if (tri.hasNext()) + { + throw new ItemCountException("More than one count row in the database"); + } + + tri.close(); + + return tr.getIntColumn("number"); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } +} diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountException.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountException.java new file mode 100644 index 0000000000..4be585b035 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountException.java @@ -0,0 +1,66 @@ +/* + * ItemCountException.java + * + * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +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); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java b/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java new file mode 100644 index 0000000000..34858e3d5e --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/browse/ItemCounter.java @@ -0,0 +1,280 @@ +/* + * ItemCounter.java + * + * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package org.dspace.browse; + +import org.apache.log4j.Logger; +import org.dspace.content.Community; +import org.dspace.content.Collection; +import org.dspace.core.Context; +import org.dspace.content.DSpaceObject; +import org.dspace.core.ConfigurationManager; + +import java.sql.SQLException; + +/** + * 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: + * + * 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 + * real time counts of content. + * + * @author Richard Jones + * + */ +public class ItemCounter +{ + /** Log4j logger */ + private static Logger log = Logger.getLogger(ItemCounter.class); + + /** DAO to use to store and retrieve data */ + private ItemCountDAO dao; + + /** DSpace Context */ + private Context context; + + /** + * method invoked by CLI which will result in the number of items + * in each community and collection being cached. These counts will + * not update themselves until this is run again. + * + * @param args + */ + public static void main(String[] args) + throws ItemCountException + { + ItemCounter ic = new ItemCounter(); + ic.buildItemCounts(); + ic.completeContext(); + } + + /** + * Construct a new item counter which will create its own + * DSpace Context + * + * @throws ItemCountException + */ + public ItemCounter() + throws ItemCountException + { + try + { + this.context = new Context(); + this.dao = ItemCountDAOFactory.getInstance(this.context); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Construct a new item counter which will use the give DSpace Context + * + * @param context + * @throws ItemCountException + */ + public ItemCounter(Context context) + throws ItemCountException + + { + this.context = context; + this.dao = ItemCountDAOFactory.getInstance(this.context); + } + + /** + * Complete the context being used by this class. This exists so that + * instances of this class which created their own instance of the + * DSpace Context can also terminate it. For Context object which were + * passed in by the constructor, the caller is responsible for + * either calling this method themselves or completing the context + * when they need to. + * + * @throws ItemCountException + */ + public void completeContext() + throws ItemCountException + { + try + { + this.context.complete(); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * This method does the grunt work of drilling through and iterating + * over all of the communities and collections in the system and + * obtaining and caching the item counts for each one. + * + * @throws ItemCountException + */ + public void buildItemCounts() + throws ItemCountException + { + try + { + Community[] tlc = Community.findAllTop(context); + for (int i = 0; i < tlc.length; i++) + { + count(tlc[i]); + } + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * Get the count of the items in the given container. If the configuration + * value webui.strengths.cache is equal to 'true' this will return the + * cached value if it exists. If it is equal to 'false' it will count + * the number of items in the container in real time + * + * @param dso + * @return + * @throws ItemCountException + */ + public int getCount(DSpaceObject dso) + throws ItemCountException + { + boolean useCache = ConfigurationManager.getBooleanProperty("webui.strengths.cache"); + + if (useCache) + { + return dao.getCount(dso); + } + + // if we make it this far, we need to manually count + if (dso instanceof Collection) + { + return ((Collection) dso).countItems(); + } + + if (dso instanceof Community) + { + return ((Collection) dso).countItems(); + } + + return 0; + } + + /** + * Remove any cached data for the given container + * + * @param dso + * @throws ItemCountException + */ + public void remove(DSpaceObject dso) + throws ItemCountException + { + dao.remove(dso); + } + + /** + * count and cache the number of items in the community. This + * will include all sub-communities and collections in the + * community. It will also recurse into sub-communities and + * collections and call count() on them also. + * + * Therefore, the count the contents of the entire system, it is + * necessary just to call this method on each top level community + * + * @param community + * @throws ItemCountException + */ + private void count(Community community) + throws ItemCountException + { + try + { + // first count the community we are in + int count = community.countItems(); + dao.communityCount(community, count); + + // now get the sub-communities + Community[] scs = community.getSubcommunities(); + for (int i = 0; i < scs.length; i++) + { + count(scs[i]); + } + + // now get the collections + Collection[] cols = community.getCollections(); + for (int i = 0; i < cols.length; i++) + { + count(cols[i]); + } + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } + + /** + * count and cache the number of items in the given collection + * + * @param collection + * @throws ItemCountException + */ + private void count(Collection collection) + throws ItemCountException + { + try + { + int ccount = collection.countItems(); + dao.collectionCount(collection, ccount); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new ItemCountException(e); + } + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java index c04c9dc3f0..3f308f005b 100644 --- a/dspace-api/src/main/java/org/dspace/content/Collection.java +++ b/dspace-api/src/main/java/org/dspace/content/Collection.java @@ -1053,6 +1053,19 @@ public class Collection extends DSpaceObject wsarray[x].deleteAll(); } + // get rid of the content count cache if it exists + try + { + ItemCounter ic = new ItemCounter(ourContext); + ic.remove(this); + } + catch (ItemCountException e) + { + // FIXME: upside down exception handling due to lack of good + // exception framework + throw new SQLException(e); + } + // Delete collection row DatabaseManager.delete(ourContext, collectionRow); diff --git a/dspace-api/src/main/java/org/dspace/content/Community.java b/dspace-api/src/main/java/org/dspace/content/Community.java index e932cc9242..334d56f019 100644 --- a/dspace-api/src/main/java/org/dspace/content/Community.java +++ b/dspace-api/src/main/java/org/dspace/content/Community.java @@ -906,6 +906,19 @@ public class Community extends DSpaceObject // Remove all authorization policies AuthorizeManager.removeAllPolicies(ourContext, this); + // get rid of the content count cache if it exists + try + { + ItemCounter ic = new ItemCounter(ourContext); + ic.remove(this); + } + catch (ItemCountException e) + { + // FIXME: upside down exception handling due to lack of good + // exception framework + throw new SQLException(e); + } + // Delete community row DatabaseManager.delete(ourContext, communityRow); } diff --git a/dspace-jspui/src/main/webapp/collection-home.jsp b/dspace-jspui/src/main/webapp/collection-home.jsp index 9fc48efc38..8f0cafb4d6 100644 --- a/dspace-jspui/src/main/webapp/collection-home.jsp +++ b/dspace-jspui/src/main/webapp/collection-home.jsp @@ -67,7 +67,8 @@ <%@ page import="org.dspace.core.ConfigurationManager"%> <%@ page import="org.dspace.eperson.Group" %> <%@ page import="org.dspace.browse.BrowseIndex" %> - +<%@ page import="org.dspace.browse.ItemCounter" %> + <%@ page import="org.dspace.app.webui.components.RecentSubmissions" %> <%@ page import="org.dspace.content.Item" %> <%@ page import="org.dspace.content.DCValue" %> @@ -127,6 +128,8 @@ { feedData = "coll:" + ConfigurationManager.getProperty("webui.feed.formats"); } + + ItemCounter ic = new ItemCounter(); %> @@ -139,7 +142,7 @@ if(ConfigurationManager.getBooleanProperty("webui.strengths.show")) { %> - : [<%= collection.countItems() %>] + : [<%= ic.getCount(collection) %>] <% } %> diff --git a/dspace-jspui/src/main/webapp/community-home.jsp b/dspace-jspui/src/main/webapp/community-home.jsp index 6259cfa0fa..e0068f5291 100644 --- a/dspace-jspui/src/main/webapp/community-home.jsp +++ b/dspace-jspui/src/main/webapp/community-home.jsp @@ -64,7 +64,8 @@ <%@ page import="org.dspace.core.Utils" %> <%@ page import="org.dspace.core.ConfigurationManager" %> <%@ page import="org.dspace.browse.BrowseIndex" %> - +<%@ page import="org.dspace.browse.ItemCounter" %> + <%@ page import="org.dspace.app.webui.components.RecentSubmissions" %> <%@ page import="org.dspace.content.Item" %> <%@ page import="org.dspace.content.DCValue" %> @@ -104,6 +105,8 @@ { feedData = "comm:" + ConfigurationManager.getProperty("webui.feed.formats"); } + + ItemCounter ic = new ItemCounter(); %> @@ -111,7 +114,16 @@
-

<%= name %>

+

<%= name %> + <% + if(ConfigurationManager.getBooleanProperty("webui.strengths.show")) + { +%> + : [<%= ic.getCount(community) %>] +<% + } +%> +

@@ -209,7 +221,7 @@ if(ConfigurationManager.getBooleanProperty("webui.strengths.show")) { %> - [<%= collections[i].countItems() %>] + [<%= ic.getCount(collections[i]) %>] <% } %> @@ -259,7 +271,7 @@ if (ConfigurationManager.getBooleanProperty("webui.strengths.show")) { %> - [<%= subcommunities[j].countItems() %>] + [<%= ic.getCount(subcommunities[j]) %>] <% } %> diff --git a/dspace-jspui/src/main/webapp/community-list.jsp b/dspace-jspui/src/main/webapp/community-list.jsp index 34448c02b1..c1a6e6dbd1 100644 --- a/dspace-jspui/src/main/webapp/community-list.jsp +++ b/dspace-jspui/src/main/webapp/community-list.jsp @@ -62,7 +62,8 @@ <%@ page import="org.dspace.content.Collection" %> <%@ page import="org.dspace.app.webui.servlet.admin.EditCommunitiesServlet" %> <%@ page import="org.dspace.core.ConfigurationManager" %> -<%@ page import="javax.servlet.jsp.jstl.fmt.LocaleSupport" %> +<%@ page import="org.dspace.browse.ItemCounter" %> +<%@ page import="org.dspace.browse.ItemCountException" %> <%@ taglib uri="http://www.dspace.org/dspace-tags.tld" prefix="dspace" %> @@ -73,6 +74,7 @@ Boolean admin_b = (Boolean)request.getAttribute("admin_button"); boolean admin_button = (admin_b == null ? false : admin_b.booleanValue()); boolean showAll = true; + ItemCounter ic = new ItemCounter(); %> <%! @@ -87,8 +89,15 @@ void showCommunity(Community c) throws IOException, SQLException { + try + { + ItemCounter ic = new ItemCounter(); out.println( "
  • " ); out.println( "" + c.getMetadata("name") + ""); + if(ConfigurationManager.getBooleanProperty("webui.strengths.show")) + { + out.println(" [" + ic.getCount(c) + "]"); + } // Get the collections in this community Collection[] cols = c.getCollections(); @@ -101,7 +110,7 @@ out.println("" + cols[j].getMetadata("name") +""); if(ConfigurationManager.getBooleanProperty("webui.strengths.show")) { - out.println(" [" + cols[j].countItems() + "]"); + out.println(" [" + ic.getCount(cols[j]) + "]"); } out.println("
  • "); @@ -122,6 +131,12 @@ } out.println("
    "); out.println(""); + } + catch (ItemCountException e) + { + // FIXME: except it's not + throw new SQLException(e); + } } %> @@ -216,7 +231,7 @@ if (ConfigurationManager.getBooleanProperty("webui.strengths.show")) { %> - [<%= cols[j].countItems() %>] + [<%= ic.getCount(cols[j]) %>] <% } %> @@ -241,7 +256,7 @@ if (ConfigurationManager.getBooleanProperty("webui.strengths.show")) { %> - [<%= comms[k].countItems() %>] + [<%= ic.getCount(comms[k]) %>] <% } %> diff --git a/dspace-jspui/src/main/webapp/home.jsp b/dspace-jspui/src/main/webapp/home.jsp index 2ee5269117..0802bd4458 100644 --- a/dspace-jspui/src/main/webapp/home.jsp +++ b/dspace-jspui/src/main/webapp/home.jsp @@ -60,6 +60,7 @@ <%@ page import="org.dspace.app.webui.util.UIUtil" %> <%@ page import="org.dspace.content.Community" %> <%@ page import="org.dspace.core.ConfigurationManager" %> +<%@ page import="org.dspace.browse.ItemCounter" %> <% Community[] communities = (Community[]) request.getAttribute("communities"); @@ -77,6 +78,7 @@ feedData = "ALL:" + ConfigurationManager.getProperty("webui.feed.formats"); } + ItemCounter ic = new ItemCounter(); %> @@ -145,7 +147,7 @@ for (int i = supportedLocales.length-1; i >= 0; i--) if (ConfigurationManager.getBooleanProperty("webui.strengths.show")) { %> - [<%= communities[i].countItems() %>] + [<%= ic.getCount(communities[i]) %>] <% } diff --git a/dspace-jspui/src/main/webapp/styles.css.jsp b/dspace-jspui/src/main/webapp/styles.css.jsp index 8a56f2a567..b5bda7b562 100644 --- a/dspace-jspui/src/main/webapp/styles.css.jsp +++ b/dspace-jspui/src/main/webapp/styles.css.jsp @@ -253,6 +253,11 @@ UL { font-family: "verdana", "Arial", "Helvetica", sans-serif; font-size: 14pt; font-weight: bold } +.communityStrength { + font-family: "verdana", "Arial", "Helvetica", sans-serif; + font-size: 12pt; + font-weight: normal } + .communityDescription { margin-left: 20px; margin-right: 10px; font-family: "verdana", "Arial", "Helvetica", sans-serif; diff --git a/dspace/CHANGES b/dspace/CHANGES index f173861a94..53628a86f1 100644 --- a/dspace/CHANGES +++ b/dspace/CHANGES @@ -1,4 +1,4 @@ -(Graham Triggs/Mark Diggory +(Graham Triggs/Mark Diggory) - S.F. Bug 1824710 Creative Commons RDF has changed. Adjusted CC processing to be XSL driven and adjusted generated XML/RDF. Created LicenseCleanup CLI to process existing CC license_rdf @@ -11,6 +11,7 @@ SF Patch 1794700 Bug fix for stat-monthly and stat-report-monthly (Richard Jones) - S.F. Patch 1670093 More stable metadata and schema registry import +- Option to generate community and collection "strength" as a batch job (Richard Jones / Graham Triggs) - New Browse code that allows customisation of the available indexes via dspace.cfg, diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 93a2119f9d..f258840e62 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -313,9 +313,26 @@ webui.preview.brand.font = SansSerif webui.preview.brand.fontpoint = 12 #webui.preview.dc = rights +##### Settings for content count/strength information #### + # whether to display collection and community strengths +# webui.strengths.show = false +# if showing the strengths, should they be counted in real time or +# fetched from cache? +# +# Counts fetched in real time will perform an actual count of the +# database contents every time a page with this feature is requested, +# which will not scale. If the below setting is to use the cache, you +# must run the following command periodically to update the count: +# +# [dspace]/bin/dsrun org.dspace.browse.ItemCounter +# +# The default is to count in real time +# +webui.strengths.cache = false + # whether to display the contents of the licence bundle (often just the deposit # licence in standard DSpace installation webui.licence_bundle.show = false diff --git a/dspace/etc/database_schema.sql b/dspace/etc/database_schema.sql index 7e504a6c08..6d92126d88 100644 --- a/dspace/etc/database_schema.sql +++ b/dspace/etc/database_schema.sql @@ -736,6 +736,19 @@ FROM ItemsBySubject, Communities2Item WHERE ItemsBySubject.item_id = Communities2Item.item_id ; +------------------------------------------------------------------------- +-- Tables to manage cache of item counts for communities and collections +------------------------------------------------------------------------- + +CREATE TABLE collection_item_count ( + collection_id INTEGER REFERENCES collection(collection_id), + number INTEGER +); + +CREATE TABLE community_item_count ( + community_id INTEGER REFERENCES community(community_id), + number INTEGER +); ------------------------------------------------------- -- Create 'special' groups, for anonymous access diff --git a/dspace/etc/database_schema_14-15.sql b/dspace/etc/database_schema_14-15.sql index 492edc022b..6a4a74eef4 100644 --- a/dspace/etc/database_schema_14-15.sql +++ b/dspace/etc/database_schema_14-15.sql @@ -61,3 +61,17 @@ alter table bundle drop column mets_bitstream_id; -- totally unused column -- of the last page reached within a step in the Configurable Submission Process ------------------------------------------------------------------------------- ALTER TABLE workspaceitem ADD page_reached INTEGER; + +------------------------------------------------------------------------- +-- Tables to manage cache of item counts for communities and collections +------------------------------------------------------------------------- + +CREATE TABLE collection_item_count ( + collection_id INTEGER REFERENCES collection(collection_id), + number INTEGER +); + +CREATE TABLE community_item_count ( + community_id INTEGER REFERENCES community(community_id), + number INTEGER +); \ No newline at end of file