// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.container.handler.test; import com.google.common.annotations.Beta; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.jdisc.LoggingRequestHandler; import com.yahoo.container.logging.AccessLog; import com.yahoo.filedistribution.fileacquirer.FileAcquirer; import com.yahoo.jdisc.Metric; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; /** * This is a generic http handler that can be used to mock a service when testing your application on jDISC. * Configuration and necessary files are given to the handle in its configuration. * * Example config: *
* <handler id="MockService"> * <config name="container.handler.test.mockservice"> * <file>myresponses.txt</file> * </config> * <binding>http://*\/my/service/path1/*</binding> * </handler> ** * The file formats supported out of the box is text, see {@link com.yahoo.container.handler.test.MockService.TextFileHandler}. * for descriptions of the format. * * @author lulf */ @Beta public class MockService extends LoggingRequestHandler { private MockServiceHandler handler; /** * Create a mock service that mocks an external service using data provided via file distribution. * A custom handler can be created by subclassing and overriding the createHandler method. * * @param executor An {@link Executor} used to create threads. * @param accessLog An {@link AccessLog} where requests will be logged. * @param fileAcquirer A {@link FileAcquirer} which is used to fetch file from config. * @param config A {@link MockserviceConfig} for this service. * @throws InterruptedException if unable to get data file within timeout. * @throws IOException if unable to create handler due to some IO errors. */ public MockService(Executor executor, AccessLog accessLog, FileAcquirer fileAcquirer, MockserviceConfig config, Metric metric) throws InterruptedException, IOException { super(executor, accessLog, metric); File dataFile = fileAcquirer.waitFor(config.file(), config.fileAcquirerTimeout(), TimeUnit.SECONDS); this.handler = createHandler(dataFile); } /** * Create a handler for a file. Override this method to handle a custom file syntax of your own. * * @param dataFile A file to read. * @return a {@link MockServiceHandler} used to handle requests. * @throws IOException if errors occured when loading the file */ protected MockServiceHandler createHandler(File dataFile) throws IOException { if (!dataFile.getName().endsWith(".txt")) { throw new IllegalArgumentException("Default handler only support .txt files"); } return new TextFileHandler(dataFile); } @Override public final HttpResponse handle(HttpRequest request) { try { MockServiceHandler.Key key = handler.createKey(request); MockServiceHandler.Value value = handler.get(key); if (value == null) { return new ErrorResponse(404, key + " was not found"); } return new RawResponse(value.returnCode, value.data, value.contentType); } catch (Exception e) { return new ExceptionResponse(500, e); } } /** * A .txt file handler deals with the following format when reading data: * method:url:responsecode:data * * For instance: * GET:/my/path1:200:{\"foo\":\"bar\"} * PUT:/my/path1:403:{\"error\":\"permission denied\"} * TODO: Support binary files */ private static class TextFileHandler implements MockServiceHandler { private final Map