software.coolstuff.springframework.owncloud.service.impl.AbstractPipedStreamSynchronizerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of owncloud-spring-boot-starter Show documentation
Show all versions of owncloud-spring-boot-starter Show documentation
Spring Boot Owncloud Service and Authentication Provider
/*-
* #%L
* owncloud-spring-boot-starter
* %%
* Copyright (C) 2016 - 2017 by the original Authors
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
package software.coolstuff.springframework.owncloud.service.impl;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.logging.LogLevel;
import org.springframework.security.core.Authentication;
import software.coolstuff.springframework.owncloud.exception.resource.OwncloudResourcePipeSynchronizationException;
import software.coolstuff.springframework.owncloud.service.api.OwncloudResourceService;
import software.coolstuff.springframework.owncloud.service.impl.OwncloudProperties.ResourceServiceProperties;
import java.io.*;
import java.net.URI;
import java.util.Optional;
import java.util.concurrent.CyclicBarrier;
/**
* Abstract Class of the PipedStream Synchronizer Implementations.
*
* This Class provides only protected Methods used by
* the PipedStream Synchronizer Implementations.
*
* A PipedStream Synchronizer handles the Synchronization between
* {@link PipedInputStream} and {@link PipedOutputStream} and acts
* as a Proxy between the Application which uses the Owncloud API
* and the Implementation in the Background (either local or REST based).
*
* In fact, the Method {@link OwncloudResourceService#getInputStream(software.coolstuff.springframework.owncloud.model.OwncloudFileResource)}
* will return a {@link PipedInputStream} whereas the Methods {@link OwncloudResourceService#getOutputStream(software.coolstuff.springframework.owncloud.model.OwncloudFileResource)}
* and {@link OwncloudResourceService#getOutputStream(URI, org.springframework.http.MediaType)} will return a {@link PipedOutputStream}.
*
* The reverse Part of the returned Stream will be handled by a PipedStream Synchronizer Implementation
*
* @author mufasa1976
* @since 1.2.0
*/
@Slf4j
public abstract class AbstractPipedStreamSynchronizerImpl {
protected static final int EOF = -1;
@Getter(AccessLevel.PROTECTED)
private final Authentication authentication;
private final OwncloudProperties owncloudProperties;
private final URI uri;
private Thread thread;
@Getter(AccessLevel.PROTECTED)
private boolean interrupted;
private final CyclicBarrier pipeSync = new CyclicBarrier(2);
protected AbstractPipedStreamSynchronizerImpl(
final Authentication authentication,
final OwncloudProperties owncloudProperties,
final URI uri) {
this.authentication = authentication;
this.owncloudProperties = owncloudProperties;
this.uri = uri;
}
@Override
protected void finalize() throws Throwable {
if (thread != null && thread.isAlive()) {
log.debug("Stop Thread {}", thread.getName());
interrupt(thread);
thread.join();
}
}
private void interrupt(Thread thread) {
interrupted = true;
thread.interrupt();
}
protected void setPipeReady() {
try {
log.debug("Release the Cyclic Barrier for the synchronized piped Stream of Thread {}", Thread.currentThread());
pipeSync.await();
} catch (Exception e) {
final String logMessage = String.format("Error while waiting for the PipedStream-Barrier by Thread %s", Thread.currentThread());
log.error(logMessage, e);
throw new OwncloudResourcePipeSynchronizationException(logMessage, e);
}
}
protected final void startThreadAndWaitForConnectedPipe() {
if (isThreadExistsAndIsAlive()) {
return;
}
startBackgroundThread();
waitForPipeReady();
}
private boolean isThreadExistsAndIsAlive() {
return thread != null && thread.isAlive();
}
private void startBackgroundThread() {
interrupted = false;
thread = new Thread(this::createPipedStream);
thread.setName(getThreadName());
thread.setUncaughtExceptionHandler(this::handleUncaughtException);
log.debug("Start the Background Thread {}", thread.getName());
thread.start();
}
protected abstract void createPipedStream();
protected abstract String getThreadName();
private void handleUncaughtException(Thread thread, Throwable cause) {
LogLevel logLevel = getUncaughtExceptionLogLevel();
if (logLevel == LogLevel.OFF) {
return;
}
final String logMessage = String.format("Error while executing %s", thread);
switch (logLevel) {
case TRACE:
log.trace(logMessage, cause);
break;
case DEBUG:
log.debug(logMessage, cause);
break;
case INFO:
log.info(logMessage, cause);
break;
case WARN:
log.warn(logMessage, cause);
break;
default:
log.error(logMessage, cause);
}
}
private LogLevel getUncaughtExceptionLogLevel() {
ResourceServiceProperties resourceProperties = owncloudProperties.getResourceService();
return resourceProperties.getPipedStreamUncaughtExceptionLogLevel();
}
protected void waitForPipeReady() {
try {
log.debug("Thread {} waits for the Background Thread of the synchronized piped Stream", Thread.currentThread());
pipeSync.await();
} catch (Exception e) {
log.error("Error while waiting for PipedStream-Barrier", e);
throw new OwncloudResourcePipeSynchronizationException(e);
}
}
protected String getUsername() {
return authentication.getName();
}
protected URI getUri() {
return uri;
}
protected long copy(InputStream input, OutputStream output) throws IOException {
long contentLength = 0;
int bufferSize = getBufferSize();
log.debug("Copy Content with a Buffersize of {} Bytes", bufferSize);
byte[] buffer = new byte[bufferSize];
for (int length = 0; (length = input.read(buffer)) != EOF; ) {
output.write(buffer, 0, length);
contentLength += length;
if (isInterrupted()) {
log.warn("Background Thread has been interrupted -> stop the Copy Process");
return contentLength;
}
}
return contentLength;
}
protected int getBufferSize() {
return Optional.ofNullable(owncloudProperties)
.map(this::extractBufferSize)
.orElse(OwncloudProperties.DEFAULT_BUFFER_SIZE);
}
private int extractBufferSize(OwncloudProperties properties) {
ResourceServiceProperties resourceProperties = properties.getResourceService();
return resourceProperties.getPipedStreamBufferSize();
}
}