/** * Copyright (C) 2010 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xcmis.sp.inmemory.query; import org.xcmis.search.SearchService; import org.xcmis.search.content.ContentEntry; import org.xcmis.search.content.IndexModificationException; import org.xcmis.search.content.Property; import org.xcmis.search.content.Property.BinaryValue; import org.xcmis.search.content.Property.ContentValue; import org.xcmis.search.content.Property.SimpleValue; import org.xcmis.search.value.PropertyType; import org.xcmis.spi.CmisConstants; import org.xcmis.spi.ContentStream; import org.xcmis.spi.DocumentData; import org.xcmis.spi.FolderData; import org.xcmis.spi.ObjectData; import org.xcmis.spi.PolicyData; import org.xcmis.spi.RelationshipData; import org.xcmis.spi.utils.Logger; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; /** * @author <a href="mailto:andrew00x@gmail.com">Andrey Parfonov</a> * @version $Id: IndexListener.java 1261 2010-06-09 09:52:06Z andrew00x $ */ public class IndexListener { private static final Logger LOG = Logger.getLogger(IndexListener.class); /** * Index storage. */ private final SearchService searchService; private final ContentEntryAdapter contentEntryAdapter; public IndexListener(SearchService searchService) { if (searchService == null) throw new IllegalArgumentException("The searchService argument may not be null"); this.searchService = searchService; this.contentEntryAdapter = new ContentEntryAdapter(); } public void created(ObjectData object) { try { searchService.update(contentEntryAdapter.createEntry(object), null); } catch (IndexModificationException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getLocalizedMessage()); } } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getLocalizedMessage()); } } } public void removed(Set<String> removed) { try { searchService.update(Collections.EMPTY_LIST, removed); } catch (IndexModificationException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getLocalizedMessage()); } } } public void updated(ObjectData object) { try { searchService.update(contentEntryAdapter.createEntry(object), object.getObjectId()); } catch (IndexModificationException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getLocalizedMessage()); } } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getLocalizedMessage()); } } } /** * Adapt changes produced by CMIS SPI to {@link ContentEntry} acceptable for. * {@link SearchService} */ public static class ContentEntryAdapter { /** * Convert {@link ObjectData} to {@link ContentEntry}. * * @param objectData ObjectData * @return contentEntry ContentEntry * @throws IOException */ public ContentEntry createEntry(ObjectData objectData) throws IOException { if (objectData != null) { switch (objectData.getBaseType()) { case DOCUMENT : return createFromDocument((DocumentData)objectData); case FOLDER : return createFromFolder((FolderData)objectData); case POLICY : return createFromPolicy((PolicyData)objectData); case RELATIONSHIP : return createFromRelationship((RelationshipData)objectData); default : throw new UnsupportedOperationException(objectData.getBaseType().toString() + " is not supported for indexing"); } } return null; } /** * @param objectData RelationshipData * @return ContentEntry ContentEntry; */ private ContentEntry createFromRelationship(RelationshipData objectData) { MockContentEntry mockEntry = fillCommonInformation(objectData); //mark parent of root as parent mockEntry.parentIdentifiers.add(""); return new ContentEntry(mockEntry.name, mockEntry.getTableNames(), mockEntry.identifier, mockEntry .getParentIdentifiers(), mockEntry.getProperties()); } /** * @param objectData PolicyData * @return contentEntry ContentEntry */ private ContentEntry createFromPolicy(PolicyData objectData) { MockContentEntry mockEntry = fillCommonInformation(objectData); //mark parent of root as parent mockEntry.parentIdentifiers.add(""); return new ContentEntry(mockEntry.name, mockEntry.getTableNames(), mockEntry.identifier, mockEntry .getParentIdentifiers(), mockEntry.getProperties()); } private MockContentEntry fillCommonInformation(ObjectData objectData) { MockContentEntry contentEntry = new MockContentEntry(); contentEntry.tableNames.add(objectData.getTypeDefinition().getQueryName()); contentEntry.identifier = objectData.getObjectId(); contentEntry.name = objectData.getName(); for (FolderData folder : objectData.getParents()) { contentEntry.parentIdentifiers.add(folder.getObjectId()); } for (org.xcmis.spi.model.Property<?> property : objectData.getProperties().values()) { if (property.getValues().size() > 0) { contentEntry.properties.add(convertProperty(property)); } } return contentEntry; } private <G> Property<G> convertProperty(org.xcmis.spi.model.Property<G> property) { Collection<ContentValue<G>> value = new ArrayList<ContentValue<G>>(); for (G contentValue : property.getValues()) { value.add(new SimpleValue<G>(contentValue)); } return new Property<G>(CmisSchema.PROPERTY_TYPES_MAP.get(property.getType()), property.getQueryName(), value); } /** * Convert {@link FolderData} to {@link ContentEntry}. * * @param objectData FolderData * @return contentEntry ContentEntry */ private ContentEntry createFromFolder(FolderData objectData) { MockContentEntry mockEntry = fillCommonInformation(objectData); return new ContentEntry(mockEntry.name, mockEntry.getTableNames(), mockEntry.identifier, mockEntry .getParentIdentifiers(), mockEntry.getProperties()); } /** * Convert {@link DocumentData} to {@link ContentEntry}. * * @param objectData DocumentData * @return contentEntry ContentEntry. * @throws IOException */ private ContentEntry createFromDocument(DocumentData objectData) throws IOException { MockContentEntry mockEntry = fillCommonInformation(objectData); ContentStream cs = objectData.getContentStream(); if (cs != null) { List<ContentValue<InputStream>> vals = new ArrayList<ContentValue<InputStream>>(1); vals.add(new BinaryValue(cs.getStream(), cs.getMediaType().getBaseType(), cs.getMediaType().getParameter( CmisConstants.CHARSET), cs.length())); //TODO add constant for property name content mockEntry.properties.add(new Property<InputStream>(PropertyType.BINARY, "content", vals)); } return new ContentEntry(mockEntry.name, mockEntry.getTableNames(), mockEntry.identifier, mockEntry .getParentIdentifiers(), mockEntry.getProperties()); } } private static class MockContentEntry { /** * List of table names which identifies this content. */ List<String> tableNames; /** * Name of the entry. */ String name; /** * List of parent entry identifiers. */ List<String> parentIdentifiers; /** * Entry identifier. */ String identifier; /** * List of entry properties. */ List<Property> properties; /** * */ MockContentEntry() { tableNames = new ArrayList<String>(); parentIdentifiers = new ArrayList<String>(); properties = new ArrayList<Property>(); } /** * @return the tableNames */ public String[] getTableNames() { return tableNames.toArray(new String[tableNames.size()]); } /** * @return the parentIdentifiers */ public String[] getParentIdentifiers() { return parentIdentifiers.toArray(new String[parentIdentifiers.size()]); } /** * @return the properties */ public Property[] getProperties() { return properties.toArray(new Property[properties.size()]); } } }