/** * */ package org.ihtsdo.otf.refset.graph.gao; import static org.ihtsdo.otf.refset.domain.RGC.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.annotation.Resource; import org.ihtsdo.otf.refset.domain.Member; import org.ihtsdo.otf.refset.exception.EntityNotFoundException; import org.ihtsdo.otf.refset.graph.RefsetGraphAccessException; import org.ihtsdo.otf.refset.graph.RefsetGraphFactory; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.util.StringUtils; import com.thinkaurelius.titan.core.TitanGraph; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.util.wrappers.event.EventGraph; import com.tinkerpop.frames.FramedGraphFactory; /**Graph Access component to do CRUD operation on underlying Refset graph * for {@link Member} node only * */ @Repository public class MemberGAO { private RefsetGAO rGao; /** * @param rGao the rGao to set */ @Autowired public void setRefsetGao(RefsetGAO rGao) { this.rGao = rGao; } private static final Logger LOGGER = LoggerFactory.getLogger(MemberGAO.class); private RefsetGraphFactory factory; private static FramedGraphFactory fgf = new FramedGraphFactory(); /**Retrieves a {@link Member} node id for given {@link Member#getReferenceComponentId()} * @param rcId * @param tg * @return * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ protected Vertex getMemberVertex(String id, TitanGraph tg) throws RefsetGraphAccessException, EntityNotFoundException { Vertex result = null; if (StringUtils.isEmpty(id)) { throw new EntityNotFoundException("Member does not exist for given reference component id"); } try { //TODO upgrade this search with status and effective date @SuppressWarnings("unchecked") Iterable<Vertex> vr = tg.query().has(ID, id).has(TYPE, VertexType.member.toString()).has(END, Long.MAX_VALUE).limit(1).vertices(); for (Vertex v : vr) { result = v; break; } } catch (Exception e) { LOGGER.error("Error during member lookup for reference component id {}", id, e); throw new RefsetGraphAccessException(e.getMessage(), e); } if(result == null) throw new EntityNotFoundException("Record does not exist"); return result; } /** Add a {@link Member} if does not exist in graph for a given {@link Member#getReferenceComponentId()} * and returns {@link Member} node id {@link Vertex#getId()} * @param m * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ protected Vertex addMemberNode(Member m, EventGraph<TitanGraph> eg, String refsetId) throws RefsetGraphAccessException, EntityNotFoundException { if (m == null || StringUtils.isEmpty(m.getReferencedComponentId())) { throw new EntityNotFoundException("Invalid member details. refset component id is mandatory in member details"); } LOGGER.debug("Adding member {}", m); eg.addListener(new EffectiveTimeChangeListener(eg.getBaseGraph(), m.getModifiedBy())); Vertex mVT = eg.getBaseGraph().addVertexWithLabel(eg.getBaseGraph().getVertexLabel("GMember")); Vertex mV = eg.getVertex(mVT.getId()); //Vertex mg = eg.getVertex(vM.getId());//, GMember.class); Integer activeFlag = m.isActive() ? 1 : 0; //mg.setActive(activeFlag); mV.setProperty(ACTIVE, activeFlag); mV.setProperty(ID, UUID.randomUUID().toString()); DateTime et = m.getEffectiveTime(); if ( et != null) { mV.setProperty(EFFECTIVE_DATE, et.getMillis()); } //mg.setModifiedBy(m.getModifiedBy()); mV.setProperty(MODIFIED_BY, m.getModifiedBy()); //mg.setCreateBy(m.getCreatedBy()); mV.setProperty(CREATED_BY, m.getCreatedBy()); //mg.setCreated(new DateTime().getMillis()); mV.setProperty(CREATED, new DateTime().getMillis()); //mg.setModifiedDate(new DateTime().getMillis()); mV.setProperty(MODIFIED_DATE, new DateTime().getMillis()); //mg.setModuleId(m.getModuleId()); mV.setProperty(MODULE_ID, m.getModuleId()); Integer publishedFlag = m.isPublished() ? 1 : 0; //mg.setPublished(publishedFlag); mV.setProperty(PUBLISHED, publishedFlag); //mg.setType(VertexType.member.toString()); mV.setProperty(TYPE, VertexType.member.toString()); //description,refsetId(ie parentId) and reference component id is needed to provide search mV.setProperty(REFERENCE_COMPONENT_ID, m.getReferencedComponentId()); mV.setProperty(DESC, m.getDescription()); mV.setProperty(PARENT_ID, refsetId); LOGGER.debug("Added Member as vertex to graph", mV.getId()); return mV; } /** * @param factory the factory to set */ @Resource(name = "refsetGraphFactory") public void setFactory(RefsetGraphFactory factory) { this.factory = factory; } /** * @param refsetId * @param members * @param user * @throws EntityNotFoundException * @throws RefsetGraphAccessException */ public Map<String, String> addMembers(String refsetId, Set<Member> members, String user) throws RefsetGraphAccessException, EntityNotFoundException { Map<String, String> outcomeMap = new HashMap<String, String>(); //needed at front end EventGraph<TitanGraph> tg = null; try { tg = factory.getEventGraph(); Vertex rV = rGao.getRefsetVertex(refsetId, fgf.create(tg.getBaseGraph())); //try adding all members and accumulate error/success List<String> rcIds = new ArrayList<String>(); for (Member member : members) { rcIds.add(member.getReferencedComponentId()); } Map<String, String> descriptions = rGao.getMembersDescription(rcIds); for (Member m : members) { Vertex mV; try { mV = getMemberVertex(m.getUuid(), tg.getBaseGraph()); LOGGER.debug("Member & relation already exist from {} to {}", mV.getId(), rV.getId()); outcomeMap.put(m.getReferencedComponentId(), "Already exist, Not adding"); } catch(EntityNotFoundException ex) { if(descriptions.containsKey(m.getReferencedComponentId())) { m.setDescription(descriptions.get(m.getReferencedComponentId())); } //add member mV = addMemberNode(m, tg, refsetId); Edge e = tg.addEdge(null, mV, rV, "members"); e.setProperty(REFERENCE_COMPONENT_ID, m.getReferencedComponentId()); long start = m.getEffectiveTime() != null ? m.getEffectiveTime().getMillis() : m.getCreated().getMillis(); e.setProperty(START, start); e.setProperty(END, Long.MAX_VALUE); LOGGER.debug("Added relationship as edge from {} to {}", mV.getId(), rV.getId()); outcomeMap.put(m.getReferencedComponentId(), "Success"); } } RefsetGraphFactory.commit(tg);; } catch (EntityNotFoundException e) { LOGGER.error("Error during bulk member upload", e); RefsetGraphFactory.rollback(tg); throw e; }catch (Exception e) { LOGGER.error("Error during bulk member upload", e); RefsetGraphFactory.rollback(tg); throw new RefsetGraphAccessException("Error while adding members"); } finally { RefsetGraphFactory.shutdown(tg); } return outcomeMap; } /** * @param refsetId * @param rcId * @param user * @throws EntityNotFoundException * @throws RefsetGraphAccessException */ public void removeMember(String refsetId, String rcId, String user) throws EntityNotFoundException, RefsetGraphAccessException { LOGGER.debug("removing member {} from refset {}", rcId, refsetId); EventGraph<TitanGraph> tg = null; if (StringUtils.isEmpty(refsetId) || StringUtils.isEmpty(rcId)) { throw new EntityNotFoundException("Invalid request, refset id and reference component id is required in member remove request"); } try { tg = factory.getEventGraph(); tg.addListener(new MemberChangeListener(tg.getBaseGraph(), user)); tg.addListener(new EffectiveTimeChangeListener(tg.getBaseGraph(), user)); Vertex rV = rGao.getRefsetVertex(refsetId, fgf.create(tg.getBaseGraph())); Iterable<Edge> eR = tg.getEdges(REFERENCE_COMPONENT_ID, rcId); for (Edge e : eR) { Vertex vIn = e.getVertex(Direction.IN); if (vIn != null && vIn.equals(rV)) { LOGGER.debug("Removing member relationship from refset {}", rV); if(!"1".equals(e.getVertex(Direction.OUT).getProperty(PUBLISHED))) { tg.removeEdge(e); } else { throw new EntityNotFoundException("Published member can not be deleted"); } break; } } RefsetGraphFactory.commit(tg); } catch (EntityNotFoundException e) { LOGGER.error("Error while removing member", e); RefsetGraphFactory.rollback(tg); throw e; } catch (Exception e) { LOGGER.error("Error while removing member", e); RefsetGraphFactory.rollback(tg); throw new RefsetGraphAccessException("Error while removing members"); } finally { RefsetGraphFactory.shutdown(tg); } } /**This is a transaction so we can not individually send error/success for a conceptId!! * @param refsetId * @param user * @param rcId * @throws EntityNotFoundException * @throws RefsetGraphAccessException */ public Map<String, String> removeMembers(String refsetId, Set<String> rcIds, String user) throws EntityNotFoundException, RefsetGraphAccessException { LOGGER.debug("removing members {} from refset {}", rcIds, refsetId); EventGraph<TitanGraph> tg = null; Map<String, String> outcome = new HashMap<String, String>(); try { tg = factory.getEventGraph(); tg.addListener(new MemberChangeListener(tg.getBaseGraph(), user)); tg.addListener(new EffectiveTimeChangeListener(tg.getBaseGraph(), user)); Vertex rV = rGao.getRefsetVertex(refsetId, fgf.create(tg.getBaseGraph())); Iterable<Edge> eR = rV.getEdges(Direction.IN, "members"); for (Edge e : eR) { String referencedComponentId = e.getProperty(REFERENCE_COMPONENT_ID); if (!StringUtils.isEmpty(referencedComponentId) && rcIds.contains(referencedComponentId)) { LOGGER.debug("Removing member {} relationship from refset {}", e, rV); e.remove(); outcome.put(referencedComponentId, "Success"); } } RefsetGraphFactory.commit(tg); } catch (EntityNotFoundException e) { LOGGER.error("Error while removing member", e); RefsetGraphFactory.rollback(tg); throw e; } catch (Exception e) { LOGGER.error("Error while removing member", e); RefsetGraphFactory.rollback(tg); throw new RefsetGraphAccessException("Error while removing members"); } finally { RefsetGraphFactory.shutdown(tg); } return outcome; } /** Update a {@link Member} and returns {@link Member} node {@link Vertex}. * If does not exist in graph for a given {@link Member#getId()} throw {@link EntityNotFoundException} * @param member * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ protected Vertex updateMemberNode(Member m, EventGraph<TitanGraph> tg) throws RefsetGraphAccessException, EntityNotFoundException { if (m == null || StringUtils.isEmpty(m.getUuid()) || StringUtils.isEmpty(m.getReferencedComponentId())) { throw new EntityNotFoundException("Invalid member details. Reference component id and member id is mandatory in member details"); } tg.addListener(new MemberChangeListener(tg.getBaseGraph(), m.getModifiedBy())); tg.addListener(new EffectiveTimeChangeListener(tg.getBaseGraph(), m.getModifiedBy())); LOGGER.debug("Updating member {}", m); Iterable<Vertex> vr = tg.query().has(TYPE, VertexType.member.toString()).has(ID, m.getUuid()).limit(1).vertices(); if (vr != null ) { for (Vertex v : vr) { //mg = tg.getVertex(v.getId());//, GMember.class); LOGGER.debug("Updating member {} and vertex {} ", m, v); /*if(Integer.valueOf(1).equals(v.getProperty(PUBLISHED))) { throw new EntityNotFoundException("Member can not be updated once published"); }*/ Integer activeFlag = m.isActive() ? 1 : 0; v.setProperty(ACTIVE, activeFlag); /*DateTime et = m.getEffectiveTime(); if ( et != null) { //mg.setEffectiveTime(et.getMillis()); v.setProperty(EFFECTIVE_DATE, et.getMillis()); }*/ v.setProperty(MODIFIED_BY, m.getModifiedBy()); v.setProperty(MODIFIED_DATE, new DateTime().getMillis()); //mg.setModifiedBy(m.getModifiedBy()); //mg.setModifiedDate(new DateTime().getMillis()); if (!StringUtils.isEmpty(m.getModuleId())) { v.setProperty(MODULE_ID, m.getModuleId()); //mg.setModuleId(m.getModuleId()); } //Integer publishedFlag = m.isPublished() ? 1 : 0; //in current implementation during member update we can only update member as not published with no effective date //TODO this will change once we have release service talking to refset. v.removeProperty(EFFECTIVE_DATE); //mg.setPublished(publishedFlag); v.setProperty(PUBLISHED, 0); //mV = mg;//mg.asVertex(); LOGGER.debug("Updated Member as vertex to graph", v.getId()); return v; } } String msg = "Member details not available for id " + m.getUuid(); throw new EntityNotFoundException(msg); } }