Newer
Older
KeeperJerry_Launcher / LaunchServer / source / response / update / UpdateResponse.java
package launchserver.response.update;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Deque;
import java.util.LinkedList;

import launcher.hasher.HashedDir;
import launcher.hasher.HashedEntry;
import launcher.helper.IOHelper;
import launcher.helper.LogHelper;
import launcher.request.RequestException;
import launcher.request.update.UpdateRequest;
import launcher.serialize.HInput;
import launcher.serialize.HOutput;
import launcher.serialize.signed.SignedObjectHolder;
import launchserver.LaunchServer;
import launchserver.response.Response;

public final class UpdateResponse extends Response {
	public UpdateResponse(LaunchServer server, HInput input, HOutput output) {
		super(server, input, output);
	}

	@Override
	public void reply() throws IOException {
		// Read update dir name
		String updateDirName = IOHelper.verifyFileName(input.readString(255));
		SignedObjectHolder<HashedDir> hdir = server.getUpdateDir(updateDirName);
		if (hdir == null) {
			throw new RequestException(String.format("Unknown update dir: %s", updateDirName));
		}
		writeNoError(output);

		// Write update hdir
		LogHelper.subDebug("Update dir: '%s'", updateDirName);
		hdir.write(output);
		output.flush();

		// Prepare variables for actions queue
		Path dir = LaunchServer.UPDATES_DIR.resolve(updateDirName);
		Deque<HashedDir> dirStack = new LinkedList<>();
		dirStack.add(hdir.object);

		// Perform update
		UpdateRequest.Action[] actionsSlice = new UpdateRequest.Action[UpdateRequest.MAX_QUEUE_SIZE];
		loop:
		while (true) {
			// Read actions slice
			int length = input.readLength(actionsSlice.length);
			for (int i = 0; i < length; i++) {
				actionsSlice[i] = new UpdateRequest.Action(input);
			}

			// Perform actions
			for (int i = 0; i < length; i++) {
				UpdateRequest.Action action = actionsSlice[i];
				switch (action.type) {
					case CD:
						LogHelper.subDebug("CD '%s'", action.name);

						// Get hashed dir (for validation)
						HashedEntry hSubdir = dirStack.getLast().getEntry(action.name);
						if (hSubdir == null || hSubdir.getType() != HashedEntry.Type.DIR) {
							throw new IOException("Unknown hashed dir: " + action.name);
						}
						dirStack.add((HashedDir) hSubdir);

						// Resolve dir
						dir = dir.resolve(action.name);
						break;
					case GET:
						LogHelper.subDebug("GET '%s'", action.name);

						// Get hashed file (for validation)
						HashedEntry hFile = dirStack.getLast().getEntry(action.name);
						if (hFile == null || hFile.getType() != HashedEntry.Type.FILE) {
							throw new IOException("Unknown hashed file: " + action.name);
						}

						// Resolve and write file
						Path file = dir.resolve(action.name);
						try (InputStream fileInput = IOHelper.newInput(file)) {
							IOHelper.transfer(fileInput, output.stream);
						}
						break;
					case CD_BACK:
						LogHelper.subDebug("CD ..");

						// Remove from hashed dir stack
						dirStack.removeLast();
						if (dirStack.isEmpty()) {
							throw new IOException("Empty hDir stack");
						}

						// Get parent
						dir = dir.getParent();
						break;
					case FINISH:
						break loop;
					default:
						throw new AssertionError(String.format("Unsupported action type: '%s'", action.type.name()));
				}
			}

			// Flush all actions
			output.flush();
		}

		// So we've updated :)
	}
}