Java tutorial
/********************************************************************************** * $URL$ * $Id$ *********************************************************************************** * * Copyright (c) 2006, 2007, 2008, 2009 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.citation.impl; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.Random; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osid.OsidContext; import org.osid.OsidException; import org.osid.repository.Asset; import org.osid.repository.AssetIterator; import org.osid.repository.Repository; import org.osid.repository.RepositoryException; import org.osid.repository.RepositoryIterator; import org.osid.repository.RepositoryManager; import org.osid.shared.ObjectIterator; import org.osid.shared.SharedException; import org.osid.shared.Type; import org.osid.shared.TypeIterator; import org.sakaibrary.xserver.session.MetasearchSessionManager; import org.sakaiproject.authz.api.SecurityAdvisor; import org.sakaiproject.authz.api.SecurityAdvisor.SecurityAdvice; import org.sakaiproject.authz.cover.SecurityService; import org.sakaiproject.citation.util.api.CQLSearchQuery; import org.sakaiproject.citation.util.api.OsidConfigurationException; import org.sakaiproject.citation.util.api.SearchCancelException; import org.sakaiproject.citation.util.api.SearchQuery; import org.sakaiproject.citation.api.ActiveSearch; import org.sakaiproject.citation.api.Citation; import org.sakaiproject.citation.api.CitationCollection; import org.sakaiproject.citation.api.CitationIterator; import org.sakaiproject.citation.api.ConfigurationService; import org.sakaiproject.citation.api.SearchCategory; import org.sakaiproject.citation.api.SearchDatabase; import org.sakaiproject.citation.api.SearchDatabaseHierarchy; import org.sakaiproject.citation.api.SearchManager; import org.sakaiproject.citation.cover.CitationService; import org.sakaiproject.citation.util.api.SearchException; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.content.cover.ContentHostingService; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.cover.EntityManager; import org.sakaiproject.event.api.Event; import org.sakaiproject.event.cover.EventTrackingService; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.ServerOverloadException; import org.sakaiproject.exception.TypeException; import org.sakaiproject.memory.api.Cache; import org.sakaiproject.memory.api.MemoryService; import org.sakaiproject.time.cover.TimeService; import org.sakaiproject.tool.api.SessionManager; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import edu.indiana.lib.twinpeaks.util.SessionContext; import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; /** * */ public class BaseSearchManager implements SearchManager, Observer { /** * Maximum number of consecutive duplicate result records we'll accept * before disabling the duplicate record check for the current search. * * The idea is to avoid displaying occasional duplicates, but don't * discard forever in the pathological case where we're only getting * duplicate results. */ protected static int MAX_DUPLICATES = 10; public class BasicObjectIterator implements ObjectIterator { protected int i = 0; protected Vector vector = new Vector(); public BasicObjectIterator(List keys) throws SharedException { this.vector = new Vector(keys); } public boolean hasNextObject() throws SharedException { return i < vector.size(); } public java.io.Serializable nextObject() throws SharedException { if (i < vector.size()) { return (java.io.Serializable) vector.elementAt(i++); } else { throw new SharedException(SharedException.NO_MORE_ITERATOR_ELEMENTS); } } } public class BasicSearch implements ActiveSearch { protected List m_assets; protected List m_pageOrder; protected Set m_duplicateCheck; protected boolean m_duplicateCheckEnabled; protected boolean m_firstPage; protected String m_searchId; protected String m_searchType; protected boolean m_lastPage; protected boolean m_newSearch; protected Integer m_pageSize; protected Integer m_startRecord; protected String[] m_databaseIds; protected AssetIterator m_assetIterator; protected Integer m_numRecordsFetched; protected Integer m_numRecordsFound; protected Integer m_numRecordsMerged; protected Repository m_repository; protected String m_repositoryId; protected String m_repositoryName; protected SearchQuery m_basicQuery; protected SearchQuery m_advancedQuery; protected String m_sortBy; protected CitationCollection m_searchResults; protected CitationCollection m_savedResults; protected CitationIterator m_resultsIterator; protected Map m_index; protected int m_lastPageViewed = -1; protected CitationIterator m_searchIterator; protected int start = 1; protected int end = DEFAULT_PAGE_SIZE; protected int m_viewPageSize = DEFAULT_PAGE_SIZE; protected String statusMessage = null; // saves the thread that the current search is running in protected Thread m_searchThread; /** * Constructor */ public BasicSearch() { this.m_searchId = newSearchId(); this.m_searchType = null; this.m_assets = new Vector(); m_pageOrder = new Vector(); this.m_index = new Hashtable(); m_duplicateCheck = new TreeSet(); m_duplicateCheckEnabled = true; m_savedResults = CitationService.getTemporaryCollection(); m_newSearch = true; m_firstPage = true; m_lastPage = false; m_pageSize = new Integer(DEFAULT_PAGE_SIZE); m_startRecord = new Integer(DEFAULT_START_RECORD); m_sortBy = DEFAULT_SORT_BY; m_databaseIds = null; } /** */ public BasicSearch(CitationCollection searchResults) { this.m_searchId = newSearchId(); this.m_assets = new Vector(); m_pageOrder = new Vector(); this.m_index = new Hashtable(); m_duplicateCheck = new TreeSet(); m_duplicateCheckEnabled = true; m_savedResults = CitationService.getTemporaryCollection(); m_newSearch = true; m_firstPage = true; m_lastPage = false; m_pageSize = new Integer(DEFAULT_PAGE_SIZE); m_startRecord = new Integer(DEFAULT_START_RECORD); m_sortBy = DEFAULT_SORT_BY; m_databaseIds = null; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getAssetIterator() */ protected AssetIterator getAssetIterator() { return m_assetIterator; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getAssets() */ public List getAssets() { return m_assets; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getGuid() */ public String getSearchId() { return m_searchId; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getNumRecordsFetched() */ public Integer getNumRecordsFetched() { return m_numRecordsFetched; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getNumRecordsFound() */ public Integer getNumRecordsFound() { return m_numRecordsFound; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getNumRecordsMerged() */ public Integer getNumRecordsMerged() { return m_numRecordsMerged; } protected void setPageLimits(int page) throws SearchException { } /** * @param page * @return * @throws SearchException */ public List viewPage(int page) throws SearchException, SearchCancelException { List citations = new Vector(); boolean searchPerformed = false; if (page < 0) { page = 0; } int oldStart = this.start; int oldEnd = this.end; this.start = page * m_viewPageSize; this.end = start + m_viewPageSize; if (start > this.m_pageOrder.size() + 1) { throw new SearchException("Request beyond next page"); } else { if (this.m_pageOrder.isEmpty()) { doSearch(this); searchPerformed = true; } else if (end > this.m_pageOrder.size()) { try { doNextPage(this); searchPerformed = true; } catch (SearchException e) { this.start = oldStart; this.end = oldEnd; setStatusMessage(m_repository); throw new SearchException(e.getMessage()); } } } /* * Determine the proper the "last page" setting. */ m_log.debug(">>> viewPage() new page is " + page + ", last page is " + m_lastPageViewed); m_log.debug(">>> viewPage() was a search done? " + searchPerformed); m_log.debug(">>> viewPage() did we find the last page? " + this.isLastPage()); m_log.debug(">>> viewPage() records found = " + getNumRecordsFetched() + ", records rendered = " + m_pageOrder.size()); /* * Step 1: Previous pages (and the first) are a special case */ if (page < m_lastPageViewed) { setLastPage(false); } /* * Step 2: Re-evaluate the "last page" status if one of these is true: * * o This is a previous (or the first) page * o This was purely a page size adjustment (no search required) * o A search was performed (and it didn't hit "end-of-search-results") */ if ((page < m_lastPageViewed) || (!searchPerformed) || (searchPerformed && !isLastPage())) { int estimatedHits = getNumRecordsFound(); int hitsRendered = m_pageOrder.size(); int pageHits = (page == 0) ? m_viewPageSize : ((page + 1) * m_viewPageSize); /* * Step 3: This is the last page if: * * o The estimated number of possible results will fit on the * current page * or * o The number of results actually rendered is less than the * current page size (we ran out) */ m_log.debug(">>> viewPage() estimate (" + estimatedHits + ") <= page size (in hits) (" + pageHits + ") ? " + (estimatedHits <= pageHits)); m_log.debug(">>> viewPage() records rendered (" + hitsRendered + ") < page size (in hits) (" + pageHits + ") ? " + (hitsRendered < pageHits)); if ((estimatedHits <= pageHits) || (hitsRendered < pageHits)) { setLastPage(true); } } if (end > m_pageOrder.size()) { end = m_pageOrder.size(); } Citation citation = null; for (int i = start; i < end; i++) { String id = (String) m_pageOrder.get(i); try { citation = m_searchResults.getCitation(id); citations.add(citation); } catch (IdUnusedException e) { m_log.warn("BasicSearch.getPage() unable to retrieve ciataion: " + id); } } m_lastPageViewed = page; return citations; } /** * */ protected void setStatusMessage(Repository repository) { try { this.statusMessage = getSearchStatusMessage(repository); } catch (SearchException e) { this.statusMessage = e.getMessage(); } } public void setStatusMessage(String msg) { this.statusMessage = msg; } public void setStatusMessage() { this.statusMessage = null; } public String getStatusMessage() { return this.statusMessage; } /** * Set the selected list of searchable databases * @param A list of database IDs */ public void setDatabaseIds(String[] databaseIds) { m_databaseIds = databaseIds; } /** * Fetch the selected list of searchable databases * @return The list of database IDs (null if none) */ public String[] getDatabaseIds() { return m_databaseIds; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getPageSize() */ public Integer getPageSize() { return m_pageSize; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getRepository() */ public Repository getRepository() { return m_repository; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getRepositoryId() */ public String getRepositoryId() { return m_repositoryId; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getRepositoryName() */ public String getRepositoryName() { return m_repositoryName; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getSearchCriteria() */ public SearchQuery getBasicQuery() { return m_basicQuery; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getSortBy() */ public String getSortBy() { return m_sortBy.toLowerCase(); } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getStartRecord() */ public Integer getStartRecord() { if (m_startRecord.intValue() < MIN_START_RECORD) { m_startRecord = new Integer(MIN_START_RECORD); } return m_startRecord; } /** * @return the firstPage */ public boolean isFirstPage() { return m_firstPage; } /** * @return the lastPage */ public boolean isLastPage() { return m_lastPage; } /** * @return the newSearch */ public boolean isNewSearch() { return m_newSearch; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setAssetIterator(org.osid.repository.AssetIterator) */ public void setAssetIterator(AssetIterator assetIterator) { this.m_assetIterator = assetIterator; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setAssets(java.util.List) */ public void setAssets(List assets) { this.m_assets = assets; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setFirstPage(boolean) */ public void setFirstPage(boolean firstPage) { this.m_firstPage = firstPage; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setGuid(java.lang.String) */ public void setGuid(String guid) { this.m_searchId = guid; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setLastPage(boolean) */ public void setLastPage(boolean lastPage) { this.m_lastPage = lastPage; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setNewSearch(boolean) */ public void setNewSearch(boolean newSearch) { this.m_newSearch = newSearch; } /** * @param numRecordsFetched the numRecordsFetched to set */ public void setNumRecordsFetched(Integer numRecordsFetched) { m_numRecordsFetched = numRecordsFetched; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setNumRecordsFound(java.lang.Integer) */ public void setNumRecordsFound(Integer numRecordsFound) { this.m_numRecordsFound = numRecordsFound; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setNumRecordsMerged(java.lang.Integer) */ public void setNumRecordsMerged(Integer numRecordsMerged) { this.m_numRecordsMerged = numRecordsMerged; } /** * @param pageSize the pageSize to set */ public void setPageSize(Integer pageSize) { if (pageSize == null || pageSize.intValue() < 1) { m_pageSize = new Integer(DEFAULT_PAGE_SIZE); } else { m_pageSize = pageSize; } } /** * @param pageSize the pageSize to set */ public void setPageSize(String pageSize) { if (pageSize == null || pageSize.trim().equals("")) { m_pageSize = new Integer(DEFAULT_PAGE_SIZE); } else { try { m_pageSize = new Integer(pageSize); } catch (NumberFormatException e) { m_pageSize = new Integer(DEFAULT_PAGE_SIZE); } } } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setRepository(org.osid.repository.Repository) */ public void setRepository(Repository repository) { this.m_repository = repository; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setRepositoryId(java.lang.String) */ public void setRepositoryId(String repositoryId) { this.m_repositoryId = repositoryId; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setRepositoryName(java.lang.String) */ public void setRepositoryName(String repositoryName) { this.m_repositoryName = repositoryName; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setSearchCriteria(java.lang.String) */ public void setBasicQuery(SearchQuery basicQuery) { this.m_basicQuery = basicQuery; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setSortBy(java.lang.String) */ public void setSortBy(String sortBy) { this.m_sortBy = sortBy; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setStartRecord(java.lang.Integer) */ public void setStartRecord(Integer startRecord) { if (startRecord.intValue() < MIN_START_RECORD) { this.m_startRecord = new Integer(MIN_START_RECORD); } else { this.m_startRecord = startRecord; } } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getSearchResults() */ public CitationCollection getSearchResults() { return m_searchResults; } /** * @param searchResults the searchResults to set */ public void setSearchResults(CitationCollection searchResults) { m_searchResults = searchResults; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setStartRecord(java.lang.String) */ public void setStartRecord(String startRecord) { if (startRecord == null || startRecord.trim().equals("")) { m_startRecord = new Integer(DEFAULT_START_RECORD); } else { try { m_startRecord = new Integer(startRecord); } catch (NumberFormatException e) { m_startRecord = new Integer(DEFAULT_START_RECORD); } } } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getIndex() */ public Map getIndex() { return m_index; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#setIndex(java.util.Map) */ public void setIndex(Map index) { m_index = new Hashtable(index); } /** * @return */ public Set getDuplicateCheck() { if (m_duplicateCheck == null) { m_duplicateCheck = new TreeSet(); } return m_duplicateCheck; } /** * Are we checking for duplicate search results? * @return true if so */ public boolean isDuplicateCheckEnabled() { return m_duplicateCheckEnabled; } /** * Enable/disable duplicate checking * @param state true to enable the duplicate check */ public void setDuplicateCheckEnabled(boolean state) { m_duplicateCheckEnabled = state; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#prepareForNextPage() * * As far as I can tell, this is no longer used. At one time it was * referenced from CitationsHelperAction. SRS, 03/34/09 */ public void prepareForNextPage() { Iterator it = m_searchResults.getCitations().iterator(); while (it.hasNext()) { Citation citation = (Citation) it.next(); if (!m_pageOrder.contains(citation.getId())) { m_pageOrder.add(citation.getId()); } } this.m_savedResults.addAll(this.m_searchResults); this.m_searchResults.clear(); } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getLastPageViewed() */ public int getViewPageNumber() { return m_lastPageViewed; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#getLastPageViewed() */ public int getViewPageSize() { return m_viewPageSize; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.ActiveSearch#viewPage() */ public List viewPage() throws SearchException, SearchCancelException { return viewPage(0); } public int getFirstRecordIndex() { return start; } public int getLastRecordIndex() { return end; } public void setViewPageSize(int size) { m_viewPageSize = size; } public String getSearchType() { return m_searchType; } public void setSearchType(String searchType) { m_searchType = searchType; } public SearchQuery getAdvancedQuery() { return m_advancedQuery; } public void setAdvancedQuery(SearchQuery advancedQuery) { m_advancedQuery = advancedQuery; } public Thread getSearchThread() { return m_searchThread; } public void setSearchThread(Thread searchThread) { m_searchThread = searchThread; } public void resetSearch() { this.m_pageOrder.clear(); this.m_searchResults.clear(); } } public class BasicSearchProperties implements org.osid.shared.Properties { protected List keys; protected java.util.Properties properties; protected Type type = new BasicType("sakaibrary", "properties", "asynchMetasearch"); public BasicSearchProperties(java.util.Properties properties) { this.keys = new Vector(); this.properties = properties; Enumeration keyNames = properties.keys(); while (keyNames.hasMoreElements()) { this.keys.add((java.io.Serializable) keyNames.nextElement()); } } public ObjectIterator getKeys() throws SharedException { return new BasicObjectIterator(keys); } public java.io.Serializable getProperty(java.io.Serializable key) throws SharedException { return (java.io.Serializable) properties.get(key); } public Type getType() throws SharedException { return type; } } public class BasicType extends org.osid.shared.Type { protected BasicType(String authority, String domain, String keyword) { super(authority, domain, keyword); } public BasicType(String authority, String domain, String keyword, String description) { super(authority, domain, keyword, description); } // public final Type CITATION = new BasicType("sakaibrary", "recordStructure", "citation"); // public final Type CREATOR = new BasicType("mit.edu", "partStructure", "creator"); // public final Type DATE = new BasicType("mit.edu", "partStructure", "date"); // public final Type DATE_RETRIEVED = new BasicType("sakaibrary", "partStructure", "dateRetrieved"); // public final Type DOI = new BasicType("sakaibrary", "partStructure", "doi"); // public final Type EDITION = new BasicType("sakaibrary", "partStructure", "edition"); // public final Type END_PAGE = new BasicType("sakaibrary", "partStructure", "endPage"); // public final Type INLINE_CITATION = new BasicType("sakaibrary", "partStructure", "inLineCitation"); // public final Type ISN_IDENTIFIER = new BasicType("sakaibrary", "partStructure", "isnIdentifier"); // public final Type ISSUE = new BasicType("sakaibrary", "partStructure", "issue"); // public final Type LANGUAGE = new BasicType("mit.edu", "partStructure", "language"); // public final Type LOC_IDENTIFIER = new BasicType("sakaibrary", "partStructure", "locIdentifier"); // public final Type NOTE = new BasicType("sakaibrary", "partStructure", "note"); // public final Type OPEN_URL = new BasicType("sakaibrary", "partStructure", "openUrl"); // public final Type PAGES = new BasicType("sakaibrary", "partStructure", "pages"); // public final Type PUB_LOCATION = new BasicType("sakaibrary", "partStructure", "publicationLocation"); // public final Type PUBLISHER = new BasicType("mit.edu", "partStructure", "publisher"); // public final Type RIGHTS = new BasicType("sakaibrary", "partStructure", "rights"); // public final Type SOURCE_TITLE = new BasicType("sakaibrary", "partStructure", "sourceTitle"); // public final Type START_PAGE = new BasicType("sakaibrary", "partStructure", "startPage"); // public final Type SUBJECT = new BasicType("mit.edu", "partStructure", "subject"); // public final Type TYPE = new BasicType("mit.edu", "partStructure", "type"); // public final Type URL = new BasicType("mit.edu", "partStructure", "url"); // public final Type URL_FORMAT = new BasicType("sakaibrary", "partStructure", "urlFormat"); // public final Type URL_LABEL = new BasicType("sakaibrary", "partStructure", "urlLabel"); // public final Type VOLUME = new BasicType("sakaibrary", "partStructure", "volume"); // public final Type YEAR = new BasicType("sakaibrary", "partStructure", "year"); } /** * @author gbhatnag * */ public class BasicSearchDatabaseHierarchy extends org.xml.sax.helpers.DefaultHandler implements SearchDatabaseHierarchy { public class BasicSearchCategory implements SearchCategory { private String id; private String displayName; private String description; private boolean defaultStatus; // list of sub-categories contained in this category (could be null) private java.util.List<SearchCategory> subcategoryList; // list of database ids contained in this category (could be null) private java.util.List<String> databaseList; // list of database ids that are recommended within this category // (could be null) private java.util.List<String> recommendedDatabases; // map of databases with alternate metadata within this category // keyed using database id private java.util.Map<String, SearchDatabase> altDatabases; /** * BasicSearchCategory constructor creates a BasicSearchCategory * with the given name and id * * @param name display name for this category * @param id unique identifier for this category */ protected BasicSearchCategory(String name, String id) { this.id = id; this.displayName = name; this.description = null; this.defaultStatus = false; } protected void updateDescription(String description) { this.description = description; } protected void addSubcategory(SearchCategory subcategory) { if (subcategory != null) { if (subcategoryList == null) { subcategoryList = new Vector<SearchCategory>(); } subcategoryList.add(subcategory); } else { m_log.warn("BasicSearchCategory.addSubCategory() was " + "passed a null subcategory to add"); } } protected void addDatabase(String databaseId) { if (databaseId != null) { if (databaseList == null) { databaseList = new Vector<String>(); } databaseList.add(databaseId); } else { m_log.warn("BasicSearchCategory.addDatabase() was " + "passed a null databaseId to add"); } } protected void addRecommendedDatabase(String databaseId) { if (databaseId != null) { if (recommendedDatabases == null) { recommendedDatabases = new Vector<String>(); } recommendedDatabases.add(databaseId); // if this database is not in the overall list of databases, // add it if (databaseList == null) { databaseList = new Vector<String>(); } if (!databaseList.contains(databaseId)) { databaseList.add(databaseId); } } else { m_log.warn("BasicSearchCategory.addRecommendedDatabase()" + " was passed a null databaseId to add"); } } protected void addAlternateDatabase(SearchDatabase altDatabase) { if (altDatabase != null) { if (altDatabases == null) { altDatabases = new Hashtable<String, SearchDatabase>(); } altDatabases.put(altDatabase.getId(), altDatabase); // if this database is not in the overall list of databases, // add it if (!databaseList.contains(altDatabase.getId())) { databaseList.add(altDatabase.getId()); } } else { m_log.warn("BasicSearchCategory.addAlternateDatabase() " + "was passed a null SearchDatabase to add"); } } protected void setDefault(boolean value) { this.defaultStatus = value; } protected boolean isDefault() { return defaultStatus; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#hasDatabases() */ public boolean hasDatabases() { return (databaseList != null && !databaseList.isEmpty()); } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#getDatabases() */ public List<SearchDatabase> getDatabases() { // List to be returned Vector<SearchDatabase> databases = new Vector<SearchDatabase>(); // make sure this category has databases in it if (!hasDatabases()) { m_log.warn("Search Library Resources Category: '" + displayName + "' contains no databases."); } else { for (int i = 0; i < databaseList.size(); i++) { String databaseId = databaseList.get(i); SearchDatabase database; // check if there is an alternate for this database if (altDatabases != null && altDatabases.containsKey(databaseId)) { database = altDatabases.get(databaseId); } else { // get the database from the global map of databases database = databaseMap.get(databaseId); } // make sure we have found the database if (database == null) { // database not found m_log.warn("Unidentified Search Libary Resources " + "database: '" + databaseId + "' in category: " + displayName); } else { // check if the database is a member of authorized groups for (String groupId : groups) { if (database.isGroupMember(groupId)) { // add to the return List databases.add(database); break; } } } } } return databases; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#getDescription() */ public String getDescription() { return description; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#getDisplayName() */ public String getDisplayName() { return displayName; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#getId() */ public String getId() { return id; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#hasSubCategories() */ public boolean hasSubCategories() { return (subcategoryList != null && !subcategoryList.isEmpty()); } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#getSubCategories() */ public List<SearchCategory> getSubCategories() { return subcategoryList; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchCategory#isDatabaseRecommended() */ public boolean isDatabaseRecommended(String databaseId) { return recommendedDatabases.contains(databaseId); } } // public class BasicSearchCategory public class BasicSearchDatabase implements SearchDatabase { private String id; private String displayName; private String description; // groups this database belongs to private List<String> groups; protected BasicSearchDatabase(String name, String id) { this.displayName = name; this.id = id; this.description = null; } protected void updateDescription(String description) { this.description = description; } protected void addGroup(String groupId) { if (groupId != null) { if (groups == null) { groups = new Vector<String>(); } groups.add(groupId); } else { m_log.warn("BasicSearchDatabase.addGroup() " + "was passed a null groupId to add"); } } public String getDescription() { return description; } public String getDisplayName() { return displayName; } public String getId() { return id; } public boolean isGroupMember(String groupId) { return groups.contains(groupId); } } // public class BasicSearchDatabase /* org.sakaiproject.citation * BasicSearchDatabaseHierarchy instance variables */ // this user's repository and groups protected String repositoryPkgName; protected List<String> groups; // root category (contains top level categories) protected BasicSearchCategory rootCategory; // map containing all databases, keyed by database id protected java.util.Map<String, SearchDatabase> databaseMap; // map containing all categories, keyed by category id protected java.util.Map<String, SearchCategory> categoryMap; // default category protected SearchCategory defaultCategory; // configured flag protected boolean isConfigured; // for SAX parsing protected StringBuilder textBuffer; protected boolean recommendedDatabaseFlag; protected int hierarchyDepth; protected java.util.Stack<BasicSearchCategory> categoryStack; protected BasicSearchDatabase currentDatabase; protected String currentDatabaseId; public BasicSearchDatabaseHierarchy(String xmlContent) { /* * Any basic user authn/authz things we can check to not go * further than we have to? */ // get a ConfigurationService instance if (m_configService == null) { m_log.warn("BasicSearchDatabaseHierarchy() m_configService is " + "null - components.xml injection did not work... getting instance from cover"); m_configService = org.sakaiproject.citation.cover.ConfigurationService.getInstance(); } isConfigured = false; try { /* * Determine which repository implementation this user should get * access to * - ip-based * - other things? * * (currently assuming X-Server) */ // repositoryPkgName = "org.sakaibrary.osid.repository.xserver"; repositoryPkgName = m_configService.getSiteConfigOsidPackageName(); if (isNull(repositoryPkgName)) { // cannot continue return; } /* * Now we know which metasearch engine, get the corresponding XML * for that database * - XML describes all accessible databases/categories for a given * metasearch engine * * (currently assuming CATEGORIES_XML for all users) */ /* * Determine which groups this user is a member of * - should get an array of strings with group names/ids * which should appear in the XML */ // String[] tempGroups = { "all", "free" }; // groups = tempGroups; groups = m_configService.getGroupIds(); /* * Parse the XML using the group information to build a hierarchy * of categories and databases this user has access to */ recommendedDatabaseFlag = false; hierarchyDepth = 0; databaseMap = new java.util.Hashtable<String, SearchDatabase>(); categoryMap = new java.util.Hashtable<String, SearchCategory>(); categoryStack = new java.util.Stack<BasicSearchCategory>(); parseXML(xmlContent); } catch (Exception exception) { m_log.warn("Exception seen in BasicSearchDatabaseHierarchy() constructor", exception); } } protected void parseXML(String xmlContent) { // Use the default (non-validating) parser SAXParserFactory factory = SAXParserFactory.newInstance(); InputSource source = new InputSource(new StringReader(xmlContent)); try { // Parse the input SAXParser saxParser = factory.newSAXParser(); saxParser.parse(source, this); m_log.debug("After parse, categories found = " + categoryMap.size()); isConfigured = (this.categoryMap.size() > 0) ? true : false; } catch (SAXParseException spe) { // Use the contained exception, if any Exception x = spe; if (spe.getException() != null) { x = spe.getException(); } // Error generated by the parser m_log.warn("parseXML() parsing exception: " + spe.getMessage() + " - xml line " + spe.getLineNumber() + ", uri " + spe.getSystemId(), x); // unset configuration flag isConfigured = false; } catch (SAXException sxe) { // Error generated by this application // (or a parser-initialization error) Exception x = sxe; if (sxe.getException() != null) { x = sxe.getException(); } m_log.warn("parseXML() SAX exception: " + sxe.getMessage(), x); // unset configuration flag isConfigured = false; } catch (ParserConfigurationException pce) { // Parser with specified options can't be built m_log.warn("parseXML() SAX parser cannot be built " + "with specified options"); // unset configuration flag isConfigured = false; } catch (IOException ioe) { // I/O error m_log.warn("parseXML() IO exception", ioe); // unset configuration flag isConfigured = false; } catch (Throwable t) { m_log.warn("parseXML() exception", t); // unset configuration flag isConfigured = false; } } protected void setDefaultCategory(SearchCategory defaultCategory) { if (defaultCategory != null) { this.defaultCategory = defaultCategory; } else { m_log.warn("BasicSearchDatabaseHierarchy.setDefaultCategory()" + " was passed a null SearchCategory to set"); } } protected void addTopLevelCategory(SearchCategory topLevelCategory) { if (topLevelCategory != null) { if (rootCategory == null) { rootCategory = new BasicSearchCategory(SearchDatabaseHierarchy.ROOT_CATEGORY_NAME, SearchDatabaseHierarchy.ROOT_CATEGORY_ID); } rootCategory.addSubcategory(topLevelCategory); } else { m_log.warn("BasicSearchDatabaseHierarchy.addTopLevelCategory()" + " was passed a null SearchCategory to add"); } } public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException { String eName = sName; // element name if (eName.equals("")) { eName = qName; // not namespaceAware } if (eName.equals("category")) { // create a new category with the given attribute info BasicSearchCategory newCategory = new BasicSearchCategory(attrs.getValue("name"), attrs.getValue("id")); // check if this is the default category if (attrs.getValue("default") != null) { newCategory.setDefault(true); } // add new category to the stack categoryStack.push(newCategory); } else if (eName.equals("database")) { // create a new database with the given attribute info currentDatabase = new BasicSearchDatabase(attrs.getValue("name"), attrs.getValue("id")); } else if (eName.equals("category_database")) { // determine whether this is a "recommended" database if (attrs.getValue("recommended") != null) { recommendedDatabaseFlag = true; } } } public void endElement(String namespaceURI, String sName, String qName) throws SAXException { String eName = sName; // element name if (eName.equals("")) { eName = qName; // not namespaceAware } parseData(eName); } public void characters(char[] buf, int offset, int len) throws SAXException { String s = new String(buf, offset, len); if (textBuffer == null) { textBuffer = new StringBuilder(s); } else { textBuffer.append(s); } } protected String getAttribute(Attributes attrs, String attrName) { if (attrs != null) { for (int i = 0; i < attrs.getLength(); i++) { String name = attrs.getLocalName(i); if (name.equals("")) { name = attrs.getQName(i); } if (name.equals(attrName)) { return attrs.getValue(i); } } } return null; } protected void parseData(String endElement) { String text = null; if (textBuffer != null) { text = textBuffer.toString().trim(); } /* * category elements */ if (endElement.equals("category_description")) { BasicSearchCategory temp = categoryStack.pop(); temp.updateDescription(text); categoryStack.push(temp); } else if (endElement.equals("category")) { // a category has just ended // attach it to its proper hierarchy container if (!categoryStack.peek().isDefault()) { // add category to the category map categoryMap.put(categoryStack.peek().getId(), categoryStack.peek()); if (categoryStack.size() == 1) { // at the top level addTopLevelCategory(categoryStack.pop()); } else { // not at the top level // determine hierarchy depth if (hierarchyDepth < categoryStack.size()) { hierarchyDepth = categoryStack.size(); } // add current subcategory to parent category BasicSearchCategory subcategory = categoryStack.pop(); BasicSearchCategory parentCategory = categoryStack.pop(); parentCategory.addSubcategory(subcategory); categoryStack.push(parentCategory); } } else { // TODO this assumes the default category is outside of the // hierarchy (it is not attached to any parent container) defaultCategory = categoryStack.pop(); } } /* * category_database elements */ else if (endElement.equals("id")) { currentDatabaseId = text; // is this database recommended? if (recommendedDatabaseFlag) { recommendedDatabaseFlag = false; BasicSearchCategory temp = categoryStack.pop(); temp.addRecommendedDatabase(text); categoryStack.push(temp); } else { BasicSearchCategory temp = categoryStack.pop(); temp.addDatabase(text); categoryStack.push(temp); } } else if (endElement.equals("alt_name")) { currentDatabase = new BasicSearchDatabase(text, currentDatabaseId); } else if (endElement.equals("alt_description")) { currentDatabase.updateDescription(text); } else if (endElement.equals("category_database")) { if (currentDatabase != null) { BasicSearchCategory temp = categoryStack.pop(); temp.addAlternateDatabase(currentDatabase); categoryStack.push(temp); currentDatabase = null; } } /* * database elements */ else if (endElement.equals("database_description")) { currentDatabase.updateDescription(text); } else if (endElement.equals("database_group")) { currentDatabase.addGroup(text); } else if (endElement.equals("database")) { // a database has just ended - add to databaseMap databaseMap.put(currentDatabase.getId(), currentDatabase); currentDatabase = null; } textBuffer = null; } public SearchCategory getCategory(String categoryId) { if (categoryId.equals(defaultCategory.getId())) { return defaultCategory; } else { return categoryMap.get(categoryId); } } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getNumLevels() */ public int getNumLevels() { return hierarchyDepth; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getNumMaxSearchableDb() */ public int getNumMaxSearchableDb() { int number = m_configService.getSiteConfigMaximumSearchableDBs(); m_log.debug("getNumMaxSearchableDb() returns " + number); return number; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getTopLevelCategories() */ public List<SearchCategory> getCategoryListing() { // return list List<SearchCategory> categoryListing = new java.util.ArrayList<SearchCategory>(); // add root category to return list categoryListing.add(rootCategory); // iterate through all categories (starting at root) // and add them to the return list for (int i = 0; i <= categoryMap.size(); i++) { SearchCategory category = categoryListing.get(i); if (category.hasSubCategories()) { for (SearchCategory cat : category.getSubCategories()) { categoryListing.add(cat); } } } return categoryListing; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getRepository() */ public Repository getRepository() { Repository repository = null; // get a RepositoryManager RepositoryManager repositoryManager = null; try { repositoryManager = (RepositoryManager) SakaiOsidLoader.getManager( "org.osid.repository.RepositoryManager", repositoryPkgName, new OsidContext(), null); RepositoryIterator rit = null; if (repositoryManager == null) { m_log.warn("getRepository() failed getting RepositoryManager from SakaiOsidLoader"); } else { rit = repositoryManager.getRepositoriesByType(repositoryType); } // get repositories of type sakaibrary/repository/metasearch if (rit == null) { m_log.warn( "getRepository() failed getting RepositoryIterator of type sakaibrary/repository/metasearch from RepositoryManager"); return null; } else { // only one repository should be in the iterator repository = rit.nextRepository(); String extendedId = m_configService.getSiteConfigExtendedRepositoryId(); if ((extendedId != null) && (extendedId.length() > 0)) { while (repository != null) { m_log.debug("Matching Repositories? " + repository.getId().getIdString() + " VS " + extendedId); if (repository.getId().getIdString().equals(extendedId)) { break; } repository = rit.nextRepository(); } } } if (repository == null) { m_log.warn("getRepository() failed getting repository from RepositoryIterator"); } } catch (OsidException oe) { m_log.warn("getRepository threw OsidException: ", oe); } return repository; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getDefaultCategory() */ public SearchCategory getDefaultCategory() { return defaultCategory; } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#isSearchableDatabase(java.lang.String) */ public boolean isSearchableDatabase(String databaseId) { return databaseMap.containsKey(databaseId); } public boolean isConfigured() { return isConfigured; } } // public class BasicSearchDatabaseHierarchy /** Our logger. */ private static Log m_log = LogFactory.getLog(BaseSearchManager.class); // our ConfigurationService (gets set in BaseSearchDatabaseHierarchy) // google scholar constants public static final String SAKAI_SESSION = "sakai.session.key"; public static final String SAKAI_KEY = "sakai.key"; public static final String SAKAI_HOST = "sakai.host"; //public static final String SERVLET_NAME = "savecite"; public static final String SERVLET_NAME = "sakai-citations-servlet"; public static final String WINDOW_PREFIX = "WebLearn Solo - "; // Our types (defined in setupTypes()) protected static BasicType categoryAssetType; protected static BasicType databaseAssetType; protected static BasicType searchType; protected static BasicType repositoryType; // String array for databases being searched and database hierarchy protected Map<String, String> hierarchyMap = new HashMap<String, String>(); protected SortedSet<String> updatableResources = Collections.synchronizedSortedSet(new TreeSet<String>()); private static Random m_generator; /* * necessary services and managers (provided by components.xml) */ protected SessionManager m_sessionManager = null; protected ConfigurationService m_configService = null; /** Dependency: ServerConfigurationService. */ protected ServerConfigurationService serverConfigurationService = null; protected String databaseHierarchyResourceRef; private MemoryService memoryService; private Cache sessionContextCache; private Cache metasearchSessionManagerCache; public void setSessionManager(SessionManager sessionManager) { m_sessionManager = sessionManager; } public void setConfigurationService(ConfigurationService configService) { m_configService = configService; } public void destroy() { m_log.info("BaseSearchManager.destroy()"); } /* (non-Javadoc) * @see org.sakaiproject.citation.impl.SearchManager#doNextPage(org.sakaiproject.citation.api.ActiveSearch) */ public ActiveSearch doNextPage(ActiveSearch search) throws SearchException { Repository repository = ((BasicSearch) search).getRepository(); AssetIterator assetIterator = ((BasicSearch) search).getAssetIterator(); int last = search.getLastRecordIndex(); CitationCollection citations = search.getSearchResults(); if (citations == null) { citations = CitationService.getTemporaryCollection(); ((BasicSearch) search).setSearchResults(citations); } Set duplicateCheck = ((BasicSearch) search).getDuplicateCheck(); int duplicateCount = 0; boolean done = false; boolean moreResults = false; try { // poll until we get pageSize results (or run out of results) moreResults = assetIterator.hasNextAsset(); while (!done && moreResults) { try { Asset asset = assetIterator.nextAsset(); Citation citation = CitationService.getTemporaryCitation(asset); String dupCheckCriteria = citation.hasPreferredUrl() ? citation.getPrimaryUrl() : citation.getOpenurlParameters(); m_log.debug("DUP CHECK: " + dupCheckCriteria); if (((BasicSearch) search).isDuplicateCheckEnabled() && duplicateCheck.contains(dupCheckCriteria)) { m_log.debug("Duplicate #" + (duplicateCount + 1) + " found"); if (duplicateCount++ >= MAX_DUPLICATES) { ((BasicSearch) search).setDuplicateCheckEnabled(false); } // make sure we have more search results moreResults = assetIterator.hasNextAsset(); continue; } else { ((BasicSearch) search).m_pageOrder.add(citation.getId()); citations.add(citation); duplicateCheck.add(dupCheckCriteria); duplicateCount = 0; } // check if we've got enough to return done = (citations.size() >= last); } catch (RepositoryException re) { if (re.getMessage().equals(SESSION_TIMED_OUT) || re.getMessage().equals(METASEARCH_ERROR) || re.getMessage().equals(SharedException.NO_MORE_ITERATOR_ELEMENTS) || re.getMessage().equals(OsidException.OPERATION_FAILED)) { // search is over, all assets that have been retrieved have been // optionally check searchStatus Properties for further details or information to present in UI search.setLastPage(true); m_log.warn("doNextPage -- RepositoryException nextAsset(): " + re.getMessage()); String message = getSearchStatusMessage(repository); if (message == null) { throw new SearchException(re.getMessage()); } throw new SearchException(message); } else if (re.getMessage().equals(ASSET_NOT_FETCHED)) { // need to wait some time and then try again try { Thread.sleep(2500); // sleep 2.5 seconds } catch (InterruptedException ie) { search.setLastPage(true); m_log.warn("doNextPage -- InterruptedException nextAsset(): ", ie); String message = getSearchStatusMessage(repository); throw new SearchException(message); } } } // make sure we have more search results moreResults = assetIterator.hasNextAsset(); } } catch (RepositoryException re) { if (re.getMessage().equals(SESSION_TIMED_OUT) || re.getMessage().equals(METASEARCH_ERROR)) { search.setLastPage(true); // search is over, all assets that have been retrieved have been // optionally check searchStatus Properties for further details or information to present in UI m_log.warn("doNextPage -- RepositoryException hasNextAsset(): " + re.getMessage()); String message = getSearchStatusMessage(repository); throw new SearchException(message); } } // get search status properties Type statusType = getPropertyType(repository); org.osid.shared.Properties statusProperties = null; try { statusProperties = repository.getPropertiesByType(statusType); } catch (RepositoryException re) { search.setLastPage(true); String message = getSearchStatusMessage(repository); throw new SearchException(message); } Integer numRecordsFound = null; Integer numRecordsFetched = null; Integer numRecordsMerged = null; try { numRecordsFound = (Integer) statusProperties.getProperty("numRecordsFound"); numRecordsFetched = (Integer) statusProperties.getProperty("numRecordsFetched"); numRecordsMerged = (Integer) statusProperties.getProperty("numRecordsMerged"); } catch (SharedException se) { search.setLastPage(true); String message = getSearchStatusMessage(repository); throw new SearchException(message); } search.setNumRecordsFound(numRecordsFound); search.setNumRecordsFetched(numRecordsFetched); search.setNumRecordsMerged(numRecordsMerged); search.setNewSearch(false); search.setFirstPage(false); /* * disable the "next page" arrow if we've exhausted the search results */ if (!moreResults) { search.setLastPage(true); } else if (done) { search.setLastPage(false); } else { search.setLastPage(true); } return search; } /* (non-Javadoc) * @see org.sakaiproject.citation.impl.SearchManager#doPrevPage(org.sakaiproject.citation.api.ActiveSearch) */ public ActiveSearch doPrevPage(ActiveSearch search) throws SearchException { return search; } /* (non-Javadoc) * @see org.sakaiproject.citation.impl.SearchManager#doSearch(org.sakaiproject.citation.api.ActiveSearch) */ public ActiveSearch doSearch(ActiveSearch search) throws SearchException, SearchCancelException { // search parameters Integer pageSize = search.getPageSize(); Integer startRecord = search.getStartRecord(); String sortBy = search.getSortBy(); String guid = search.getSearchId(); String[] searchDbs = search.getDatabaseIds(); /* * Repository set up */ SearchDatabaseHierarchy hierarchy = getSearchHierarchy(); if (hierarchy == null) { throw new SearchException("ERROR: No appropriate database hierarchy available"); } Repository repository = hierarchy.getRepository(); // CQL search query setup String cqlQuery = null; CQLSearchQuery cqlSearch = new org.sakaiproject.citation.util.impl.CQLSearchQuery(); // determine whether this is an advanced or basic search if (search.getSearchType().equalsIgnoreCase(ActiveSearch.BASIC_SEARCH_TYPE)) { // get search criteria in CQL cqlQuery = cqlSearch.getCQLSearchQueryString(search.getBasicQuery()); } else { // get search criteria in CQL cqlQuery = cqlSearch.getCQLSearchQueryString(search.getAdvancedQuery()); } m_log.debug("CQL query: " + cqlQuery); // initiate the search try { if (cqlQuery == null) { // something went horrible throw new SearchException("ERROR: could not properly " + "convert search criteria to CQL."); } // set up search properties java.util.Properties properties = new java.util.Properties(); properties.put("guid", guid); properties.put("baseUrl", m_configService.getSiteConfigMetasearchBaseUrl()); properties.put("username", m_configService.getSiteConfigMetasearchUsername()); properties.put("password", m_configService.getSiteConfigMetasearchPassword()); properties.put("sortBy", sortBy); properties.put("pageSize", pageSize); properties.put("startRecord", startRecord); // put selected databases List<String> databaseIds = new java.util.ArrayList<String>(); for (String databaseId : searchDbs) { databaseIds.add(databaseId); } properties.put("databaseIds", databaseIds); // create OSID Properties org.osid.shared.Properties searchProperties = new BasicSearchProperties(properties); // get "sakaibrary / search / asynchMetasearch" search type Type searchType = getSearchType(repository); // call getAssetsBySearch AssetIterator assetIterator = repository.getAssetsBySearch(cqlQuery, searchType, searchProperties); CitationCollection citations = search.getSearchResults(); if (citations == null) { citations = CitationService.getTemporaryCollection(); ((BasicSearch) search).setSearchResults(citations); } Set duplicateCheck = ((BasicSearch) search).getDuplicateCheck(); int duplicateCount = 0; int assetsRetrieved = 0; boolean done = false; boolean moreResults = false; try { // poll until we get pageSize results (or run out of results) moreResults = assetIterator.hasNextAsset(); while (!done && moreResults) { try { Asset asset = assetIterator.nextAsset(); Citation citation = CitationService.getTemporaryCitation(asset); String dupCheckCriteria = citation.hasPreferredUrl() ? citation.getPrimaryUrl() : citation.getOpenurlParameters(); m_log.debug("DUP CHECK: " + dupCheckCriteria); if (((BasicSearch) search).isDuplicateCheckEnabled() && duplicateCheck.contains(dupCheckCriteria)) { m_log.debug("Duplicate #" + (duplicateCount + 1) + " found"); if (duplicateCount++ >= MAX_DUPLICATES) { ((BasicSearch) search).setDuplicateCheckEnabled(false); } // make sure we have more search results moreResults = assetIterator.hasNextAsset(); continue; } else { ((BasicSearch) search).m_pageOrder.add(citation.getId()); citations.add(citation); duplicateCheck.add(dupCheckCriteria); duplicateCount = 0; } // check if we've got enough to return if (++assetsRetrieved >= pageSize.intValue()) { done = true; } } catch (RepositoryException re) { if (re.getMessage().equals(SESSION_TIMED_OUT) || re.getMessage().equals(METASEARCH_ERROR) || re.getMessage().equals(SharedException.NO_MORE_ITERATOR_ELEMENTS) || re.getMessage().equals(OsidException.OPERATION_FAILED)) { search.setLastPage(true); search.resetSearch(); // search is over, all assets that have been retrieved have been // optionally check searchStatus Properties for further details or information to present in UI String message = getSearchStatusMessage(repository); m_log.warn("doSearch -- RepositoryException nextAsset(): " + re.getMessage()); throw new SearchException(message); } else if (re.getMessage().equals(ASSET_NOT_FETCHED)) { // need to wait some time and then try again try { Thread.sleep(2500); // sleep 2.5 seconds } catch (InterruptedException ie) { // search canceled throw new SearchCancelException(); } } } // make sure we have more search results moreResults = assetIterator.hasNextAsset(); } } catch (RepositoryException re) { if (re.getMessage().equals(SESSION_TIMED_OUT) || re.getMessage().equals(METASEARCH_ERROR)) { search.setLastPage(true); // search is over, all assets that have been retrieved have been // optionally check searchStatus Properties for further details or information to present in UI String message = getSearchStatusMessage(repository); m_log.warn("doSearch -- RepositoryException hasNextAsset(): " + re.getMessage()); throw new SearchException(message); } } // get search status properties Type statusType = getPropertyType(repository); org.osid.shared.Properties statusProperties = repository.getPropertiesByType(statusType); Integer numRecordsFound = null; Integer numRecordsFetched = null; Integer numRecordsMerged = null; try { numRecordsFetched = (Integer) statusProperties.getProperty("numRecordsFetched"); numRecordsFound = (Integer) statusProperties.getProperty("numRecordsFound"); numRecordsMerged = (Integer) statusProperties.getProperty("numRecordsMerged"); } catch (SharedException se) { search.setLastPage(true); String message = getSearchStatusMessage(repository); throw new SearchException(message); } /* * forward results handling */ search.setNumRecordsFound(numRecordsFound); search.setNumRecordsFetched(numRecordsFetched); search.setNumRecordsMerged(numRecordsMerged); search.setNewSearch(false); search.setFirstPage(false); /* * disable the "next page" arrow if we've exhausted the search results */ if (!moreResults) { search.setLastPage(true); } else { search.setLastPage(!done); } ((BasicSearch) search).setRepository(repository); ((BasicSearch) search).setAssetIterator(assetIterator); return search; } catch (RepositoryException re) { m_log.warn("doSearch -- RepositoryException: " + re.getMessage()); throw new SearchException(re.getMessage()); } } protected String newSearchId() { /******* A unique ID per-session ********/ return m_sessionManager.getCurrentSession().getId(); /******* Unique ID per-search (original) String sessionId = m_sessionManager.getCurrentSession().getId(); long number = m_generator.nextLong(); String hexString = Long.toHexString(number); m_log.debug("getSearchId: " + sessionId + hexString); return sessionId + hexString; *************************************************************************/ } protected Type getPropertyType(Repository repository) throws SearchException { TypeIterator propertyTypes = null; Type propertyType = null; try { propertyTypes = repository.getPropertyTypes(); while (propertyTypes.hasNextType()) { Type tempType = propertyTypes.nextType(); if (tempType.getAuthority().equals("sakaibrary") && tempType.getDomain().equals("properties") && tempType.getKeyword().equals("metasearchStatus")) { propertyType = tempType; break; } } } catch (OsidException oe) { m_log.warn("getPropertyType -- OsidException: " + oe.getMessage()); throw new SearchException("ERROR in getting search types: " + oe.getMessage()); } return propertyType; } protected Type getCategoryType(Repository repository) throws SearchException { TypeIterator assetTypes = null; try { assetTypes = repository.getAssetTypes(); while (assetTypes.hasNextType()) { Type tempType = assetTypes.nextType(); if (tempType.isEqual(categoryAssetType)) { return tempType; } } } catch (OsidException oe) { m_log.warn("getCategoryType -- OsidException: ", oe); throw new SearchException("ERROR in getting category type: " + oe.getMessage()); } return null; } protected Type getSearchType(Repository repository) throws SearchException { TypeIterator searchTypes = null; try { searchTypes = repository.getSearchTypes(); while (searchTypes.hasNextType()) { Type tempType = searchTypes.nextType(); if (tempType.isEqual(searchType)) { return tempType; } } } catch (OsidException oe) { m_log.warn("getSearchType -- OsidException: ", oe); throw new SearchException("ERROR in getting search types: " + oe.getMessage()); } return null; } protected String getSearchStatusMessage(Repository repository) throws SearchException { BasicType statusType = new BasicType("sakaibrary", "properties", "metasearchStatus"); String message = null; try { org.osid.shared.Properties statusProperties = repository.getPropertiesByType(statusType); message = (String) statusProperties.getProperty("statusMessage"); } catch (RepositoryException e) { // let the message remain null but log this exception m_log.warn("getSearchStatusMessage RepositoryException getting properties " + e.getMessage()); } catch (SharedException e) { // let the message remain null but log this exception m_log.warn("getSearchStatusMessage SharedException getting property " + e.getMessage()); } return message; } public void init() { sessionContextCache = memoryService .getCache("org.sakaiproject.citation.api.SearchManager.sessionContextCache"); SessionContext.setCache(sessionContextCache); metasearchSessionManagerCache = memoryService .getCache("org.sakaiproject.citation.api.SearchManager.metasearchSessionManagerCache"); MetasearchSessionManager.setCache(metasearchSessionManagerCache); m_log.info("BaseSearchManager.init()"); EventTrackingService.addObserver(this); long seed = TimeService.newTime().getTime(); m_generator = new Random(seed); setupTypes(); String configFolderRef = m_configService.getConfigFolderReference(); Collection<String> hierarchyIds = m_configService.getAllCategoryXml(); for (String hierarchyId : hierarchyIds) { this.updateHierarchy(configFolderRef + hierarchyId); this.updatableResources.add(configFolderRef + hierarchyId); } } protected void setupTypes() { categoryAssetType = new BasicType("sakaibrary", "asset", "category"); databaseAssetType = new BasicType("sakaibrary", "asset", "database"); searchType = new BasicType("sakaibrary", "search", "asynchMetasearch"); repositoryType = new BasicType("sakaibrary", "repository", "metasearch"); } /* * Fetch the appropriate database hierarchy resource from our cache (a new * hiearchy resource is created if none exists). * * @see org.sakaiproject.citation.api.SearchManager#listRepositories() */ public SearchDatabaseHierarchy getSearchHierarchy() throws SearchException { SearchDatabaseHierarchy hierarchy; try { String configFolderRef = m_configService.getConfigFolderReference(); String hierarchyXml = m_configService.getDatabaseHierarchyXml(); if (!isNull(configFolderRef) && !isNull(hierarchyXml)) { String hierarchyRef = configFolderRef + hierarchyXml; String xmlContent; /* * Look up the requested hierarchy */ m_log.debug("Looking for hierarchy: " + hierarchyRef); synchronized (this) { if ((xmlContent = this.hierarchyMap.get(hierarchyRef)) == null) { return null; } } /* * Set up the per-user database hierarchy */ hierarchy = new BasicSearchDatabaseHierarchy(xmlContent); return hierarchy.isConfigured() ? hierarchy : null; } } catch (OsidConfigurationException exception) { m_log.warn("Failed to get configuration details: " + exception); } return null; } public synchronized void updateHierarchy(String databaseXmlReference) { try { String xmlContent = getResourceContent(databaseXmlReference); if (xmlContent != null) { this.hierarchyMap.put(databaseXmlReference, xmlContent); } } catch (Exception exception) { m_log.warn("Failed to load " + databaseXmlReference + " (no changes made): " + exception); } } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchManager#newSearch(java.lang.String) */ public ActiveSearch newSearch() { return new BasicSearch(); } /* (non-Javadoc) * @see org.sakaiproject.citation.api.SearchManager#newSearch(java.lang.String) */ public ActiveSearch newSearch(CitationCollection savedResults) { return new BasicSearch(savedResults); } protected boolean paramIsEmpty(String param) { return param.trim().equals(""); } /** * */ public String getGoogleScholarUrl(String resourceId) { String serverUrl = serverConfigurationService.getServerUrl(); //SessionManager sessionManager = (SessionManager) ComponentManager.get("org.sakaiproject.tool.api.SessionManager"); //String sessionId = sessionManager.getCurrentSession().getId(); try { return (m_configService.getSiteConfigGoogleBaseUrl() + "?sciui=2&as_sdt=0,15&" + "linkurl_base=" + java.net.URLEncoder.encode(serverUrl + Entity.SEPARATOR + SERVLET_NAME + Entity.SEPARATOR + resourceId + "?" + SAKAI_SESSION + "=nada&", "UTF-8") + "&linkurl_id=" + java.net.URLEncoder.encode(m_configService.getSiteConfigSakaiServerKey(), "UTF-8")); } catch (Exception e) { m_log.warn("getGoogleScholarUrl encoding failed", e); return null; } } /** * Supply the url for the savecite servlet to add a citation to a particular citation list. * @param resourceId The identifier for the citation list. */ public String getSaveciteUrl(String resourceId, String saveciteClientId) { StringBuilder buf = new StringBuilder(); String serverUrl = serverConfigurationService.getServerUrl(); buf.append(serverUrl); buf.append(Entity.SEPARATOR); buf.append(SERVLET_NAME); buf.append(Entity.SEPARATOR); buf.append(resourceId); buf.append('?'); buf.append(SAKAI_SESSION); buf.append("=nada&client="); buf.append(saveciteClientId); //buf.append("&"); return buf.toString(); } public String getExternalSearchWindowName(String resourceId) { String serverUrl = serverConfigurationService.getServerUrl() + Entity.SEPARATOR + SERVLET_NAME + Entity.SEPARATOR + resourceId; try { String encodedUrl = URLEncoder.encode(serverUrl, "UTF-8"); return WINDOW_PREFIX + encodedUrl; } catch (UnsupportedEncodingException e) { throw new RuntimeException("Does anywhere not have UTF-8?", e); } } /** * @return the serverConfigurationService */ public ServerConfigurationService getServerConfigurationService() { return serverConfigurationService; } /** * @param serverConfigurationService the serverConfigurationService to set */ public void setServerConfigurationService(ServerConfigurationService serverConfigurationService) { this.serverConfigurationService = serverConfigurationService; } public void update(Observable arg0, Object arg1) { if (arg1 instanceof Event) { Event event = (Event) arg1; /* * Modified? If so, reload if it's one of our hierarchy files */ if (event.getModify()) { String refstr = event.getResource(); synchronized (this) { if (this.updatableResources.contains(refstr)) { m_log.debug("Updating configuration from " + refstr); this.updateHierarchy(refstr); } } } } } /** * Establish a security advisor to allow the "embedded" azg work to occur * with no need for additional security permissions. */ protected SecurityAdvisor enableSecurityAdvisor() { SecurityAdvisor advisor = new SecurityAdvisor() { public SecurityAdvice isAllowed(String userId, String function, String reference) { return SecurityAdvice.ALLOWED; } }; // put in a security advisor so we can create citationAdmin site without need // of further permissions SecurityService.pushAdvisor(advisor); return advisor; } /** * Fetch the content from a Resource * @param resource Content hosting resource */ public String getResourceContent(ContentResource resource) throws IOException, ServerOverloadException { InputStream input = resource.streamContent(); StringBuilder content = new StringBuilder(); byte[] bytesIn = new byte[1024 * 8]; int count; while ((count = input.read(bytesIn)) != -1) { content.append(new String(bytesIn, 0, count, "UTF-8")); } return content.toString(); } /** * Fetch content from a resource reference (a named resource) * @param resourceReference Resource reference * @return Resource content (null if none) */ public String getResourceContent(String resourceReference) throws IOException, IdUnusedException, PermissionException, ServerOverloadException, TypeException { String content = null; Reference reference = EntityManager.newReference(resourceReference); if (reference == null) { return null; } SecurityAdvisor pushed = enableSecurityAdvisor(); try { ContentResource resource = ContentHostingService.getResource(reference.getId()); if (resource != null) { content = getResourceContent(resource); } } catch (Exception e) { m_log.warn("getReourceContent() " + e); } finally { if (pushed != null) { boolean found = false; while (SecurityService.hasAdvisors() && !found) { SecurityAdvisor popped = SecurityService.popAdvisor(); found = popped == pushed; } } } return content; } /** * Null (or empty) String? * @param string String to check * @return true if so */ private boolean isNull(String string) { return (string == null) || (string.trim().equals("")); } /** * @return the memoryService */ public MemoryService getMemoryService() { return memoryService; } /** * @param memoryService the memoryService to set */ public void setMemoryService(MemoryService memoryService) { this.memoryService = memoryService; } }