/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.service.dxl;
import com.foundationdb.server.service.session.Session;
import com.foundationdb.util.MultipleCauseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
public final class CompositeHook implements DXLFunctionsHook {
private final Session.Key<Integer> COUNT = Session.Key.named("SUCCESS_COUNT");
private final List<DXLFunctionsHook> hooks;
public CompositeHook(List<DXLFunctionsHook> hooks) {
this.hooks = Collections.unmodifiableList(new ArrayList<>(hooks));
}
@Override
public void hookFunctionIn(Session session, DXLFunction function) {
assert session.get(COUNT) == null : session.get(COUNT);
int successes = 0;
try {
for (DXLFunctionsHook hook : hooks) {
hook.hookFunctionIn(session, function);
++successes;
}
} catch (RuntimeException e) {
Integer old = session.put(COUNT, successes);
assert old == null : old;
throw e;
}
}
@Override
public void hookFunctionCatch(Session session, DXLFunction function, Throwable throwable) {
RuntimeException eToThrow = null;
List<DXLFunctionsHook> allHooks = hooks(session);
ListIterator<DXLFunctionsHook> revIt = allHooks.listIterator(allHooks.size());
while (revIt.hasPrevious()) {
try {
DXLFunctionsHook hook = revIt.previous();
hook.hookFunctionCatch(session, function, throwable);
} catch (RuntimeException e) {
eToThrow = forException(eToThrow, e);
}
}
if (eToThrow != null) {
throw eToThrow;
}
}
@Override
public void hookFunctionFinally(Session session, DXLFunction function, Throwable throwable) {
RuntimeException eToThrow = null;
List<DXLFunctionsHook> allHooks = hooks(session);
ListIterator<DXLFunctionsHook> revIt = allHooks.listIterator(allHooks.size());
while (revIt.hasPrevious()) {
try {
DXLFunctionsHook hook = revIt.previous();
hook.hookFunctionFinally(session, function, throwable);
} catch (RuntimeException e) {
eToThrow = forException(eToThrow, e);
}
}
session.remove(COUNT);
if (eToThrow != null) {
throw eToThrow;
}
}
private List<DXLFunctionsHook> hooks(Session session) {
Integer previousSuccesses = session.get(COUNT);
return previousSuccesses == null ? this.hooks : hooks.subList(0, previousSuccesses + 1);
}
private RuntimeException forException(RuntimeException aggregate, RuntimeException exception) {
if (aggregate == null) {
return exception;
}
if (aggregate instanceof MultipleCauseException) {
((MultipleCauseException)aggregate).addCause(exception);
return aggregate;
}
MultipleCauseException multipleCauseException = new MultipleCauseException();
multipleCauseException.addCause(aggregate);
multipleCauseException.addCause(exception);
return multipleCauseException;
}
}