package org.gcube.data.access.storagehub.handlers.vres; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.stream.Collectors; import javax.inject.Inject; import javax.jcr.Credentials; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.observation.Event; import javax.jcr.observation.EventJournal; import org.gcube.common.storagehub.model.Excludes; import org.gcube.common.storagehub.model.NodeConstants; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; import org.gcube.common.storagehub.model.items.AbstractFileItem; import org.gcube.common.storagehub.model.items.FolderItem; import org.gcube.common.storagehub.model.items.Item; import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class VREQueryRetriever implements Callable> { private static final Logger logger = LoggerFactory.getLogger(VREQueryRetriever.class); private static final int CACHE_DIMENSION = 50; private Repository repository; private Credentials credentials; private Item vreFolder; Map cachedList = new HashMap<>(CACHE_DIMENSION); long higherTimestamp = Long.MAX_VALUE; long lastTimestamp =0; Node2ItemConverter node2Item; public VREQueryRetriever(Repository repository, Credentials credentials, Node2ItemConverter node2Item, Item vreFolder) { super(); this.repository = repository; this.credentials = credentials; this.vreFolder = vreFolder; this.node2Item = node2Item; } public List call() { logger.debug("executing recents task for {}",vreFolder.getTitle()); Session ses = null; try { ses = repository.login(credentials); //if (cachedList.isEmpty()) { init(ses); /*} else { logger.debug("redoing recents for {}",vreFolder.getTitle()); try { long timestampToUse = lastTimestamp; lastTimestamp = System.currentTimeMillis(); long start = System.currentTimeMillis(); ses = repository.login(credentials); final String[] types = { "nthl:workspaceLeafItem", "nthl:workspaceItem"}; EventJournal journalChanged = ses.getWorkspace().getObservationManager().getEventJournal(Event.PROPERTY_CHANGED^Event.NODE_REMOVED^Event.NODE_MOVED^Event.NODE_ADDED, vreFolder.getPath(), true, null, types); journalChanged.skipTo(timestampToUse); logger.debug("getting the journal took {}",System.currentTimeMillis()-start); int events = 0; while (journalChanged.hasNext()) { events++; Event event = journalChanged.nextEvent(); switch(event.getType()) { case Event.NODE_ADDED: if (ses.nodeExists(event.getPath())) { Node nodeAdded = ses.getNode(event.getPath()); if (nodeAdded.isNodeType("nthl:workspaceLeafItem")) { logger.trace("node added event received with name {}", nodeAdded.getName()); Item item = node2Item.getItem(nodeAdded, Arrays.asList(NodeConstants.ACCOUNTING_NAME)); if (cachedList.get(event.getIdentifier())!=null) cachedList.remove(event.getIdentifier()); insertItemInTheRightPlace(item); } } break; case Event.PROPERTY_CHANGED: if (ses.propertyExists(event.getPath())) { Property property = ses.getProperty(event.getPath()); if (property.getName().equalsIgnoreCase("jcr:lastModified")) { logger.trace("event property changed on {} with value {} and parent {}",property.getName(), property.getValue().getString(), property.getParent().getPath()); String identifier = property.getParent().getIdentifier(); cachedList.remove(identifier); Item item = node2Item.getItem(property.getParent(), Excludes.EXCLUDE_ACCOUNTING); insertItemInTheRightPlace(item); } } break; case Event.NODE_REMOVED: logger.trace("node removed event received with type {}", event.getIdentifier()); if (cachedList.get(event.getIdentifier())!=null && cachedList.get(event.getIdentifier()) correctValues(Session ses){ logger.debug("preparing returning values for {}",vreFolder.getTitle()); long start = System.currentTimeMillis(); List> list = new LinkedList<>(cachedList.entrySet()); list.sort((c1, c2) -> c1.getValue().compareTo(c2.getValue())*-1); if (list.size()>CACHE_DIMENSION) for (int index = CACHE_DIMENSION-1; index< list.size() ; index++) cachedList.remove(list.get(index).getKey()); List cachedIds = list.stream().map(m -> m.getKey()).collect(Collectors.toList()); if (cachedIds.size()>10) cachedIds = cachedIds.subList(0, 10); List result = new ArrayList<>(10); for (String id: cachedIds) { try { Item item = node2Item.getItem(id, ses, Excludes.EXCLUDE_ACCOUNTING); if (item!=null) result.add(item); else logger.warn("item with id {} is null",id); } catch (BackendGenericError | RepositoryException e) { } } logger.debug("returning values prepared in {} for {}",System.currentTimeMillis()-start,vreFolder.getTitle()); return result; } private void insertItemInTheRightPlace(Item item) { long lastModifiedTime = item.getLastModificationTime().getTime().getTime(); if (!(lastModifiedTime>higherTimestamp && cachedList.size()>CACHE_DIMENSION)) { cachedList.put(item.getId(), lastModifiedTime); if (lastModifiedTime>higherTimestamp) higherTimestamp = lastModifiedTime; } } private void init(Session ses){ try { long start = System.currentTimeMillis(); Calendar now = Calendar.getInstance(); now.add(Calendar.YEAR, -1); lastTimestamp = System.currentTimeMillis(); Node vreFolderNode = ses.getNodeByIdentifier(vreFolder.getId()); logger.debug("starting visiting children for {}",vreFolder.getTitle()); visitChildren(vreFolderNode); logger.debug("initializing recents for {} took {}",vreFolder.getTitle(),System.currentTimeMillis()-start); } catch (Exception e) { logger.error("error querying vre {}",vreFolder.getTitle(),e); throw new RuntimeException(e); } } private void visitChildren(Node node) throws Exception{ NodeIterator nodeIt = node.getNodes(); while(nodeIt.hasNext()) { Node child = nodeIt.nextNode(); Item item = node2Item.getItem(child, Excludes.ALL); if (item==null || item.isHidden()) continue; if (item instanceof FolderItem) visitChildren(child); else if(item instanceof AbstractFileItem) insertItemInTheRightPlace(item); } } /* @Override public void onEvent(EventIterator events) { logger.trace("on event called"); while (events.hasNext()) { Event event = events.nextEvent(); try { logger.trace("new event received of type {} on node {}",event.getType(),event.getIdentifier()); } catch (RepositoryException e) { logger.error("error reading event",e); } } }*/ }