/** * Copyright (C) 2010-2017 Structr GmbH * * This file is part of Structr <http://structr.org>. * * Structr is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Structr 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Structr. If not, see <http://www.gnu.org/licenses/>. */ package org.structr.net.repository; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; import org.structr.net.data.time.PseudoTime; import org.structr.net.peer.Peer; /** * */ public class DefaultRepository implements Repository { private final List<InternalChangeListener> internalChangeListeners = new LinkedList<>(); private final List<ExternalChangeListener> externalChangeListeners = new LinkedList<>(); private final Map<String, DefaultPossibility> possibilities = new LinkedHashMap<>(); private final Map<String, RepositoryObject> objects = new LinkedHashMap<>(); private Peer peer = null; private String uuid = null; public DefaultRepository(final String uuid) { this.uuid = uuid; } public void setPeer(final Peer peer) { this.peer = peer; } @Override public String getUuid() { return uuid; } @Override public void addInternalChangeListener(final InternalChangeListener listener) { internalChangeListeners.add(listener); } @Override public void removeInternalChangeListener(final InternalChangeListener listener) { internalChangeListeners.remove(listener); } @Override public void addExternalChangeListener(final ExternalChangeListener listener) { externalChangeListeners.add(listener); listener.onAdd(); } @Override public void removeExternalChangeListener(final ExternalChangeListener listener) { externalChangeListeners.remove(listener); listener.onRemove(); } public RepositoryObject add(final String id, final String type, final String deviceId, final String userId, final PseudoTime creationTime) { synchronized (objects) { final RepositoryObject obj = new DefaultRepositoryObject(this, id, type, deviceId, userId, creationTime); objects.put(id, obj); return obj; } } @Override public RepositoryObject create(final String id, final String type, final String deviceId, final String userId, final PseudoTime created, final Map<String, Object> data) { RepositoryObject obj = getObject(id); if (obj == null) { obj = new DefaultRepositoryObject(this, id, type, deviceId, userId, created); synchronized (objects) { final String transactionId = UUID.randomUUID().toString().replaceAll("\\-", ""); for (final Entry<String, Object> entry : data.entrySet()) { obj.setProperty(created, transactionId, entry.getKey(), entry.getValue()); } complete(transactionId); objects.put(id, obj); } for (final InternalChangeListener listener : internalChangeListeners) { listener.onObjectCreation(obj, data); } notifyCreation(obj, data); } return obj; } @Override public void update(final RepositoryObject obj, final String type, final String deviceId, final String userId, final PseudoTime lastModified, final Map<String, Object> data) { final String transactionId = UUID.randomUUID().toString().replaceAll("\\-", ""); for (final Entry<String, Object> entry : data.entrySet()) { obj.setProperty(lastModified, transactionId, entry.getKey(), entry.getValue()); } complete(transactionId); notifyModification(obj, data); } @Override public void delete(final String id, final PseudoTime timestamp) { synchronized (objects) { final RepositoryObject obj = objects.get(id); if (obj != null) { objects.remove(id); notifyDeletion(obj); } for (final InternalChangeListener listener : internalChangeListeners) { listener.onObjectDeletion(id); } } } @Override public RepositoryObject objectCreated(final String id, final String type, final String deviceId, final String userId, final PseudoTime created, final PseudoTime lastModified, final Map<String, Object> data) { synchronized (objects) { RepositoryObject obj = getObject(id); if (obj == null) { obj = new DefaultRepositoryObject(this, id, type, deviceId, userId, created); final String transactionId = UUID.randomUUID().toString().replaceAll("\\-", ""); if (data != null) { for (final Entry<String, Object> entry : data.entrySet()) { obj.setProperty(lastModified, transactionId, entry.getKey(), entry.getValue()); } } objects.put(id, obj); complete(transactionId); notifyCreation(obj, data); } return obj; } } @Override public void objectDeleted(final String id, final PseudoTime timestamp) { synchronized (objects) { final RepositoryObject obj = objects.get(id); if (obj != null) { objects.remove(id); notifyDeletion(obj); } } } @Override public boolean contains(final String id) { notifyRepositoryQuery(); synchronized (objects) { return objects.containsKey(id); } } @Override public RepositoryObject getObject(final String id) { notifyRepositoryQuery(); synchronized (objects) { return objects.get(id); } } @Override public Collection<RepositoryObject> getObjects() { notifyRepositoryQuery(); final List<RepositoryObject> list = new LinkedList<>(); synchronized (objects) { list.addAll(objects.values()); } return list; } @Override public int objectCount() { return objects.size(); } @Override public String beginTransaction(final long timeout) { final DefaultPossibility p = new DefaultPossibility(peer, timeout); synchronized (possibilities) { possibilities.put(p.getUuid(), p); } return p.getUuid(); } @Override public void complete(final String transactionId) { final DefaultPossibility p = getPossibility(transactionId); if (p != null) { if (!p.isAborted()) { p.complete(); } } } public DefaultPossibility getPossibility(final String transactionId) { synchronized (possibilities) { DefaultPossibility p = possibilities.get(transactionId); if (p == null) { p = new DefaultPossibility(peer, transactionId); possibilities.put(transactionId, p); } return p; } } public void clear() { objects.clear(); } // ----- private methods ----- private void notifyCreation(final RepositoryObject object, final Map<String, Object> data) { for (final ExternalChangeListener listener : externalChangeListeners) { listener.onCreate(object, data); } } private void notifyDeletion(final RepositoryObject object) { for (final ExternalChangeListener listener : externalChangeListeners) { listener.onDelete(object); } } private void notifyModification(final RepositoryObject object, final Map<String, Object> data) { for (final ExternalChangeListener listener : externalChangeListeners) { listener.onModify(object, data); } } private void notifyRepositoryQuery() { for (final ExternalChangeListener listener : externalChangeListeners) { listener.onQuery(); } } }