SaveToFileRequestHandler.java 11.5 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.scene7.is.provider.RequestTypeEnum
 *  com.scene7.is.provider.RequestTypeSpec
 *  com.scene7.is.provider.catalog.Catalog
 *  com.scene7.is.provider.catalog.CatalogException
 *  com.scene7.is.provider.catalog.ObjectRecord
 *  com.scene7.is.sleng.CacheEnum
 *  com.scene7.is.sleng.FXGServer
 *  com.scene7.is.sleng.ImageAccess
 *  com.scene7.is.sleng.ImageAccessException
 *  com.scene7.is.sleng.ImageServer
 *  com.scene7.is.sleng.ipp.IppConnectionException
 *  com.scene7.is.util.AbstractPath
 *  com.scene7.is.util.error.Scaffold
 *  com.scene7.is.util.text.ParameterException
 *  com.scene7.is.util.text.ParsingException
 *  org.jetbrains.annotations.NotNull
 */
package com.scene7.is.ps.provider;

import com.scene7.is.provider.RequestTypeEnum;
import com.scene7.is.provider.RequestTypeSpec;
import com.scene7.is.provider.catalog.Catalog;
import com.scene7.is.provider.catalog.CatalogException;
import com.scene7.is.provider.catalog.ObjectRecord;
import com.scene7.is.ps.provider.IZoomException;
import com.scene7.is.ps.provider.ModifierSet;
import com.scene7.is.ps.provider.PropertiesHandler;
import com.scene7.is.ps.provider.Request;
import com.scene7.is.ps.provider.SlengCompiler;
import com.scene7.is.ps.provider.Util;
import com.scene7.is.ps.provider.defs.ModifierEnum;
import com.scene7.is.sleng.CacheEnum;
import com.scene7.is.sleng.FXGServer;
import com.scene7.is.sleng.ImageAccess;
import com.scene7.is.sleng.ImageAccessException;
import com.scene7.is.sleng.ImageServer;
import com.scene7.is.sleng.ipp.IppConnectionException;
import com.scene7.is.util.AbstractPath;
import com.scene7.is.util.error.Scaffold;
import com.scene7.is.util.text.ParameterException;
import com.scene7.is.util.text.ParsingException;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;

public class SaveToFileRequestHandler
extends PropertiesHandler {
    private final ImageServer server;
    private final FXGServer fxgServer;
    private final Semaphore semaphore;
    private final AtomicInteger overlap = new AtomicInteger();
    private final int maxQueued;
    private final int maxThreads;
    private final long saveTimeout;
    private final long pollingInterval;
    private static AtomicInteger CURRENT_ACTIVE_THREADS = new AtomicInteger();
    private static AtomicInteger CURRENT_QUEUED_THREADS = new AtomicInteger();

    SaveToFileRequestHandler(@NotNull ImageServer server, @NotNull FXGServer fxgServer, long saveTimeout, int maxQueued, int maxThreads, long pollingInterval) {
        assert (maxQueued >= 0);
        assert (maxThreads > 0);
        this.server = server;
        this.fxgServer = fxgServer;
        this.saveTimeout = saveTimeout;
        this.maxQueued = maxQueued;
        this.maxThreads = maxThreads;
        this.pollingInterval = pollingInterval;
        this.semaphore = new Semaphore(maxThreads, true);
    }

    @Override
    public Map<String, Object> getProperties(Request request) throws ImageAccessException, IZoomException, CatalogException, ParsingException {
        ModifierSet attr = request.getGlobalAttributes();
        try {
            RequestTypeEnum requestType = attr.getOrDie(ModifierEnum.REQ).type;
            if (requestType == RequestTypeEnum.SAVE_TO_FILE) {
                String savePath = request.getGlobalAttributes().get(ModifierEnum.I_SAVE_PATH, "");
                if (savePath == null || savePath.isEmpty()) {
                    throw new IZoomException(IZoomException.INVALID_SAVE_TO_FILE, "Save to file is disabled for this catalog", null);
                }
                String name = attr.get(ModifierEnum.NAME);
                if (!SaveToFileRequestHandler.isValidName(name)) {
                    throw new IZoomException(IZoomException.ILLEGAL_PATH, name, null);
                }
                SlengCompiler compiler = new SlengCompiler(this.server, true, false, false);
                byte[] code = Util.doOptimize(request) ? compiler.compileOptimizedRequest(this.fxgServer, request) : compiler.compileRequest(request);
                Catalog catalog = request.getRecord().getCatalog();
                ObjectRecord record = catalog.getRecord(name);
                AbstractPath catalogPath = record.getCatalog().getPath();
                String subPath = record.getName();
                String fileName = new AbstractPath(catalogPath, subPath).getPath();
                if (savePath.startsWith("/")) {
                    savePath = "" + '.' + savePath;
                }
                String prependedFileName = SaveToFileRequestHandler.prependPath(savePath, fileName);
                long timeout = attr.get(ModifierEnum.TIMEOUT, this.saveTimeout);
                return this.saveInitSynchronous(code, prependedFileName, timeout);
            }
            throw Scaffold.error((Object)requestType);
        }
        catch (ParameterException e) {
            IZoomException wrapper = new IZoomException(IZoomException.INVALID_REQUEST, "save to file handler", (Throwable)e);
            wrapper.setProperty("message", e.getMessage());
            throw wrapper;
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private Map<String, Object> saveInitSynchronous(byte[] slengCode, String fileName, long timeout) throws ImageAccessException {
        int currentOverlap = this.overlap.incrementAndGet();
        try {
            Map<String, Object> map = this.synchronousSave(slengCode, fileName, timeout, currentOverlap);
            return map;
        }
        finally {
            this.overlap.getAndDecrement();
        }
    }

    private Map<String, Object> synchronousSave(byte[] slengCode, String fileName, long timeout, int currentOverlap) throws ImageAccessException {
        ImageAccess imageAccess = this.server.getImageAccess(this.fxgServer, CacheEnum.ON, slengCode);
        try {
            if (currentOverlap <= this.maxThreads + this.maxQueued) {
                Map<String, Object> map = this.saveInitSynchronousGuarded(imageAccess, fileName, timeout);
                return map;
            }
            throw new IppConnectionException(8, null);
        }
        finally {
            imageAccess.dispose();
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private Map<String, Object> saveInitSynchronousGuarded(ImageAccess imageAccess, String fileName, long timeout) throws ImageAccessException {
        long startTime = System.currentTimeMillis();
        try {
            Map<String, Object> statusProps;
            boolean result;
            CURRENT_QUEUED_THREADS.incrementAndGet();
            try {
                result = this.semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS);
            }
            finally {
                CURRENT_QUEUED_THREADS.getAndDecrement();
            }
            if (!result) {
                throw new ImageAccessException(12, "SaveToFile timed out while in queue waiting for permit", null);
            }
            CURRENT_ACTIVE_THREADS.incrementAndGet();
            try {
                statusProps = this.handleSynchronousSave(imageAccess, fileName, timeout, startTime);
            }
            finally {
                CURRENT_ACTIVE_THREADS.getAndDecrement();
            }
            return statusProps;
        }
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new ImageAccessException(12, "Thread was interrupted while attempting to acquire semaphore", (Throwable)e);
        }
    }

    private Map<String, Object> handleSynchronousSave(ImageAccess imageAccess, String fileName, long timeout, long startTime) throws ImageAccessException {
        try {
            long startTimeLocal = startTime;
            String saveId = this.server.saveInit(imageAccess, fileName);
            Map statusProps = this.server.saveProgress(saveId);
            boolean timeoutReached = false;
            while (statusProps.get("status").equals("queued") || statusProps.get("status").equals("processing")) {
                Thread.sleep(this.pollingInterval);
                statusProps = this.server.saveProgress(saveId);
                long currentTime = System.currentTimeMillis();
                if (currentTime - startTimeLocal <= timeout) continue;
                String currentStatus = (String)statusProps.get("status");
                if (currentStatus.equals("processing")) {
                    if (timeoutReached) {
                        this.server.saveCancel(saveId);
                        throw new ImageAccessException(12, "SaveToFile timed out while processing", null);
                    }
                    startTimeLocal = System.currentTimeMillis();
                    timeoutReached = true;
                    continue;
                }
                if (currentStatus.equals("queued")) {
                    this.server.saveCancel(saveId);
                    throw new ImageAccessException(12, "SaveToFile timed out while in image server queue", null);
                }
                assert (currentStatus.equals("done") || currentStatus.equals("failed") || currentStatus.equals("canceled"));
                break;
            }
            Map currentTime = statusProps;
            return currentTime;
        }
        catch (InterruptedException e) {
            throw new ImageAccessException(12, "Interrupted thread during synchronous save", (Throwable)e);
        }
        finally {
            this.semaphore.release();
        }
    }

    private static String prependPath(String savePath, String fileName) {
        boolean fileNameBeginsWithSeparator;
        assert (savePath != null && !savePath.isEmpty());
        assert (fileName != null && !fileName.isEmpty());
        boolean savePathEndsInSeparator = savePath.charAt(savePath.length() - 1) == '/';
        boolean bl = fileNameBeginsWithSeparator = fileName.charAt(0) == '/';
        if (!savePathEndsInSeparator && !fileNameBeginsWithSeparator) {
            return savePath + '/' + fileName;
        }
        if (savePathEndsInSeparator && fileNameBeginsWithSeparator) {
            return savePath.substring(0, savePath.length() - 1) + fileName;
        }
        return savePath + fileName;
    }

    public static boolean isValidName(@NotNull String name) {
        if (name.isEmpty()) {
            return false;
        }
        if (name.equals("..")) {
            return false;
        }
        if (name.contains(":")) {
            return false;
        }
        if (name.startsWith("//")) {
            return false;
        }
        if (name.startsWith("\\\\")) {
            return false;
        }
        if (name.contains("../")) {
            return false;
        }
        if (name.contains("/..")) {
            return false;
        }
        if (name.contains("\\..")) {
            return false;
        }
        if (name.contains("..\\")) {
            return false;
        }
        return true;
    }

    public long getSaveTimeout() {
        return this.saveTimeout;
    }

    public int getMaxQueued() {
        return this.maxQueued;
    }

    public int getMaxThreads() {
        return this.maxThreads;
    }

    public long getPollingInterval() {
        return this.pollingInterval;
    }

    public static int getCurrentActiveThreads() {
        return CURRENT_ACTIVE_THREADS.get();
    }

    public static int getCurrentQueuedThreads() {
        return CURRENT_QUEUED_THREADS.get();
    }
}