Example usage for org.apache.commons.lang3.tuple ImmutableTriple ImmutableTriple

List of usage examples for org.apache.commons.lang3.tuple ImmutableTriple ImmutableTriple

Introduction

In this page you can find the example usage for org.apache.commons.lang3.tuple ImmutableTriple ImmutableTriple.

Prototype

public ImmutableTriple(final L left, final M middle, final R right) 

Source Link

Document

Create a new triple instance.

Usage

From source file:richtercloud.document.scanner.gui.ScannerEditDialog.java

/**
 * Sets convenient default values on the scanner ("color" scan mode and the
 * resolution value closest to 300 DPI). Readability and writability of
 * options are checked. The type of the options are checked as well in order
 * to fail with a helpful error message in case of an errornous SANE
 * implementation./*  w w  w. j  a  va2 s  . c o  m*/
 *
 * For a list of SANE options see
 * http://www.sane-project.org/html/doc014.html.
 *
 * @param device
 * @param changedOptions
 * @param overwrite overwrite values which have already been set according
 * to {@code changedOptions}
 * @throws IOException
 * @throws SaneException
 * @throws IllegalArgumentException if the option denoted by
 * {@link #MODE_OPTION_NAME} or {@link #RESOLUTION_OPTION_NAME} isn't
 * readable or writable
 * @return the mode, resolution and document source in a {@link Triple}
 */
public static Triple<String, Integer, String> configureDefaultOptionValues(SaneDevice device,
        Map<SaneDevice, Map<SaneOption, Object>> changedOptions, boolean overwrite)
        throws IOException, SaneException {
    Map<SaneOption, Object> changedOptionsDeviceMap = changedOptions.get(device);
    if (changedOptionsDeviceMap == null) {
        changedOptionsDeviceMap = new HashMap<>();
        changedOptions.put(device, changedOptionsDeviceMap);
    }
    if (!device.isOpen()) {
        device.open();
    }
    SaneOption modeOption = device.getOption(MODE_OPTION_NAME);
    if (!modeOption.isReadable()) {
        throw new IllegalArgumentException(String.format("option '%s' isn't readable", MODE_OPTION_NAME));
    }
    if (!modeOption.getType().equals(OptionValueType.STRING)) {
        throw new IllegalArgumentException(String.format(
                "Option '%s' isn't of type STRING. This indicates an errornous SANE implementation. Can't proceed.",
                MODE_OPTION_NAME));
    }
    String mode = (String) changedOptionsDeviceMap.get(modeOption);
    if (overwrite || mode == null) {
        for (String modeConstraint : modeOption.getStringConstraints()) {
            if ("color".equalsIgnoreCase(modeConstraint.trim())) {
                mode = modeConstraint;
                break;
            }
        }
        if (mode == null) {
            mode = modeOption.getStringConstraints().get(0);
        }
        if (!modeOption.isWriteable()) {
            throw new IllegalArgumentException(String.format("Option '%s' isn't writable.", MODE_OPTION_NAME));
        }
        LOGGER.debug(String.format("setting default mode '%s' on device '%s'", mode, device));
        modeOption.setStringValue(mode);
        changedOptionsDeviceMap.put(modeOption, mode);
    }
    SaneOption resolutionOption = device.getOption(RESOLUTION_OPTION_NAME);
    if (!resolutionOption.isReadable()) {
        throw new IllegalArgumentException(
                String.format("Option '%s' isn't readable.", RESOLUTION_OPTION_NAME));
    }
    if (!resolutionOption.getType().equals(OptionValueType.INT)) {
        throw new IllegalArgumentException(String.format(
                "Option '%s' isn't of type INT. This indicates an errornous SANE implementation. Can't proceed.",
                RESOLUTION_OPTION_NAME));
    }
    Integer resolution = (Integer) changedOptionsDeviceMap.get(resolutionOption);
    if (overwrite || resolution == null) {
        int resolutionDifference = Integer.MAX_VALUE, resolutionWish = 300;
        for (SaneWord resolutionConstraint : resolutionOption.getWordConstraints()) {
            int resolutionConstraintValue = resolutionConstraint.integerValue();
            int resolutionConstraintDifference = Math.abs(resolutionWish - resolutionConstraintValue);
            if (resolutionConstraintDifference < resolutionDifference) {
                resolution = resolutionConstraintValue;
                resolutionDifference = resolutionConstraintDifference;
                if (resolutionDifference == 0) {
                    //not possible to find more accurate values
                    break;
                }
            }
        }
        assert resolution != null;
        if (!resolutionOption.isWriteable()) {
            throw new IllegalArgumentException(
                    String.format("option '%s' isn't writable", RESOLUTION_OPTION_NAME));
        }
        LOGGER.debug(String.format("setting default resolution '%d' on device '%s'", resolution, device));
        resolutionOption.setIntegerValue(resolution);
        changedOptionsDeviceMap.put(resolutionOption, resolution);
    }
    SaneOption documentSourceOption = device.getOption(DOCUMENT_SOURCE_OPTION_NAME);
    if (!documentSourceOption.isReadable()) {
        throw new IllegalArgumentException(
                String.format("Option '%s' isn't readable.", DOCUMENT_SOURCE_OPTION_NAME));
    }
    if (!documentSourceOption.getType().equals(OptionValueType.STRING)) {
        throw new IllegalArgumentException(
                String.format("Option '%s' " + "isn't of type STRING. This indicates an errornous SANE "
                        + "implementation. Can't proceed.", DOCUMENT_SOURCE_OPTION_NAME));
    }
    String documentSource = (String) changedOptionsDeviceMap.get(documentSourceOption);
    if (overwrite || documentSource == null) {
        for (String documentSourceConstraint : documentSourceOption.getStringConstraints()) {
            if (documentSourceConstraint.equalsIgnoreCase("ADF")
                    || documentSourceConstraint.equalsIgnoreCase("Automatic document feeder")) {
                documentSource = documentSourceConstraint;
                break;
            }
        }
        if (documentSource == null) {
            documentSource = documentSourceOption.getStringConstraints().get(0);
        }
        assert documentSource != null;
        if (!documentSourceOption.isWriteable()) {
            throw new IllegalArgumentException(
                    String.format("option '%s' isn't writable", DOCUMENT_SOURCE_OPTION_NAME));
        }
        LOGGER.debug(
                String.format("setting default document source '%d' on device '%s'", documentSource, device));
        documentSourceOption.setStringValue(documentSource);
        changedOptionsDeviceMap.put(documentSourceOption, documentSource);
    }
    return new ImmutableTriple<>(mode, resolution, documentSource);
}

From source file:ruc.irm.wikit.db.Wikipedia.java

public static void main(String[] args) throws ParseException, IOException {
    String helpMsg = "usage: Wikipedia -c config.xml -pid 123";

    HelpFormatter helpFormatter = new HelpFormatter();
    CommandLineParser parser = new PosixParser();
    Options options = new Options();
    options.addOption(new Option("c", true, "config file"));
    options.addOption(new Option("type", true, "database type: " + "articleByTitle|categoryByTitle|page"));

    CommandLine commandLine = parser.parse(options, args);
    if (!commandLine.hasOption("c") || !commandLine.hasOption("type")) {
        helpFormatter.printHelp(helpMsg, options);
        return;//  w ww.j av  a 2  s  . c  o  m
    }

    Conf conf = ConfFactory.createConf(commandLine.getOptionValue("c"), true);
    Wikipedia wikipedia = new Wikipedia(conf);
    String type = commandLine.getOptionValue("type");

    switch (type) {
    case "page":
        ConsoleLoop.loop(new ImmutableTriple<String, String, ConsoleLoop.Handler>("id", "list page by id",
                new ConsoleLoop.Handler() {
                    @Override
                    public void handle(String input) throws IOException {
                        Page page = wikipedia.getPageById(NumberUtils.toInt(input));
                        System.out.println(page);
                        if (page != null) {
                            System.out.println(page.getContent());
                        }
                    }
                }), new ImmutableTriple<String, String, ConsoleLoop.Handler>("list", "list all",
                        new ConsoleLoop.Handler() {
                            @Override
                            public void handle(String input) throws IOException {
                                PageIterator it = wikipedia.getPageIterator();
                                Scanner scanner = new Scanner(System.in);
                                while (it.hasNext()) {
                                    Page page = it.next();
                                    System.out.println(page);
                                    if (page.getContent().length() > 200) {
                                        System.out.println(page.getContent().substring(0, 200));
                                    } else {
                                        System.out.println(page.getContent());
                                    }

                                    System.out.println("type exit to return, or enter to continue");
                                    String command = scanner.nextLine();
                                    if (command.equalsIgnoreCase("exit")) {
                                        break;
                                    }
                                }
                                it.close();
                            }
                        }));
        break;
    case "articleByTitle":
        ConsoleLoop.loop(new ImmutableTriple<String, String, ConsoleLoop.Handler>("title",
                "input title " + "and return its id", new ConsoleLoop.Handler() {
                    @Override
                    public void handle(String input) throws IOException {
                        Integer id = wikipedia.getIdByArticleTitle(input);
                        System.out.println(id);
                    }
                }), new ImmutableTriple<String, String, ConsoleLoop.Handler>("list",
                        "list all " + "article title and its id", new ConsoleLoop.Handler() {
                            @Override
                            public void handle(String input) throws IOException {
                                WIterator<String, Integer> it = wikipedia.getArticleTitleIterator();
                                int count = 0;
                                while (it.hasNext()) {
                                    WEntry<String, Integer> entry = it.next();
                                    System.out.println("\t" + entry.getKey() + "\t" + entry.getValue());

                                    count++;
                                    if (count % 20 == 0) {
                                        System.out.println("type exit to return, or enter to continue");
                                        String command = new Scanner(System.in).nextLine();
                                        if (command.equalsIgnoreCase("exit")) {
                                            break;
                                        }
                                    }
                                }
                                it.close();
                            }
                        }));
        break;
    case "categoryByTitle":
        ConsoleLoop.loop(new ImmutableTriple<String, String, ConsoleLoop.Handler>("title",
                "get id by " + "title", new ConsoleLoop.Handler() {
                    @Override
                    public void handle(String input) throws IOException {
                        Integer id = wikipedia.getIdByCategoryTitle(input);
                        System.out.println(id);
                    }
                }), new ImmutableTriple<String, String, ConsoleLoop.Handler>("list", "list all",
                        new ConsoleLoop.Handler() {
                            @Override
                            public void handle(String input) throws IOException {
                                WIterator<String, Integer> it = wikipedia.getCategoryTitleIterator();
                                int count = 0;
                                while (it.hasNext()) {
                                    WEntry<String, Integer> entry = it.next();
                                    System.out.println("\t" + entry.getKey() + "\t" + entry.getValue());

                                    count++;
                                    if (count % 20 == 0) {
                                        System.out.println("type exit to return, or enter to continue");
                                        String command = new Scanner(System.in).nextLine();
                                        if (command.equalsIgnoreCase("exit")) {
                                            break;
                                        }
                                    }
                                }
                                it.close();
                            }
                        }));
        break;
    }
    ;

    wikipedia.close();
}

From source file:squash.deployment.lambdas.AngularjsAppCustomResourceLambda.java

/**
 * Implementation for the AWS Lambda function backing the AngularjsApp
 * resource./*  www  .  ja v a  2s.c o m*/
 * 
 * <p>
 * This lambda requires the following environment variables:
 * <ul>
 * <li>WebsiteBucket - name of S3 bucket serving the booking website.</li>
 * <li>AngularjsZipBucket - S3 bucket holding the Angularjs app zip file.</li>
 * <li>CognitoIdentityPoolId - id of the Cognito Identity Pool.</li>
 * <li>CognitoUserPoolId - id of the Cognito User Pool.</li>
 * <li>CognitoUserPoolIdentityProviderName - Name of user pool identity provider.</li>
 * <li>JavascriptClientAppId - id of the Cognito User Pool app to use from javascript.</li>
 * <li>ApiGatewayBaseUrl - base Url of the ApiGateway Api.</li>
 * <li>Region - the AWS region in which the Cloudformation stack is created.</li>
 * <li>Revision - integer incremented to force stack updates to update this resource.</li>
 * </ul>
 *
 * <p>On success, it returns the following output to Cloudformation:
 * <ul>
 *    <li>WebsiteURL - Url of the Angularjs website.</li>
 * </ul>
 *
 * <p>Updates will delete the previous deployment and replace it with the new one.
 *
 * @param request
 *            request parameters as provided by the CloudFormation service
 * @param context
 *            context as provided by the CloudFormation service
 */
@Override
public Object handleRequest(Map<String, Object> request, Context context) {

    LambdaLogger logger = context.getLogger();
    logger.log("Starting AngularjsApp custom resource handleRequest");

    // Handle standard request parameters
    Map<String, String> standardRequestParameters = LambdaInputLogger.logStandardRequestParameters(request,
            logger);
    String requestType = standardRequestParameters.get("RequestType");

    // Handle required environment variables
    logger.log("Logging required environment variables for custom resource request");
    String websiteBucket = System.getenv("WebsiteBucket");
    String angularjsZipBucket = System.getenv("AngularjsZipBucket");
    String cognitoIdentityPoolId = System.getenv("CognitoIdentityPoolId");
    String cognitoUserPoolId = System.getenv("CognitoUserPoolId");
    String cognitoUserPoolIdentityProviderName = System.getenv("CognitoUserPoolIdentityProviderName");
    String javascriptClientAppId = System.getenv("JavascriptClientAppId");
    String apiGatewayBaseUrl = System.getenv("ApiGatewayBaseUrl");
    String region = System.getenv("AWS_REGION");
    String revision = System.getenv("Revision");

    // Log out our required environment variables
    logger.log("WebsiteBucket: " + websiteBucket);
    logger.log("AngularjsZipBucket: " + angularjsZipBucket);
    logger.log("CognitoIdentityPoolId: " + cognitoIdentityPoolId);
    logger.log("CognitoUserPoolId: " + cognitoUserPoolId);
    logger.log("CognitoUserPoolIdentityProviderName: " + cognitoUserPoolIdentityProviderName);
    logger.log("JavascriptClientAppId: " + javascriptClientAppId);
    logger.log("ApiGatewayBaseUrl: " + apiGatewayBaseUrl);
    logger.log("Region: " + region);
    logger.log("Revision: " + revision);

    // API calls below can sometimes give access denied errors during stack
    // creation which I think is bc required new roles have not yet propagated
    // across AWS. We sleep here to allow time for this propagation.
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        logger.log("Sleep to allow new roles to propagate has been interrupted.");
    }

    // Prepare our response to be sent in the finally block
    CloudFormationResponder cloudFormationResponder = new CloudFormationResponder(standardRequestParameters,
            "DummyPhysicalResourceId");
    // Initialise failure response, which will be changed on success
    String responseStatus = "FAILED";

    String websiteURL = null;
    try {
        cloudFormationResponder.initialise();

        if (requestType.equals("Create") || requestType.equals("Update")) {

            // On updates we clear out the app first
            if (requestType.equals("Update")) {
                deleteAngularjsApp(websiteBucket, logger);
            }

            // Get the Angularjs app's zip file
            try {
                logger.log("Downloading Angularjs zip from S3");
                IS3TransferManager transferManager = getS3TransferManager();
                String zipDownloadPath = "/tmp/AngularjsApp.zip";
                File downloadedFile = new File(zipDownloadPath);
                TransferUtils.waitForS3Transfer(
                        transferManager.download(angularjsZipBucket, "AngularjsApp.zip", downloadedFile),
                        logger);
                logger.log("Downloaded Angularjs zip successfully from S3");

                // Modify the Bookings and Identity Service files to point to the
                // correct Cognito data, ApiGateway base url, and region.
                logger.log("Extracting Angularjs zip");
                String extractPath = "/tmp";
                try {
                    ZipFile zipFile = new ZipFile(zipDownloadPath);
                    // Will produce /tmp/app/app.js etc
                    zipFile.extractAll(extractPath);
                } catch (ZipException e) {
                    logger.log("Caught a ZipException Exception: " + e.getMessage());
                    throw e;
                }
                logger.log("Extracted Angularjs zip");

                logger.log(
                        "Modifying the Bookings and Identity Services to point to the correct ApiGatewayBaseUrl, Cognito data, and region");
                String fileContent;
                String filePath = extractPath + "/app/sqawsh.min.js";
                try (FileInputStream inputStream = new FileInputStream(filePath)) {
                    fileContent = IOUtils.toString(inputStream);
                }
                fileContent = fileContent.replace("bookingregiontobereplaced", region)
                        .replace("bookingurltobereplaced", apiGatewayBaseUrl)
                        .replace("bookingbuckettobereplaced", websiteBucket)
                        .replace("identityregiontobereplaced", region)
                        .replace("identitypoolidtobereplaced", cognitoIdentityPoolId)
                        .replace("identityuserpoolidtobereplaced", cognitoUserPoolId)
                        .replace("identityprovidernametobereplaced", cognitoUserPoolIdentityProviderName)
                        .replace("identityappidtobereplaced", javascriptClientAppId);

                FileUtils.writeStringToFile(new File(filePath), fileContent);
                logger.log(
                        "Modified the Bookings and Identity Services to point to the correct ApiGatewayBaseUrl, Cognito data, and region");

                // We will later modify the gzip-ed filenames to add a revving suffix.
                // But before we gzip, we need to modify the revved file links in
                // index.html
                String revvingSuffix = System.getenv("RevvingSuffix");
                File appPath = new File("/tmp/app");
                logger.log("Modifying links to revved files in index.html");
                Path indexPath = new File(appPath, "index.html").toPath();
                Charset charset = StandardCharsets.UTF_8;
                List<String> newLines = new ArrayList<>();
                for (String line : Files.readAllLines(indexPath, charset)) {
                    if (line.contains("googleapis") || line.contains("cloudflare") || line.contains("maxcdn")) {
                        // Don't alter lines linking to cdn-s. They are already revved.
                        newLines.add(line);
                    } else {
                        newLines.add(line.replace(".js", "_" + revvingSuffix + ".js").replace(".css",
                                "_" + revvingSuffix + ".css"));
                    }
                }
                Files.write(indexPath, newLines, charset);
                logger.log("Modified links to revved files in index.html");

                // GZIP all js, css, and html files within app folder
                logger.log("GZip-ing files in app folder to enable serving gzip-ed from S3");
                squash.deployment.lambdas.utils.FileUtils.gzip(Arrays.asList(appPath), Collections.emptyList(),
                        logger);
                logger.log("GZip-ed files in app folder to enable serving gzip-ed from S3");

                // Rev the js and css files by appending revving-suffix to names - for
                // cache-ing
                logger.log("Appending revving suffix to js and css files in app folder");
                squash.deployment.lambdas.utils.FileUtils.appendRevvingSuffix(revvingSuffix, appPath.toPath(),
                        logger);
                logger.log("Appended revving suffix to js and css files in app folder");

                // Upload the modified app to the S3 website bucket
                logger.log("Uploading modified Angularjs app to S3 website bucket");
                // Will produce <S3BucketRoot>/app/sqawsh.min.js etc
                TransferUtils.waitForS3Transfer(transferManager.uploadDirectory(websiteBucket, "app",
                        new File(extractPath + "/app"), true), logger);
                logger.log("Uploaded modified Angularjs app to S3 website bucket");

                // Add gzip content-encoding metadata to zip-ed files
                logger.log("Updating metadata on modified Angularjs app in S3 bucket");
                TransferUtils.addGzipContentEncodingMetadata(websiteBucket, Optional.of("app"), logger);
                logger.log("Updated metadata on modified Angularjs app in S3 bucket");

                // Upload Cognito SDKs and their dependencies - these should all be
                // zipped first. N.B. We also append filenames with the revving
                // suffix.
                logger.log("About to upload Cognito libraries");
                List<ImmutableTriple<String, String, byte[]>> cognitoLibraries = new ArrayList<>();
                cognitoLibraries.add(new ImmutableTriple<>("Cognito SDK",
                        "aws-cognito-sdk.min_" + revvingSuffix + ".js", IOUtils.toByteArray(new URL(
                                "https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/aws-cognito-sdk.min.js"))));
                cognitoLibraries.add(new ImmutableTriple<>("Cognito Identity SDK",
                        "amazon-cognito-identity.min_" + revvingSuffix + ".js", IOUtils.toByteArray(new URL(
                                "https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/amazon-cognito-identity.min.js"))));
                cognitoLibraries.add(new ImmutableTriple<>("Big Integer Library",
                        "jsbn_" + revvingSuffix + ".js",
                        IOUtils.toByteArray(new URL("http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js"))));
                cognitoLibraries.add(new ImmutableTriple<>("Big Integer Library 2",
                        "jsbn2_" + revvingSuffix + ".js", IOUtils.toByteArray(
                                new URL("http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn2.js"))));

                // The SJCL still seems to need configuring to include the bytes
                // codec, despite 1.0 of Cognito Idp saying it had removed this
                // dependency. So for now we get this bytes-codec-configured version
                // from our resources.
                String sjcl_library;
                try {
                    sjcl_library = IOUtils.toString(AngularjsAppCustomResourceLambda.class
                            .getResourceAsStream("/squash/deployment/lambdas/sjcl.js"));
                } catch (IOException e) {
                    logger.log("Exception caught reading sjcl.js file: " + e.getMessage());
                    throw new Exception("Exception caught reading sjcl.js file");
                }
                logger.log("Read modified SJCL library from resources");
                cognitoLibraries.add(new ImmutableTriple<>("Stanford Javascript Crypto Library",
                        "sjcl_" + revvingSuffix + ".js", sjcl_library.getBytes(Charset.forName("UTF-8"))));

                for (ImmutableTriple<String, String, byte[]> cognitoLibrary : cognitoLibraries) {
                    logger.log("Uploading a Cognito library to S3 website bucket. Library name: "
                            + cognitoLibrary.left);

                    byte[] zippedLibrary = squash.deployment.lambdas.utils.FileUtils.gzip(cognitoLibrary.right,
                            logger);
                    ByteArrayInputStream libraryAsGzippedStream = new ByteArrayInputStream(zippedLibrary);
                    ObjectMetadata metadata = new ObjectMetadata();
                    metadata.setContentLength(zippedLibrary.length);
                    metadata.setContentEncoding("gzip");
                    String keyName = "app/components/identity/cognito/" + cognitoLibrary.middle;
                    logger.log("Uploading to key: " + keyName);
                    PutObjectRequest putObjectRequest = new PutObjectRequest(websiteBucket, keyName,
                            libraryAsGzippedStream, metadata);
                    TransferUtils.waitForS3Transfer(transferManager.upload(putObjectRequest), logger);
                    logger.log("Uploaded a Cognito library to S3 website bucket: " + cognitoLibrary.left);
                }

                // Add cache-control metadata to files. Css and js files will have
                // 1-year cache validity, since they are rev-ved.
                logger.log("Updating cache-control metadata on angular app in S3 bucket");
                TransferUtils.addCacheControlHeader("max-age=31536000", websiteBucket, Optional.of("app"),
                        ".js", logger);
                TransferUtils.addCacheControlHeader("max-age=31536000", websiteBucket, Optional.of("app"),
                        ".css", logger);
                // All html must revalidate every time
                TransferUtils.addCacheControlHeader("no-cache, must-revalidate", websiteBucket,
                        Optional.of("app"), ".html", logger);
                logger.log("Updated cache-control metadata on angular app in S3 bucket");

                // App content must be public so it can be served from the website
                logger.log("Modifying Angularjs app ACL in S3 website bucket");
                TransferUtils.setPublicReadPermissionsOnBucket(websiteBucket, Optional.of("app/"), logger);
                logger.log("Modified Angularjs app ACL in S3 website bucket");

            } catch (MalformedInputException mie) {
                logger.log("Caught a MalformedInputException: " + mie.getMessage());
                throw mie;
            } catch (IOException ioe) {
                logger.log("Caught an IO Exception: " + ioe.getMessage());
                throw ioe;
            }

            websiteURL = "http://" + websiteBucket + ".s3-website-" + region + ".amazonaws.com/app/index.html";
            ;
        } else if (requestType.equals("Delete")) {
            logger.log("Delete request - so deleting the app");
            deleteAngularjsApp(websiteBucket, logger);
        }

        responseStatus = "SUCCESS";
        return null;
    } catch (AmazonServiceException ase) {
        ExceptionUtils.logAmazonServiceException(ase, logger);
        return null;
    } catch (AmazonClientException ace) {
        ExceptionUtils.logAmazonClientException(ace, logger);
        return null;
    } catch (Exception e) {
        logger.log("Exception caught in AngularjsApp Lambda: " + e.getMessage());
        return null;
    } finally {
        // Send response to CloudFormation
        cloudFormationResponder.addKeyValueOutputsPair("WebsiteURL", websiteURL);
        cloudFormationResponder.sendResponse(responseStatus, logger);
    }
}