package io.eguan.vvr.repository.core.api; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * Licensed 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. * #L% */ import io.eguan.configuration.AbstractConfigKey; import io.eguan.configuration.ConfigValidationException; import io.eguan.configuration.MetaConfiguration; import io.eguan.dtx.DtxTaskAdm; import io.eguan.dtx.DtxTaskApi; import io.eguan.dtx.DtxTaskApiAbstract; import io.eguan.dtx.DtxTaskStatus; import io.eguan.dtx.config.DtxConfigurationContext; import io.eguan.dtx.config.DtxTaskKeeperAbsoluteDurationConfigKey; import io.eguan.dtx.config.DtxTaskKeeperAbsoluteSizeConfigKey; import io.eguan.dtx.config.DtxTaskKeeperMaxDurationConfigKey; import io.eguan.dtx.config.DtxTaskKeeperMaxSizeConfigKey; import io.eguan.dtx.config.DtxTaskKeeperPurgeDelayConfigKey; import io.eguan.dtx.config.DtxTaskKeeperPurgePeriodConfigKey; import io.eguan.net.MsgClientStartpoint; import io.eguan.net.MsgServerRemoteStatus; import io.eguan.net.MsgServerTimeoutException; import io.eguan.proto.Common.OpCode; import io.eguan.proto.Common.ProtocolVersion; import io.eguan.proto.Common.Type; import io.eguan.proto.Common.Uuid; import io.eguan.proto.vvr.VvrRemote.RemoteOperation; import io.eguan.vvr.configuration.keys.DeletedConfigKey; import io.eguan.vvr.remote.VvrRemoteUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.ConnectException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.concurrent.GuardedBy; import com.google.common.eventbus.EventBus; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageLite; /** * Abstract superclass for all {@link VersionedVolumeRepository} implementations. * * This class provides a {@link AbstractRepositoryImpl.Builder builder} taking a configuration parameter made available * via {@link #getConfiguration()}. * * @author oodrive * @author llambert * @author pwehrle * @author ebredzinski * @author jmcaba * */ public abstract class AbstractRepositoryImpl extends AbstractUniqueVvrObject implements VersionedVolumeRepository { /** * Implementation of the default Dtx task API. First 'naive' implementation: for now, all the tasks are considered * successful (which is not always true) and the tasks are executed by the current thread. * <p> * Used for unit tests in vvr. * * */ private static final class DtxTaskApiImpl extends DtxTaskApiAbstract { private static MetaConfiguration defaultConfiguration; static { try { defaultConfiguration = MetaConfiguration.newConfiguration(new ByteArrayInputStream(new byte[0]), DtxConfigurationContext.getInstance()); } catch (ConfigValidationException | IOException e) { // Ignore } } private final VersionedVolumeRepository vvr; DtxTaskApiImpl(final VersionedVolumeRepository vvr) { super(new TaskKeeperParameters(DtxTaskKeeperAbsoluteDurationConfigKey.getInstance() .getTypedValue(defaultConfiguration).longValue(), DtxTaskKeeperAbsoluteSizeConfigKey.getInstance() .getTypedValue(defaultConfiguration).intValue(), DtxTaskKeeperMaxDurationConfigKey.getInstance() .getTypedValue(defaultConfiguration).longValue(), DtxTaskKeeperMaxSizeConfigKey.getInstance() .getTypedValue(defaultConfiguration).intValue(), DtxTaskKeeperPurgePeriodConfigKey.getInstance() .getTypedValue(defaultConfiguration).longValue(), DtxTaskKeeperPurgeDelayConfigKey.getInstance() .getTypedValue(defaultConfiguration).longValue())); this.vvr = vvr; } @Override public final UUID submit(final UUID resourceId, final byte[] payload) throws IllegalStateException { final RemoteOperation operation; try { operation = RemoteOperation.parseFrom(payload); } catch (final InvalidProtocolBufferException e) { throw new IllegalStateException(e); } vvr.handleMsg(operation); final UUID taskId = UUID.randomUUID(); setTask(taskId, -1, resourceId, DtxTaskStatus.COMMITTED, null); return taskId; } @Override public final boolean cancel(final UUID taskId) throws IllegalStateException { // Should not get here throw new AssertionError(); } @Override protected TaskLoader readTask(final UUID taskId) { return new TaskLoader(new DtxTaskAdm(taskId.toString(), "", "", null, DtxTaskStatus.COMMITTED), null); } } /** Create a {@link EventBus} for this {@link VersionedVolumeRepository}s */ protected final EventBus eventBus; /** The owner of this repository. */ private final UUID ownerId; /** The node on which this repository is running. */ private final UUID nodeId; private final Uuid nodeIdTx; /** If not <code>null</code>, distributed task manager. */ private final AtomicReference<DtxTaskApi> dtxTaskApiRef; /** If not <code>null</code>, destination of remote messages. */ private final AtomicReference<MsgClientStartpoint> syncClientRef; /** Source of sent messages. The node UUID as a Uuid */ private final Uuid msgSource; /** UUID of the VVR, proto VVR version */ private final Uuid vvrUuid; /** * Reference to this repository's {@link MetaConfiguration} */ @GuardedBy(value = "configuration") private volatile MetaConfiguration configuration; /** * Protected constructor to be called by subclasses. * * @param builder * a completely initialized {@link Builder} */ protected AbstractRepositoryImpl(final Builder builder) { super(builder); if (builder.owner == null) { throw new NullPointerException("owner UUID must not be null"); } if (builder.node == null) { throw new NullPointerException("node UUID must not be null"); } this.ownerId = builder.owner; this.nodeId = builder.node; this.nodeIdTx = VvrRemoteUtils.newUuid(nodeId); this.syncClientRef = builder.syncClientRef; if (builder.dtxTaskApiRef == null) { // Create a default task manager this.dtxTaskApiRef = new AtomicReference<DtxTaskApi>(new DtxTaskApiImpl(this)); } else { this.dtxTaskApiRef = builder.dtxTaskApiRef; } this.msgSource = VvrRemoteUtils.newUuid(nodeId); configuration = builder.configuration; if (configuration == null) { throw new IllegalStateException("Configuration is null"); } final UUID uuid = getUuid(); // Event bus eventBus = new EventBus("VVR-" + uuid); // Initialise vvrUuid = VvrRemoteUtils.newUuid(uuid); } @Override public final UUID getOwnerId() { return this.ownerId; } @Override public final UUID getNodeId() { return this.nodeId; } protected final MsgClientStartpoint getMsgClientStartpoint() { return syncClientRef.get(); } protected final void enhanceMessage(final RemoteOperation.Builder opBuilder) { opBuilder.setVvr(vvrUuid); opBuilder.setSource(msgSource); } /** * @param opBuilder * @param type * @param opCode * @param async * @param peer * @return status of sync request or <code>null</code> for async messages or stand alone mode * @throws MsgServerTimeoutException * @throws InterruptedException * @throws ConnectException * if peer is not <code>null</code> */ protected Collection<MsgServerRemoteStatus> sendMessage(final RemoteOperation.Builder opBuilder, final Type type, final OpCode opCode, final boolean async, final UUID peer) throws MsgServerTimeoutException, InterruptedException, ConnectException { opBuilder.setVvr(vvrUuid); if (type == Type.VVR) { opBuilder.setUuid(vvrUuid); } return VvrRemoteUtils.sendMessage(opBuilder, syncClientRef.get(), msgSource, type, opCode, async, peer); } /** * Create the reply for a remote message. * * @param opBuilder * @param type * @param opCode * @return the created message. */ protected final MessageLite createMessageReply(final RemoteOperation.Builder opBuilder, final Type type, final OpCode opCode) { opBuilder.setVersion(ProtocolVersion.VERSION_1); opBuilder.setSource(msgSource); opBuilder.setType(type); opBuilder.setOp(opCode); return opBuilder.build(); } /** * Submit a new distributed transaction. * * @param opBuilder * @param type * @param opCode * @return the UUID of created task */ protected UUID submitTransaction(final RemoteOperation.Builder opBuilder, final Type type, final OpCode opCode) { if (type == Type.VVR) { opBuilder.setUuid(vvrUuid); opBuilder.setVvr(vvrUuid); } else { opBuilder.setVvr(vvrUuid); } return VvrRemoteUtils.submitTransaction(opBuilder, dtxTaskApiRef.get(), getUuid(), nodeIdTx, type, opCode); } @Override protected final FutureVoid submitTransaction(final RemoteOperation.Builder opBuilder, final OpCode opCode) { return new FutureVoid(AbstractRepositoryImpl.this, submitTransaction(opBuilder, Type.VVR, opCode), getUuid()); } protected final DtxTaskApi getDtxTaskApi() { return dtxTaskApiRef.get(); } /** * Flag that the VVR must be deleted. */ protected final void setDeleted() { final Map<AbstractConfigKey, Object> newKeyValueMap = new HashMap<>(); newKeyValueMap.put(DeletedConfigKey.getInstance(), Boolean.TRUE); updateConfiguration(newKeyValueMap); } @Override public final boolean isDeleted() { return DeletedConfigKey.getInstance().getTypedValue(configuration).booleanValue(); } @Override public final void registerItemEvents(final Object subscriber) { eventBus.register(subscriber); } @Override public final void unregisterItemEvents(final Object subscriber) { eventBus.unregister(subscriber); } @Override public final MetaConfiguration getConfiguration() { return configuration; } @Override public void updateConfiguration(final Map<AbstractConfigKey, Object> newKeyValueMap) { try { synchronized (configuration) { configuration = configuration.copyAndAlterConfiguration(newKeyValueMap); } } catch (final Exception e) { throw new IllegalArgumentException("Failed to update configuration", e); } } public static abstract class Builder extends AbstractUniqueVvrObject.Builder { /** * this repository's owner (mandatory). * <p> */ private UUID owner; public final Builder ownerId(final UUID ownerId) { this.owner = ownerId; return this; } /** * Current node (mandatory). * <p> */ private UUID node; public final Builder nodeId(final UUID nodeId) { this.node = nodeId; return this; } /** * Destination of remote messages (optional). * <p> */ private AtomicReference<MsgClientStartpoint> syncClientRef = new AtomicReference<>(); public final Builder syncClientRef(final AtomicReference<MsgClientStartpoint> syncClientRef) { this.syncClientRef = syncClientRef; return this; } /** * Destination of remote messages (optional). */ private AtomicReference<DtxTaskApi> dtxTaskApiRef = null; public final Builder dtxTaskApiRef(final AtomicReference<DtxTaskApi> dtxTaskApiRef) { this.dtxTaskApiRef = dtxTaskApiRef; return this; } /** * The {@link MetaConfiguration} to build the {@link VersionedVolumeRepository} on. */ private MetaConfiguration configuration; /** * Gets the current {@link MetaConfiguration} set by {@link #configuration(MetaConfiguration)}. * * @return the immutable {@link MetaConfiguration} instance or <code>null</code> if none was set */ protected MetaConfiguration getConfiguration() { return this.configuration; } public final Builder configuration(final MetaConfiguration configuration) { this.configuration = configuration; return this; } } }