diskCacheV111.srm.dcache.DcacheSrm.java Source code

Java tutorial

Introduction

Here is the source code for diskCacheV111.srm.dcache.DcacheSrm.java

Source

/*
 * dCache - http://www.dcache.org/
 *
 * Copyright (C) 2016 Deutsches Elektronen-Synchrotron
 *
 * 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 diskCacheV111.srm.dcache;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Uninterruptibles;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.DataAccessException;

import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

import dmg.cells.nucleus.CellAddressCore;
import dmg.cells.nucleus.CellIdentityAware;
import dmg.cells.nucleus.CellMessageReceiver;
import dmg.cells.nucleus.CellPath;
import dmg.cells.nucleus.NoRouteToCellException;
import dmg.cells.zookeeper.PathChildrenCache;

import org.dcache.cells.CellStub;
import org.dcache.cells.CuratorFrameworkAware;
import org.dcache.srm.AbstractStorageElement;
import org.dcache.srm.SRM;
import org.dcache.srm.SRMException;
import org.dcache.srm.SRMInternalErrorException;
import org.dcache.srm.SRMInvalidPathException;
import org.dcache.srm.SRMNonEmptyDirectoryException;
import org.dcache.srm.SrmAbortTransfersRequest;
import org.dcache.srm.SrmAbortTransfersResponse;
import org.dcache.srm.SrmQueryPutRequest;
import org.dcache.srm.SrmQueryPutResponse;

import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.util.stream.Collectors.toList;

/**
 * Subclass of the SRM main class implementing cross-cutting concerns in
 * clustered SrmManager deployments.
 */
public class DcacheSrm extends SRM implements CuratorFrameworkAware, CellIdentityAware, CellMessageReceiver {
    private PathChildrenCache backends;

    private CuratorFramework client;

    private CellStub srmManagerStub;

    private CellAddressCore address;

    public DcacheSrm(Configuration config, AbstractStorageElement storage)
            throws IOException, InterruptedException, DataAccessException {
        super(config, storage);
    }

    @Override
    public void start() throws Exception {
        backends = new PathChildrenCache(client, "/dcache/srm/backends", true);
        backends.start();
        super.start();
    }

    @Override
    public void stop() throws Exception {
        super.stop();
        if (backends != null) {
            backends.close();
        }
    }

    @Override
    public void setCellAddress(CellAddressCore address) {
        this.address = address;
    }

    @Override
    public void setCuratorFramework(CuratorFramework client) {
        this.client = client;
    }

    @Required
    public void setSrmManagerStub(CellStub srmManagerStub) {
        this.srmManagerStub = srmManagerStub;
    }

    @Override
    public boolean isFileBusy(URI surl) throws SRMException {
        return super.isFileBusy(surl) || findRemoteUpload(surl, false).isPresent();
    }

    @Override
    public boolean hasMultipleUploads(URI surl) throws SRMException {
        Preconditions.checkState(super.isFileBusy(surl),
                "Must only be called while at least one local upload is active");
        return super.hasMultipleUploads(surl) || findRemoteUpload(surl, false).isPresent();
    }

    @Override
    public String getUploadFileId(URI surl) throws SRMException {
        String fileId = super.getUploadFileId(surl);
        if (fileId != null) {
            return fileId;
        }

        return findRemoteUpload(surl, false).map(SrmQueryPutResponse::getFileId).orElse(null);
    }

    @Override
    public boolean abortTransfers(URI surl, String reason) throws SRMException {
        boolean isUploadAborted = abortLocalTransfers(surl, reason);
        for (ListenableFuture<SrmAbortTransfersResponse> future : queryRemotes(
                new SrmAbortTransfersRequest(surl, reason), SrmAbortTransfersResponse.class)) {
            try {
                isUploadAborted |= Uninterruptibles.getUninterruptibly(future).isUploadAborted();
            } catch (ExecutionException e) {
                Throwable cause = e.getCause();
                if (!(cause instanceof NoRouteToCellException)) {
                    Throwables.propagateIfPossible(cause, SRMException.class);
                    throw new SRMInternalErrorException("Failed to abort transfers", cause);
                }
            }
        }
        return isUploadAborted;
    }

    @Override
    public void checkRemoveDirectory(URI surl) throws SRMException {
        super.checkRemoveDirectory(surl);

        Optional<SrmQueryPutResponse> request = findRemoteUpload(surl, true);
        if (request.isPresent()) {
            if (request.get().getSurl().equals(surl)) {
                throw new SRMInvalidPathException("Not a directory");
            } else {
                throw new SRMNonEmptyDirectoryException("Directory is not empty");
            }
        }
    }

    private boolean abortLocalTransfers(URI surl, String reason) throws SRMException {
        return super.abortTransfers(surl, reason);
    }

    private Optional<SrmQueryPutResponse> findRemoteUpload(URI surl, boolean isRecursive) throws SRMException {
        for (ListenableFuture<SrmQueryPutResponse> future : queryRemotes(new SrmQueryPutRequest(surl),
                SrmQueryPutResponse.class)) {
            try {
                SrmQueryPutResponse response = Uninterruptibles.getUninterruptibly(future);
                if (response.getSurl() != null && (isRecursive || response.getSurl().equals(surl))) {
                    return Optional.of(response);
                }
            } catch (ExecutionException e) {
                Throwable cause = e.getCause();
                if (!(cause instanceof NoRouteToCellException)) {
                    Throwables.propagateIfPossible(cause, SRMException.class);
                    throw new SRMInternalErrorException("Failed to abort transfers", cause);
                }
            }
        }
        return Optional.empty();
    }

    private <T extends Serializable> List<ListenableFuture<T>> queryRemotes(Serializable request,
            Class<T> response) {
        return Futures.inCompletionOrder(backends.getCurrentData().stream().map(this::toCellAddress)
                .filter(adr -> !address.equals(adr)).map(CellPath::new)
                .map(path -> srmManagerStub.send(path, request, response)).collect(toList()));
    }

    private CellAddressCore toCellAddress(ChildData data) {
        return new CellAddressCore(new String(data.getData(), US_ASCII));
    }

    public SrmQueryPutResponse messageArrived(SrmQueryPutRequest msg) {
        return getActivePutFileRequests(msg.getSurl()).min((a, b) -> a.getSurl().compareTo(b.getSurl()))
                .map(r -> new SrmQueryPutResponse(r.getSurl(), r.getId(), r.getFileId()))
                .orElseGet(SrmQueryPutResponse::new);
    }

    public SrmAbortTransfersResponse messageArrived(SrmAbortTransfersRequest msg) throws SRMException {
        return new SrmAbortTransfersResponse(msg.getSurl(), abortLocalTransfers(msg.getSurl(), msg.getReason()));
    }
}