/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.jooby.aws; import static java.util.Objects.requireNonNull; import java.util.ArrayList; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; import org.jooby.Env; import org.jooby.Jooby; import org.jooby.internal.aws.AwsShutdownSupport; import org.jooby.internal.aws.ConfigCredentialsProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.auth.AWSCredentialsProvider; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.inject.Binder; import com.typesafe.config.Config; /** * <h1>aws module</h1> * <p> * Small utility module that exposes {@link AmazonWebServiceClient} and give you access to aws * credentials (access and secret keys). * </p> * * <h2>exposes</h2> * <ul> * <li>One ore more {@link AmazonWebServiceClient}</li> * </ul> * * <h2>usage</h2> * * application.conf: * * <pre> * aws.accessKey = AKIAIOSFODNN7EXAMPLE * aws.secretKey = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY * </pre> * * <pre> * { * use(new Aws() * .with(creds {@literal ->} new AmazonS3Client(creds)) * .with(creds {@literal ->} new AmazonSimpleEmailServiceClient(creds)) * ); * * get("/", req {@literal ->} { * AmazonS3 s3 = req.require(AmazonS3.class); * // work with s3 * }); * } * </pre> * * <p> * Keep in mind, you will need the <code>s3 (ses, sqs,sns, etc..)</code> jar in your classpath. * </p> * * <p> * This module is small and simple. All it does is bind {@link AmazonWebServiceClient} instances in * Guice. It also helps to bind utility classes like <code>TransferManager</code>. * </p> * * <pre> * { * use(new Aws() * .with(creds {@literal ->} new AmazonS3Client(creds)) * .doWith((AmazonS3Client s3) {@literal ->} new TransferManager(s3)) * ); * * post("/", req {@literal ->} { * TransferMananger tm = require(TransferManager.class); * }); * } * </pre> * * <h2>handling access and secret keys</h2> * <p> * Keys are defined in <code>.conf</code> file. It is possible to use global or per service keys. * </p> * * <p> * Here is an example of global keys: * </p> * * <pre> * aws.accessKey = AKIAIOSFODNN7EXAMPLE * aws.secretKey = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY * * aws.s3.accessKey = S3IOSFODNN7S3EXAMPLE * aws.s3.secretKey = s3alrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY * </pre> * * <pre> * { * use(new Aws() * .with(creds {@literal ->} new AmazonS3Client(creds)) // use aws.s3 keys * .with(creds {@literal ->} new AmazonSimpleEmailServiceClient(creds)) // use global keys * ); * </pre> * * It uses the {@link AmazonWebServiceClient#getServiceName()} method in order to find per service * keys. * * @author edgar * @since 0.7.0 */ @SuppressWarnings({"rawtypes", "unchecked" }) public class Aws implements Jooby.Module { /** The logging system. */ private final Logger log = LoggerFactory.getLogger(getClass()); private Builder<BiFunction<AWSCredentialsProvider, Config, AmazonWebServiceClient>> callbacks = ImmutableList.builder(); private List<BiFunction> after = new ArrayList<>(); /** * Bind an {@link AmazonWebServiceClient} instances as Guice service. * * <pre> * { * use(new Aws() * .with(creds {@literal ->} new AmazonS3Client(creds)) * ); * </pre> * * @param callback A creation callback. * @return This module. */ public Aws with( final BiFunction<AWSCredentialsProvider, Config, AmazonWebServiceClient> callback) { requireNonNull(callback, "Callback is required."); callbacks.add(callback); return this; } /** * Bind an {@link AmazonWebServiceClient} instances as Guice service. * * <pre> * { * use(new Aws() * .with((creds, conf) {@literal ->} { * AmazonS3Client s3 = new AmazonS3Client(creds); * s3.setXXX(conf.getString("XXXX")); * return s3; * }) * ); * </pre> * * @param callback A creation callback. * @return This module. */ public Aws with(final Function<AWSCredentialsProvider, AmazonWebServiceClient> callback) { return with((creds, conf) -> callback.apply(creds)); } /** * Like {@link #with(BiFunction)} but it depends on a previously created service. * * <pre> * { * use(new Aws() * .with(creds {@literal ->} new AmazonS3Client(creds)) * .with(creds {@literal ->} new AmazonSQSClient(creds)) * .doWith((AmazonS3Client s3) {@literal ->} new TransferManager(s3)) * ); * </pre> * * It will bind a <code>TransferManager</code> as a Guice service. * * @param callback A creation callback. * @param <T> Aws service type. * @return This module. */ public <T extends AmazonWebServiceClient> Aws doWith( final BiFunction<T, Config, Object> callback) { requireNonNull(callback, "Callback is required."); after.add(callback); return this; } /** * Like {@link #with(Function)} but it depends on a previously created service. * * <pre> * { * use(new Aws() * .with(creds {@literal ->} new AmazonS3Client(creds)) * .with(creds {@literal ->} new AmazonSQSClient(creds)) * .doWith((AmazonS3Client s3) {@literal ->} new TransferManager(s3)) * ); * </pre> * * It will bind a <code>TransferManager</code> as a Guice service. * * @param callback A creation callback. * @param <T> Aws service type. * @return This module. */ public <T extends AmazonWebServiceClient> Aws doWith(final Function<T, Object> callback) { requireNonNull(callback, "Callback is required."); return doWith((s, c) -> callback.apply((T) s)); } @Override public void configure(final Env env, final Config config, final Binder binder) { callbacks.build().forEach(it -> { ConfigCredentialsProvider creds = new ConfigCredentialsProvider(config); AmazonWebServiceClient service = it.apply(creds, config); creds.service(service.getServiceName()); Class serviceType = service.getClass(); Class[] interfaces = serviceType.getInterfaces(); if (interfaces.length > 0) { // pick first binder.bind(interfaces[0]).toInstance(service); } binder.bind(serviceType).toInstance(service); env.onStop(new AwsShutdownSupport(service)); after(env, binder, config, service); }); } private void after(final Env env, final Binder binder, final Config config, final AmazonWebServiceClient service) { after.forEach(it -> { try { Object dep = it.apply(service, config); requireNonNull(dep, "A nonnull value is required."); Class type = dep.getClass(); binder.bind(type).toInstance(dep); env.onStop(new AwsShutdownSupport(dep)); } catch (ClassCastException ex) { log.debug("ignoring callback {}", it); } }); } }