Java tutorial
// Copyright 2015-2016 Eivind Vegsundvg // // 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 ninja.eivind.hotsreplayuploader.services; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; import ninja.eivind.hotsreplayuploader.concurrent.tasks.UploadTask; import ninja.eivind.hotsreplayuploader.files.AccountDirectoryWatcher; import ninja.eivind.hotsreplayuploader.models.ReplayFile; import ninja.eivind.hotsreplayuploader.models.Status; import ninja.eivind.hotsreplayuploader.providers.Provider; import ninja.eivind.hotsreplayuploader.repositories.FileRepository; import ninja.eivind.stormparser.StormParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.stream.Collectors; /** * {@link ScheduledService}, that is responsible for uploading {@link ReplayFile}s * to {@link Provider}s. Does also take care of updating the UI in the process. */ @Component public class UploaderService extends ScheduledService<ReplayFile> implements InitializingBean { private static final Logger logger = LoggerFactory.getLogger(UploaderService.class); private final StringProperty uploadedCount = new SimpleStringProperty("0"); private final BlockingQueue<ReplayFile> uploadQueue; private final ObservableList<ReplayFile> files; @Autowired private AccountDirectoryWatcher watcher; @Autowired private FileRepository fileRepository; @Autowired private List<Provider> providers; @Autowired private StormParser parser; public UploaderService() throws IOException { logger.info("Instantiating " + getClass().getSimpleName()); uploadQueue = new LinkedBlockingQueue<>(); files = FXCollections.observableArrayList(); setExecutor(Executors.newCachedThreadPool()); logger.info("Instantiated " + getClass().getSimpleName()); } @Override public void afterPropertiesSet() { logger.info("Initializing " + getClass().getSimpleName()); watcher.addFileListener(file -> { fileRepository.updateReplay(file); files.add(file); uploadQueue.add(file); }); registerInitial(); logger.info("Initialized " + getClass().getSimpleName()); } private void registerInitial() { logger.info("Registering initial replays."); final List<ReplayFile> fileList = fileRepository.getAll(); uploadQueue.addAll(fileList.stream().filter(replayFile -> replayFile.getStatus() == Status.NEW) .collect(Collectors.toList())); updateUploadedCount(fileList); getQueuableFiles(fileList); } private void getQueuableFiles(final List<ReplayFile> mapped) { logger.info("Registering not yet uploaded replays."); files.addAll(mapped.stream().filter( replayFile -> replayFile.getStatus() == Status.NEW || replayFile.getStatus() == Status.EXCEPTION) .collect(Collectors.toList())); } private void updateUploadedCount(final List<ReplayFile> mapped) { logger.info("Updating counter for uploaded replays."); uploadedCount.set(String .valueOf(mapped.stream().filter(replayFile -> replayFile.getStatus() == Status.UPLOADED).count())); } @Override protected Task<ReplayFile> createTask() { if (isIdle()) { return new Task<ReplayFile>() { @Override protected ReplayFile call() throws Exception { Thread.sleep(2000); return null; } }; } try { logger.info("Attempting to take file from queue"); final ReplayFile replayFile = uploadQueue.take(); if (!replayFile.getFile().exists()) { fileRepository.deleteReplay(replayFile); files.remove(replayFile); return createTask(); } final UploadTask uploadTask = new UploadTask(providers, replayFile, parser); uploadTask.setOnSucceeded(event -> { try { final ReplayFile result = uploadTask.get(); final Status status = result.getStatus(); logger.info("Resolved status {} for {}", status, result); switch (status) { case UPLOADED: final int newCount = Integer.valueOf(uploadedCount.getValue()) + 1; uploadedCount.setValue(String.valueOf(newCount)); logger.info("Upload count updated to " + newCount); case UNSUPPORTED_GAME_MODE: result.getFailedProperty().setValue(false); logger.info("Removing {} from display list", result); files.remove(result); break; case EXCEPTION: case NEW: logger.warn("Upload failed for replay " + result + ". Tagging replay."); result.getFailedProperty().set(true); break; } logger.info("Updating {} in database.", result); fileRepository.updateReplay(result); logger.info("Finished handling file."); } catch (InterruptedException | ExecutionException e) { logger.error("Could not execute task successfully.", e); } }); uploadTask.setOnFailed(event -> { logger.error("UploadTask failed.", event.getSource().getException()); uploadQueue.add(replayFile); }); logger.info("Prepared task"); return uploadTask; } catch (InterruptedException e) { logger.warn("Service interrupted while waiting for task", e); return null; } } public boolean isIdle() { return uploadQueue.isEmpty(); } public StringProperty getUploadedCount() { return uploadedCount; } public void invalidateReplay(ReplayFile item) { item.getUploadStatuses().stream().filter(status -> status.getStatus() != Status.UPLOADED) .forEach(status -> status.setStatus(Status.NEW)); uploadQueue.add(item); logger.info("Replay " + item + " invalidated and marked as new."); } public void deleteReplay(ReplayFile item) { files.remove(item); fileRepository.deleteReplay(item); } public ObservableList<ReplayFile> getFiles() { return files; } }