/* * Copyright 2016 The Simple File Server Authors * * 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. */ package org.sfs.elasticsearch; import com.google.common.base.Optional; import io.vertx.core.json.JsonObject; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import org.sfs.Server; import org.sfs.VertxContext; import org.sfs.nodes.ClusterInfo; import org.sfs.nodes.compute.object.PruneObject; import org.sfs.rx.RxHelper; import org.sfs.vo.PersistentAccount; import org.sfs.vo.PersistentContainer; import org.sfs.vo.PersistentObject; import org.sfs.vo.TransientVersion; import rx.Observable; import rx.Scheduler; import java.util.concurrent.TimeUnit; import static com.google.common.base.Optional.absent; import static com.google.common.base.Optional.of; import static org.sfs.rx.Defer.just; import static rx.Observable.defer; public class SearchHitDestroyObjectEndableWrite extends AbstractBulkUpdateEndableWriteStream { private static final Logger LOGGER = LoggerFactory.getLogger(SearchHitDestroyObjectEndableWrite.class); private final ClusterInfo clusterInfo; private final CachedAccount cachedAccount; private final CachedContainer cachedContainer; private final Scheduler scheduler; public SearchHitDestroyObjectEndableWrite(VertxContext<Server> vertxContext) { super(vertxContext); this.clusterInfo = vertxContext.verticle().getClusterInfo(); this.scheduler = RxHelper.scheduler(vertxContext.vertx()); this.cachedAccount = new CachedAccount(vertxContext); this.cachedContainer = new CachedContainer(vertxContext); } @Override protected Observable<Optional<JsonObject>> transform(final JsonObject data, String id, long version) { return toPersistentObject(data, id, version) .doOnNext(persistentObject -> { for (TransientVersion transientVersion : persistentObject.getVersions()) { transientVersion.setDeleted(Boolean.TRUE); } }) // attempt to delete versions that need deleting .flatMap(this::pruneObject) .timeout(3, TimeUnit.MINUTES, Observable.error(new RuntimeException("Timeout on pruneObject " + data.encodePrettily())), scheduler) .map(persistentObject -> { if (persistentObject.getVersions().isEmpty()) { return absent(); } else { JsonObject jsonObject = persistentObject.toJsonObject(); return of(jsonObject); } }); } protected Observable<PersistentObject> pruneObject(PersistentObject persistentObject) { return just(persistentObject) .flatMap(new PruneObject(vertxContext)) .map(modified -> persistentObject); } protected Observable<PersistentObject> toPersistentObject(JsonObject jsonObject, String id, long version) { return defer(() -> { final String accountId = jsonObject.getString("account_id"); final String containerId = jsonObject.getString("container_id"); return just(jsonObject) .filter(jsonObject1 -> accountId != null && containerId != null) .flatMap(document -> getAccount(accountId)) .flatMap(persistentAccount -> getContainer(persistentAccount, containerId)) .map(persistentContainer -> new PersistentObject(persistentContainer, id, version).merge(jsonObject)); }); } protected Observable<PersistentAccount> getAccount(String accountId) { return cachedAccount.get(accountId); } protected Observable<PersistentContainer> getContainer(PersistentAccount persistentAccount, String containerId) { return cachedContainer.get(persistentAccount, containerId); } }