/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache 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.apache.org/licenses/LICENSE-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.apache.cayenne;
import org.apache.cayenne.graph.GraphChangeHandler;
import org.apache.cayenne.graph.GraphDiff;
import org.apache.cayenne.graph.GraphManager;
import org.apache.cayenne.map.LifecycleEvent;
import org.apache.cayenne.reflect.LifecycleCallbackRegistry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* @since 3.1
*/
// note: made public in 3.1 to be used in all tiers
public abstract class DataChannelSyncCallbackAction implements GraphChangeHandler {
static enum Op {
INSERT, UPDATE, DELETE
}
public static DataChannelSyncCallbackAction getCallbackAction(
LifecycleCallbackRegistry callbackRegistry,
GraphManager graphManager,
GraphDiff changes,
int syncType) {
switch (syncType) {
case DataChannel.FLUSH_CASCADE_SYNC:
case DataChannel.FLUSH_NOCASCADE_SYNC:
return new FlushCallbackAction(callbackRegistry, graphManager, changes);
case DataChannel.ROLLBACK_CASCADE_SYNC:
return new RollbackCallbackAction(callbackRegistry, graphManager, changes);
default:
throw new IllegalArgumentException("Unsupported sync type: " + syncType);
}
}
LifecycleCallbackRegistry callbackRegistry;
Collection<Object> updated;
Collection<Object> persisted;
Collection<Object> removed;
private Map<Object, Op> seenIds;
private GraphManager graphManager;
DataChannelSyncCallbackAction(LifecycleCallbackRegistry callbackRegistry,
GraphManager graphManager, GraphDiff changes) {
this.callbackRegistry = callbackRegistry;
this.graphManager = graphManager;
if (hasListeners()) {
this.seenIds = new HashMap<>();
changes.apply(this);
}
}
protected abstract boolean hasListeners();
public abstract void applyPreCommit();
public abstract void applyPostCommit();
void apply(LifecycleEvent callbackType, Collection<?> objects) {
if (seenIds != null && objects != null) {
callbackRegistry.performCallbacks(callbackType, objects);
}
}
public void nodeCreated(Object nodeId) {
Op op = seenIds.put(nodeId, Op.INSERT);
if (op == null) {
Object node = graphManager.getNode(nodeId);
if (node != null) {
if (persisted == null) {
persisted = new ArrayList<>();
}
persisted.add(node);
}
}
}
public void nodeRemoved(Object nodeId) {
Op op = seenIds.put(nodeId, Op.DELETE);
// the node may have been updated prior to delete
if (op != Op.DELETE) {
Object node = graphManager.getNode(nodeId);
if (node != null) {
if (removed == null) {
removed = new ArrayList<>();
}
removed.add(node);
if (op == Op.UPDATE) {
updated.remove(node);
}
// don't care about preceding Op.INSERT, as NEW -> DELETED objects are
// purged from the change log upstream and we don't see them here
}
}
}
public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
// TODO: andrus, 9/21/2006 - should we register to-many relationship updates?
nodeUpdated(nodeId);
}
public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
// TODO: andrus, 9/21/2006 - should we register to-many relationship updates?
nodeUpdated(nodeId);
}
public void nodeIdChanged(Object nodeId, Object newId) {
}
public void nodePropertyChanged(
Object nodeId,
String property,
Object oldValue,
Object newValue) {
nodeUpdated(nodeId);
}
private void nodeUpdated(Object nodeId) {
Op op = seenIds.put(nodeId, Op.UPDATE);
if (op == null) {
Object node = graphManager.getNode(nodeId);
if (node != null) {
if (updated == null) {
updated = new ArrayList<>();
}
updated.add(node);
}
}
}
static class FlushCallbackAction extends DataChannelSyncCallbackAction {
FlushCallbackAction(LifecycleCallbackRegistry callbackRegistry,
GraphManager graphManager, GraphDiff changes) {
super(callbackRegistry, graphManager, changes);
}
@Override
protected boolean hasListeners() {
return !(callbackRegistry.isEmpty(LifecycleEvent.PRE_UPDATE)
&& callbackRegistry.isEmpty(LifecycleEvent.PRE_PERSIST)
&& callbackRegistry.isEmpty(LifecycleEvent.POST_UPDATE)
&& callbackRegistry.isEmpty(LifecycleEvent.POST_REMOVE) && callbackRegistry
.isEmpty(LifecycleEvent.POST_PERSIST));
}
@Override
public void applyPreCommit() {
apply(LifecycleEvent.PRE_PERSIST, persisted);
apply(LifecycleEvent.PRE_UPDATE, updated);
}
@Override
public void applyPostCommit() {
apply(LifecycleEvent.POST_UPDATE, updated);
apply(LifecycleEvent.POST_REMOVE, removed);
apply(LifecycleEvent.POST_PERSIST, persisted);
}
}
static class RollbackCallbackAction extends DataChannelSyncCallbackAction {
RollbackCallbackAction(LifecycleCallbackRegistry callbackRegistry,
GraphManager graphManager, GraphDiff changes) {
super(callbackRegistry, graphManager, changes);
}
@Override
protected boolean hasListeners() {
return !callbackRegistry.isEmpty(LifecycleEvent.POST_LOAD);
}
@Override
public void applyPreCommit() {
// noop
}
@Override
public void applyPostCommit() {
apply(LifecycleEvent.POST_LOAD, updated);
apply(LifecycleEvent.POST_LOAD, removed);
}
}
}