http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/application/modules/AppLauncherModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/application/modules/AppLauncherModule.java b/commons/src/main/java/com/twitter/common/application/modules/AppLauncherModule.java deleted file mode 100644 index 5c95fa2..0000000 --- a/commons/src/main/java/com/twitter/common/application/modules/AppLauncherModule.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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 com.twitter.common.application.modules; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.google.inject.AbstractModule; -import com.google.inject.Singleton; - -import com.twitter.common.stats.Stats; -import com.twitter.common.util.BuildInfo; - -/** - * Binding module for the bare minimum requirements for the - * {@link com.twitter.common.application.AppLauncher}. - * - * @author William Farner - */ -public class AppLauncherModule extends AbstractModule { - - private static final Logger LOG = Logger.getLogger(AppLauncherModule.class.getName()); - private static final AtomicLong UNCAUGHT_EXCEPTIONS = Stats.exportLong("uncaught_exceptions"); - - @Override - protected void configure() { - bind(BuildInfo.class).in(Singleton.class); - bind(UncaughtExceptionHandler.class).to(LoggingExceptionHandler.class); - } - - public static class LoggingExceptionHandler implements UncaughtExceptionHandler { - @Override public void uncaughtException(Thread t, Throwable e) { - UNCAUGHT_EXCEPTIONS.incrementAndGet(); - LOG.log(Level.SEVERE, "Uncaught exception from " + t + ":" + e, e); - } - } -}
http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/application/modules/LifecycleModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/application/modules/LifecycleModule.java b/commons/src/main/java/com/twitter/common/application/modules/LifecycleModule.java deleted file mode 100644 index 1d9af17..0000000 --- a/commons/src/main/java/com/twitter/common/application/modules/LifecycleModule.java +++ /dev/null @@ -1,195 +0,0 @@ -/** - * 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 com.twitter.common.application.modules; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import com.google.common.annotations.VisibleForTesting; -import com.google.inject.AbstractModule; -import com.google.inject.Binder; -import com.google.inject.BindingAnnotation; -import com.google.inject.Inject; -import com.google.inject.Key; -import com.google.inject.Singleton; -import com.google.inject.multibindings.Multibinder; - -import com.twitter.common.application.Lifecycle; -import com.twitter.common.application.ShutdownRegistry; -import com.twitter.common.application.ShutdownRegistry.ShutdownRegistryImpl; -import com.twitter.common.application.ShutdownStage; -import com.twitter.common.application.StartupRegistry; -import com.twitter.common.application.StartupStage; -import com.twitter.common.application.modules.LocalServiceRegistry.LocalService; -import com.twitter.common.base.Command; -import com.twitter.common.base.ExceptionalCommand; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Binding module for startup and shutdown controller and registries. - * - * Bindings provided by this module: - * <ul> - * <li>{@code @StartupStage ExceptionalCommand} - Command to execute all startup actions. - * <li>{@code ShutdownRegistry} - Registry for adding shutdown actions. - * <li>{@code @ShutdownStage Command} - Command to execute all shutdown commands. - * </ul> - * - * If you would like to register a startup action that starts a local network service, please - * consider using {@link LocalServiceRegistry}. - * - * @author William Farner - */ -public class LifecycleModule extends AbstractModule { - - /** - * Binding annotation used for local services. - * This is used to ensure the LocalService bindings are visibile within the package only, to - * prevent injection inadvertently triggering a service launch. - */ - @BindingAnnotation - @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) - @interface Service { } - - @Override - protected void configure() { - bind(Lifecycle.class).in(Singleton.class); - - bind(Key.get(ExceptionalCommand.class, StartupStage.class)).to(StartupRegistry.class); - bind(StartupRegistry.class).in(Singleton.class); - - bind(ShutdownRegistry.class).to(ShutdownRegistryImpl.class); - bind(Key.get(Command.class, ShutdownStage.class)).to(ShutdownRegistryImpl.class); - bind(ShutdownRegistryImpl.class).in(Singleton.class); - bindStartupAction(binder(), ShutdownHookRegistration.class); - - bind(LocalServiceRegistry.class).in(Singleton.class); - - // Ensure that there is at least an empty set for the service runners. - runnerBinder(binder()); - - bindStartupAction(binder(), LocalServiceLauncher.class); - } - - /** - * Thrown when a local service fails to launch. - */ - public static class LaunchException extends Exception { - public LaunchException(String msg) { - super(msg); - } - - public LaunchException(String msg, Throwable cause) { - super(msg, cause); - } - } - - /** - * Responsible for starting and stopping a local service. - */ - public interface ServiceRunner { - - /** - * Launches the local service. - * - * @return Information about the launched service. - * @throws LaunchException If the service failed to launch. - */ - LocalService launch() throws LaunchException; - } - - @VisibleForTesting - static Multibinder<ServiceRunner> runnerBinder(Binder binder) { - return Multibinder.newSetBinder(binder, ServiceRunner.class, Service.class); - } - - /** - * Binds a service runner that will start and stop a local service. - * - * @param binder Binder to bind against. - * @param launcher Launcher class for a service. - */ - public static void bindServiceRunner(Binder binder, Class<? extends ServiceRunner> launcher) { - runnerBinder(binder).addBinding().to(launcher); - binder.bind(launcher).in(Singleton.class); - } - - /** - * Binds a local service instance, without attaching an explicit lifecycle. - * - * @param binder Binder to bind against. - * @param service Local service instance to bind. - */ - public static void bindLocalService(Binder binder, final LocalService service) { - runnerBinder(binder).addBinding().toInstance( - new ServiceRunner() { - @Override public LocalService launch() { - return service; - } - }); - } - - /** - * Adds a startup action to the startup registry binding. - * - * @param binder Binder to bind against. - * @param actionClass Class to bind (and instantiate via guice) for execution at startup. - */ - public static void bindStartupAction(Binder binder, - Class<? extends ExceptionalCommand> actionClass) { - - Multibinder.newSetBinder(binder, ExceptionalCommand.class, StartupStage.class) - .addBinding().to(actionClass); - } - - /** - * Startup command to register the shutdown registry as a process shutdown hook. - */ - private static class ShutdownHookRegistration implements Command { - private final Command shutdownCommand; - - @Inject ShutdownHookRegistration(@ShutdownStage Command shutdownCommand) { - this.shutdownCommand = checkNotNull(shutdownCommand); - } - - @Override public void execute() { - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - @Override public void run() { - shutdownCommand.execute(); - } - }, "ShutdownRegistry-Hook")); - } - } - - /** - * Startup command that ensures startup and shutdown of local services. - */ - private static class LocalServiceLauncher implements Command { - private final LocalServiceRegistry serviceRegistry; - - @Inject LocalServiceLauncher(LocalServiceRegistry serviceRegistry) { - this.serviceRegistry = checkNotNull(serviceRegistry); - } - - @Override public void execute() { - serviceRegistry.ensureLaunched(); - } - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/application/modules/LocalServiceRegistry.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/application/modules/LocalServiceRegistry.java b/commons/src/main/java/com/twitter/common/application/modules/LocalServiceRegistry.java deleted file mode 100644 index baa785f..0000000 --- a/commons/src/main/java/com/twitter/common/application/modules/LocalServiceRegistry.java +++ /dev/null @@ -1,258 +0,0 @@ -/** - * 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 com.twitter.common.application.modules; - -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import org.apache.commons.lang.builder.ToStringBuilder; - -import com.twitter.common.application.ShutdownRegistry; -import com.twitter.common.application.modules.LifecycleModule.LaunchException; -import com.twitter.common.application.modules.LifecycleModule.Service; -import com.twitter.common.application.modules.LifecycleModule.ServiceRunner; -import com.twitter.common.base.Command; -import com.twitter.common.base.MorePreconditions; -import com.twitter.common.net.InetSocketAddressHelper; - -/** - * Registry for services that should be exported from the application. - * - * Example of announcing and registering a port: - * <pre> - * class MyLauncher implements Provider<LocalService> { - * public LocalService get() { - * // Launch service. - * } - * } - * - * class MyServiceModule extends AbstractModule { - * public void configure() { - * LifeCycleModule.bindServiceLauncher(binder(), MyLauncher.class); - * } - * } - * </pre> - */ -public class LocalServiceRegistry { - - private static final Predicate<LocalService> IS_PRIMARY = new Predicate<LocalService>() { - @Override public boolean apply(LocalService service) { - return service.primary; - } - }; - - private static final Function<LocalService, InetSocketAddress> SERVICE_TO_SOCKET = - new Function<LocalService, InetSocketAddress>() { - @Override public InetSocketAddress apply(LocalService service) { - try { - return InetSocketAddressHelper.getLocalAddress(service.port); - } catch (UnknownHostException e) { - throw new RuntimeException("Failed to resolve local address for " + service, e); - } - } - }; - - private static final Function<LocalService, String> GET_NAME = - new Function<LocalService, String>() { - @Override public String apply(LocalService service) { - return Iterables.getOnlyElement(service.names); - } - }; - - private final ShutdownRegistry shutdownRegistry; - private final Provider<Set<ServiceRunner>> runnerProvider; - - private Optional<InetSocketAddress> primarySocket = null; - private Map<String, InetSocketAddress> auxiliarySockets = null; - - /** - * Creates a new local service registry. - * - * @param runnerProvider provider of registered local services. - * @param shutdownRegistry Shutdown registry to tear down launched services. - */ - @Inject - public LocalServiceRegistry(@Service Provider<Set<ServiceRunner>> runnerProvider, - ShutdownRegistry shutdownRegistry) { - this.runnerProvider = Preconditions.checkNotNull(runnerProvider); - this.shutdownRegistry = Preconditions.checkNotNull(shutdownRegistry); - } - - private static final Function<LocalService, Iterable<LocalService>> AUX_NAME_BREAKOUT = - new Function<LocalService, Iterable<LocalService>>() { - @Override public Iterable<LocalService> apply(final LocalService service) { - Preconditions.checkArgument(!service.primary); - Function<String, LocalService> oneNameService = new Function<String, LocalService>() { - @Override public LocalService apply(String name) { - return LocalService.auxiliaryService(name, service.port, service.shutdownCommand); - } - }; - return Iterables.transform(service.names, oneNameService); - } - }; - - /** - * Launches the local services if not already launched, otherwise this is a no-op. - */ - void ensureLaunched() { - if (primarySocket == null) { - ImmutableList.Builder<LocalService> builder = ImmutableList.builder(); - - for (ServiceRunner runner : runnerProvider.get()) { - try { - LocalService service = runner.launch(); - builder.add(service); - shutdownRegistry.addAction(service.shutdownCommand); - } catch (LaunchException e) { - throw new IllegalStateException("Failed to launch " + runner, e); - } - } - - List<LocalService> localServices = builder.build(); - Iterable<LocalService> primaries = Iterables.filter(localServices, IS_PRIMARY); - switch (Iterables.size(primaries)) { - case 0: - primarySocket = Optional.absent(); - break; - - case 1: - primarySocket = Optional.of(SERVICE_TO_SOCKET.apply(Iterables.getOnlyElement(primaries))); - break; - - default: - throw new IllegalArgumentException("More than one primary local service: " + primaries); - } - - Iterable<LocalService> auxSinglyNamed = Iterables.concat( - FluentIterable.from(localServices) - .filter(Predicates.not(IS_PRIMARY)) - .transform(AUX_NAME_BREAKOUT)); - - Map<String, LocalService> byName; - try { - byName = Maps.uniqueIndex(auxSinglyNamed, GET_NAME); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Auxiliary services with identical names.", e); - } - - auxiliarySockets = ImmutableMap.copyOf(Maps.transformValues(byName, SERVICE_TO_SOCKET)); - } - } - - /** - * Gets the mapping from auxiliary port name to socket. - * - * @return Auxiliary port mapping. - */ - public synchronized Map<String, InetSocketAddress> getAuxiliarySockets() { - ensureLaunched(); - return auxiliarySockets; - } - - /** - * Gets the optional primary socket address, and returns an unresolved local socket address - * representing that port. - * - * @return Local socket address for the primary port. - * @throws IllegalStateException If the primary port was not set. - */ - public synchronized Optional<InetSocketAddress> getPrimarySocket() { - ensureLaunched(); - return primarySocket; - } - - /** - * An individual local service. - */ - public static final class LocalService { - private final boolean primary; - private final Set<String> names; - private final int port; - private final Command shutdownCommand; - - private LocalService(boolean primary, Set<String> names, int port, - Command shutdownCommand) { - this.primary = primary; - this.names = names; - this.port = port; - this.shutdownCommand = Preconditions.checkNotNull(shutdownCommand); - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("primary", primary) - .append("name", names) - .append("port", port) - .toString(); - } - - /** - * Creates a primary local service. - * - * @param port Service port. - * @param shutdownCommand A command that will shut down the service. - * @return A new primary local service. - */ - public static LocalService primaryService(int port, Command shutdownCommand) { - return new LocalService(true, ImmutableSet.<String>of(), port, shutdownCommand); - } - - /** - * Creates a named auxiliary service. - * - * @param name Service name. - * @param port Service port. - * @param shutdownCommand A command that will shut down the service. - * @return A new auxiliary local service. - */ - public static LocalService auxiliaryService(String name, int port, Command shutdownCommand) { - return auxiliaryService(ImmutableSet.of(name), port, shutdownCommand); - } - - /** - * Creates an auxiliary service identified by multiple names. - * - * @param names Service names. - * @param port Service port. - * @param shutdownCommand A command that will shut down the service. - * @return A new auxiliary local service. - */ - public static LocalService auxiliaryService( - Set<String> names, - int port, - Command shutdownCommand) { - - MorePreconditions.checkNotBlank(names); - return new LocalService(false, names, port, shutdownCommand); - } - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/application/modules/LogModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/application/modules/LogModule.java b/commons/src/main/java/com/twitter/common/application/modules/LogModule.java deleted file mode 100644 index e324b99..0000000 --- a/commons/src/main/java/com/twitter/common/application/modules/LogModule.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * 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 com.twitter.common.application.modules; - -import java.io.File; -import java.util.logging.Logger; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.inject.AbstractModule; -import com.google.inject.Inject; -import com.google.inject.TypeLiteral; -import com.google.inject.name.Named; -import com.google.inject.name.Names; - -import com.twitter.common.args.Arg; -import com.twitter.common.args.CmdLine; -import com.twitter.common.args.constraints.CanRead; -import com.twitter.common.args.constraints.Exists; -import com.twitter.common.args.constraints.IsDirectory; -import com.twitter.common.base.Command; -import com.twitter.common.logging.LogUtil; -import com.twitter.common.logging.RootLogConfig; -import com.twitter.common.logging.RootLogConfig.Configuration; -import com.twitter.common.net.http.handlers.LogPrinter; -import com.twitter.common.stats.StatImpl; -import com.twitter.common.stats.Stats; - -/** - * Binding module for logging-related bindings, such as the log directory. - * - * This module uses a single optional command line argument 'log_dir'. If unset, the logging - * directory will be auto-discovered via: - * {@link com.twitter.common.logging.LogUtil#getLogManagerLogDir()}. - * - * Bindings provided by this module: - * <ul> - * <li>{@code @Named(LogPrinter.LOG_DIR_KEY) File} - Log directory. - * <li>{@code Optional<RootLogConfig.Configuraton>} - If glog is enabled the configuration - * used. - * </ul> - * - * Default bindings that may be overridden: - * <ul> - * <li>Log directory: directory where application logs are written. May be overridden by binding - * to: {@code bind(File.class).annotatedWith(Names.named(LogPrinter.LOG_DIR_KEY))}. - * </ul> - * - * @author William Farner - */ -public class LogModule extends AbstractModule { - - private static final Logger LOG = Logger.getLogger(LogModule.class.getName()); - - @Exists - @CanRead - @IsDirectory - @CmdLine(name = "log_dir", - help = "The directory where application logs are written.") - private static final Arg<File> LOG_DIR = Arg.create(null); - - @CmdLine(name = "use_glog", - help = "True to use the new glog-based configuration for the root logger.") - private static final Arg<Boolean> USE_GLOG = Arg.create(true); - - @Override - protected void configure() { - // Bind the default log directory. - bind(File.class).annotatedWith(Names.named(LogPrinter.LOG_DIR_KEY)).toInstance(getLogDir()); - - LifecycleModule.bindStartupAction(binder(), ExportLogDir.class); - - Configuration configuration = null; - if (USE_GLOG.get()) { - configuration = RootLogConfig.configurationFromFlags(); - configuration.apply(); - } - bind(new TypeLiteral<Optional<Configuration>>() { }) - .toInstance(Optional.fromNullable(configuration)); - } - - private File getLogDir() { - File logDir = LOG_DIR.get(); - if (logDir == null) { - logDir = LogUtil.getLogManagerLogDir(); - LOG.info("From logging properties, parsed log directory " + logDir.getAbsolutePath()); - } - return logDir; - } - - public static final class ExportLogDir implements Command { - private final File logDir; - - @Inject ExportLogDir(@Named(LogPrinter.LOG_DIR_KEY) final File logDir) { - this.logDir = Preconditions.checkNotNull(logDir); - } - - @Override public void execute() { - Stats.exportStatic(new StatImpl<String>("logging_dir") { - @Override public String read() { - return logDir.getAbsolutePath(); - } - }); - } - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/application/modules/StatsExportModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/application/modules/StatsExportModule.java b/commons/src/main/java/com/twitter/common/application/modules/StatsExportModule.java deleted file mode 100644 index 10fe24a..0000000 --- a/commons/src/main/java/com/twitter/common/application/modules/StatsExportModule.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * 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 com.twitter.common.application.modules; - -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.google.inject.AbstractModule; -import com.google.inject.Inject; -import com.google.inject.Key; -import com.google.inject.TypeLiteral; - -import com.twitter.common.application.ShutdownRegistry; -import com.twitter.common.args.Arg; -import com.twitter.common.args.CmdLine; -import com.twitter.common.base.Closure; -import com.twitter.common.base.Command; -import com.twitter.common.quantity.Amount; -import com.twitter.common.quantity.Time; -import com.twitter.common.stats.NumericStatExporter; - -/** - * Module to enable periodic exporting of registered stats to an external service. - * - * This modules supports a single command line argument, {@code stat_export_interval}, which - * controls the export interval (defaulting to 1 minute). - * - * Bindings required by this module: - * <ul> - * <li>{@code @ShutdownStage ShutdownRegistry} - Shutdown action registry. - * </ul> - * - * @author William Farner - */ -public class StatsExportModule extends AbstractModule { - - @CmdLine(name = "stat_export_interval", - help = "Amount of time to wait between stat exports.") - private static final Arg<Amount<Long, Time>> EXPORT_INTERVAL = - Arg.create(Amount.of(1L, Time.MINUTES)); - - @Override - protected void configure() { - requireBinding(Key.get(new TypeLiteral<Closure<Map<String, ? extends Number>>>() { })); - LifecycleModule.bindStartupAction(binder(), StartCuckooExporter.class); - } - - public static final class StartCuckooExporter implements Command { - - private final Closure<Map<String, ? extends Number>> statSink; - private final ShutdownRegistry shutdownRegistry; - - @Inject StartCuckooExporter( - Closure<Map<String, ? extends Number>> statSink, - ShutdownRegistry shutdownRegistry) { - - this.statSink = Preconditions.checkNotNull(statSink); - this.shutdownRegistry = Preconditions.checkNotNull(shutdownRegistry); - } - - @Override public void execute() { - ThreadFactory threadFactory = - new ThreadFactoryBuilder().setNameFormat("CuckooExporter-%d").setDaemon(true).build(); - - final NumericStatExporter exporter = new NumericStatExporter(statSink, - Executors.newScheduledThreadPool(1, threadFactory), EXPORT_INTERVAL.get()); - - exporter.start(shutdownRegistry); - } - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/application/modules/StatsModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/application/modules/StatsModule.java b/commons/src/main/java/com/twitter/common/application/modules/StatsModule.java deleted file mode 100644 index ba7d206..0000000 --- a/commons/src/main/java/com/twitter/common/application/modules/StatsModule.java +++ /dev/null @@ -1,146 +0,0 @@ -/** - * 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 com.twitter.common.application.modules; - -import java.util.Properties; -import java.util.logging.Logger; - -import com.google.common.base.Supplier; -import com.google.common.primitives.Longs; -import com.google.inject.AbstractModule; -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.google.inject.TypeLiteral; -import com.google.inject.name.Names; - -import com.twitter.common.application.ShutdownRegistry; -import com.twitter.common.args.Arg; -import com.twitter.common.args.CmdLine; -import com.twitter.common.base.Command; -import com.twitter.common.quantity.Amount; -import com.twitter.common.quantity.Time; -import com.twitter.common.stats.JvmStats; -import com.twitter.common.stats.Stat; -import com.twitter.common.stats.StatImpl; -import com.twitter.common.stats.StatRegistry; -import com.twitter.common.stats.Stats; -import com.twitter.common.stats.TimeSeriesRepository; -import com.twitter.common.stats.TimeSeriesRepositoryImpl; -import com.twitter.common.util.BuildInfo; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Binding module for injections related to the in-process stats system. - * - * This modules supports two command line arguments: - * <ul> - * <li>{@code stat_sampling_interval} - Statistic value sampling interval. - * <li>{@code stat_retention_period} - Time for a stat to be retained in memory before expring. - * </ul> - * - * Bindings required by this module: - * <ul> - * <li>{@code ShutdownRegistry} - Shutdown hook registry. - * <li>{@code BuildInfo} - Build information for the application. - * </ul> - * - * @author William Farner - */ -public class StatsModule extends AbstractModule { - - @CmdLine(name = "stat_sampling_interval", help = "Statistic value sampling interval.") - private static final Arg<Amount<Long, Time>> SAMPLING_INTERVAL = - Arg.create(Amount.of(1L, Time.SECONDS)); - - @CmdLine(name = "stat_retention_period", - help = "Time for a stat to be retained in memory before expiring.") - private static final Arg<Amount<Long, Time>> RETENTION_PERIOD = - Arg.create(Amount.of(1L, Time.HOURS)); - - public static Amount<Long, Time> getSamplingInterval() { - return SAMPLING_INTERVAL.get(); - } - - @Override - protected void configure() { - requireBinding(ShutdownRegistry.class); - requireBinding(BuildInfo.class); - - // Bindings for TimeSeriesRepositoryImpl. - bind(StatRegistry.class).toInstance(Stats.STAT_REGISTRY); - bind(new TypeLiteral<Amount<Long, Time>>() { }) - .annotatedWith(Names.named(TimeSeriesRepositoryImpl.SAMPLE_RETENTION_PERIOD)) - .toInstance(RETENTION_PERIOD.get()); - bind(new TypeLiteral<Amount<Long, Time>>() { }) - .annotatedWith(Names.named(TimeSeriesRepositoryImpl.SAMPLE_PERIOD)) - .toInstance(SAMPLING_INTERVAL.get()); - bind(TimeSeriesRepository.class).to(TimeSeriesRepositoryImpl.class).in(Singleton.class); - - bind(new TypeLiteral<Supplier<Iterable<Stat<?>>>>() { }).toInstance( - new Supplier<Iterable<Stat<?>>>() { - @Override public Iterable<Stat<?>> get() { - return Stats.getVariables(); - } - } - ); - - LifecycleModule.bindStartupAction(binder(), StartStatPoller.class); - } - - public static final class StartStatPoller implements Command { - private static final Logger LOG = Logger.getLogger(StartStatPoller.class.getName()); - private final ShutdownRegistry shutdownRegistry; - private final BuildInfo buildInfo; - private final TimeSeriesRepository timeSeriesRepository; - - @Inject StartStatPoller( - ShutdownRegistry shutdownRegistry, - BuildInfo buildInfo, - TimeSeriesRepository timeSeriesRepository) { - - this.shutdownRegistry = checkNotNull(shutdownRegistry); - this.buildInfo = checkNotNull(buildInfo); - this.timeSeriesRepository = checkNotNull(timeSeriesRepository); - } - - @Override public void execute() { - Properties properties = buildInfo.getProperties(); - LOG.info("Build information: " + properties); - for (String name : properties.stringPropertyNames()) { - final String stringValue = properties.getProperty(name); - if (stringValue == null) { - continue; - } - final Long longValue = Longs.tryParse(stringValue); - if (longValue != null) { - Stats.exportStatic(new StatImpl<Long>(Stats.normalizeName(name)) { - @Override public Long read() { - return longValue; - } - }); - } else { - Stats.exportString(new StatImpl<String>(Stats.normalizeName(name)) { - @Override public String read() { - return stringValue; - } - }); - } - } - - JvmStats.export(); - timeSeriesRepository.start(shutdownRegistry); - } - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/application/modules/ThriftModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/application/modules/ThriftModule.java b/commons/src/main/java/com/twitter/common/application/modules/ThriftModule.java deleted file mode 100644 index aeb5bed..0000000 --- a/commons/src/main/java/com/twitter/common/application/modules/ThriftModule.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 com.twitter.common.application.modules; - -import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import com.google.inject.name.Names; - -import com.twitter.common.application.http.Registration; -import com.twitter.common.net.http.handlers.ThriftServlet; -import com.twitter.common.net.monitoring.TrafficMonitor; - -/** - * Binding module for thrift traffic monitor servlets, to ensure an empty set is available for - * the thrift traffic monitor servlet. - * - * @author William Farner - */ -public class ThriftModule extends AbstractModule { - @Override - protected void configure() { - // Make sure that there is at least an empty set bound to client andserver monitors. - Multibinder.newSetBinder(binder(), TrafficMonitor.class, - Names.named(ThriftServlet.THRIFT_CLIENT_MONITORS)); - Multibinder.newSetBinder(binder(), TrafficMonitor.class, - Names.named(ThriftServlet.THRIFT_SERVER_MONITORS)); - - Registration.registerServlet(binder(), "/thrift", ThriftServlet.class, false); - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/args/ArgFilters.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/args/ArgFilters.java b/commons/src/main/java/com/twitter/common/args/ArgFilters.java deleted file mode 100644 index cd11332..0000000 --- a/commons/src/main/java/com/twitter/common/args/ArgFilters.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * 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 com.twitter.common.args; - -import java.lang.reflect.Field; -import java.util.Set; - -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableSet; - -import com.twitter.common.base.MorePreconditions; - -/** - * Utilities for generating {@literal @CmdLine} {@link Arg} filters suitable for use with - * {@link com.twitter.common.args.ArgScanner#parse(Predicate, Iterable)}. These filters assume the - * fields parsed will all be annotated with {@link CmdLine}. - * - * @author John Sirois - */ -public final class ArgFilters { - - /** - * A filter that selects all {@literal @CmdLine} {@link Arg}s found on the classpath. - */ - public static final Predicate<Field> SELECT_ALL = Predicates.alwaysTrue(); - - private ArgFilters() { - // utility - } - - /** - * Creates a filter that selects all {@literal @CmdLine} {@link Arg}s found in classes that are - * members of the given package. Note that this will not select subpackages. - * - * @param pkg The exact package of classes whose command line args will be selected. - * @return A filter that selects only command line args declared in classes that are members of - * the given {@code pkg}. - */ - public static Predicate<Field> selectPackage(final Package pkg) { - Preconditions.checkNotNull(pkg); - return new Predicate<Field>() { - @Override public boolean apply(Field field) { - return field.getDeclaringClass().getPackage().equals(pkg); - } - }; - } - - /** - * Creates a filter that selects all {@literal @CmdLine} {@link Arg}s found in classes that are - * members of the given package or its sub-packages. - * - * @param pkg The ancestor package of classes whose command line args will be selected. - * @return A filter that selects only command line args declared in classes that are members of - * the given {@code pkg} or its sub-packages. - */ - public static Predicate<Field> selectAllPackagesUnderHere(final Package pkg) { - Preconditions.checkNotNull(pkg); - final String prefix = pkg.getName() + '.'; - return Predicates.or(selectPackage(pkg), new Predicate<Field>() { - @Override public boolean apply(Field field) { - return field.getDeclaringClass().getPackage().getName().startsWith(prefix); - } - }); - } - - /** - * Creates a filter that selects all {@literal @CmdLine} {@link Arg}s found in the given class. - * - * @param clazz The class whose command line args will be selected. - * @return A filter that selects only command line args declared in the given {@code clazz}. - */ - public static Predicate<Field> selectClass(final Class<?> clazz) { - Preconditions.checkNotNull(clazz); - return new Predicate<Field>() { - @Override public boolean apply(Field field) { - return field.getDeclaringClass().equals(clazz); - } - }; - } - - /** - * Creates a filter that selects all {@literal @CmdLine} {@link Arg}s found in the given classes. - * - * @param cls The classes whose command line args will be selected. - * @return A filter that selects only command line args declared in the given classes. - */ - public static Predicate<Field> selectClasses(final Class<?> ... cls) { - Preconditions.checkNotNull(cls); - final Set<Class<?>> listOfClasses = ImmutableSet.copyOf(cls); - return new Predicate<Field>() { - @Override public boolean apply(Field field) { - return listOfClasses.contains(field.getDeclaringClass()); - } - }; - } - - /** - * Creates a filter that selects a single {@literal @CmdLine} {@link Arg}. - * - * @param clazz The class that declares the command line arg to be selected. - * @param name The {@link com.twitter.common.args.CmdLine#name()} of the arg to select. - * @return A filter that selects a single specified command line arg. - */ - public static Predicate<Field> selectCmdLineArg(Class<?> clazz, final String name) { - MorePreconditions.checkNotBlank(name); - return Predicates.and(selectClass(clazz), new Predicate<Field>() { - @Override public boolean apply(Field field) { - return field.getAnnotation(CmdLine.class).name().equals(name); - } - }); - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/args/ArgScanner.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/args/ArgScanner.java b/commons/src/main/java/com/twitter/common/args/ArgScanner.java deleted file mode 100644 index 6b50ed9..0000000 --- a/commons/src/main/java/com/twitter/common/args/ArgScanner.java +++ /dev/null @@ -1,560 +0,0 @@ -/** - * 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 com.twitter.common.args; - -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; -import com.google.common.collect.Sets; - -import com.twitter.common.args.Args.ArgsInfo; -import com.twitter.common.args.apt.Configuration; -import com.twitter.common.collections.Pair; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Argument scanning, parsing, and validating system. This class is designed recursively scan a - * package for declared arguments, parse the values based on the declared type, and validate against - * any constraints that the arugment is decorated with. - * - * The supported argument formats are: - * -arg_name=arg_value - * -arg_name arg_value - * Where {@code arg_value} may be single or double-quoted if desired or necessary to prevent - * splitting by the terminal application. - * - * A special format for boolean arguments is also supported. The following syntaxes all set the - * {@code bool_arg} to {@code true}: - * -bool_arg - * -bool_arg=true - * -no_bool_arg=false (double negation) - * - * Likewise, the following would set {@code bool_arg} to {@code false}: - * -no_bool_arg - * -bool_arg=false - * -no_bool_arg=true (negation) - * - * As with the general argument format, spaces may be used in place of equals for boolean argument - * assignment. - * - * TODO(William Farner): Make default verifier and parser classes package-private and in this - * package. - */ -public final class ArgScanner { - - private static final Function<OptionInfo<?>, String> GET_OPTION_INFO_NAME = - new Function<OptionInfo<?>, String>() { - @Override public String apply(OptionInfo<?> optionInfo) { - return optionInfo.getName(); - } - }; - - public static final Ordering<OptionInfo<?>> ORDER_BY_NAME = - Ordering.natural().onResultOf(GET_OPTION_INFO_NAME); - - private static final Function<String, String> ARG_NAME_TO_FLAG = new Function<String, String>() { - @Override public String apply(String argName) { - return "-" + argName; - } - }; - - private static final Predicate<OptionInfo<?>> IS_BOOLEAN = - new Predicate<OptionInfo<?>>() { - @Override public boolean apply(OptionInfo<?> optionInfo) { - return optionInfo.isBoolean(); - } - }; - - // Regular expression to identify a possible dangling assignment. - // A dangling assignment occurs in two cases: - // - The command line used spaces between arg names and values, causing the name and value to - // end up in different command line arg array elements. - // - The command line is using the short form for a boolean argument, - // such as -use_feature, or -no_use_feature. - private static final String DANGLING_ASSIGNMENT_RE = - String.format("^-%s", OptionInfo.ARG_NAME_RE); - private static final Pattern DANGLING_ASSIGNMENT_PATTERN = - Pattern.compile(DANGLING_ASSIGNMENT_RE); - - // Pattern to identify a full assignment, which would be disassociated from a preceding dangling - // assignment. - private static final Pattern ASSIGNMENT_PATTERN = - Pattern.compile(String.format("%s=.+", DANGLING_ASSIGNMENT_RE)); - - /** - * Extracts the name from an @OptionInfo. - */ - private static final Function<OptionInfo<?>, String> GET_OPTION_INFO_NEGATED_NAME = - new Function<OptionInfo<?>, String>() { - @Override public String apply(OptionInfo<?> optionInfo) { - return optionInfo.getNegatedName(); - } - }; - - /** - * Gets the canonical name for an @Arg, based on the class containing the field it annotates. - */ - private static final Function<OptionInfo<?>, String> GET_CANONICAL_ARG_NAME = - new Function<OptionInfo<?>, String>() { - @Override public String apply(OptionInfo<?> optionInfo) { - return optionInfo.getCanonicalName(); - } - }; - - /** - * Gets the canonical negated name for an @Arg. - */ - private static final Function<OptionInfo<?>, String> GET_CANONICAL_NEGATED_ARG_NAME = - new Function<OptionInfo<?>, String>() { - @Override public String apply(OptionInfo<?> optionInfo) { - return optionInfo.getCanonicalNegatedName(); - } - }; - - private static final Logger LOG = Logger.getLogger(ArgScanner.class.getName()); - - // Pattern for the required argument format. - private static final Pattern ARG_PATTERN = - Pattern.compile(String.format("-(%s)(?:(?:=| +)(.*))?", OptionInfo.ARG_NAME_RE)); - - private static final Pattern QUOTE_PATTERN = Pattern.compile("(['\"])([^\\\1]*)\\1"); - - private final PrintStream out; - - /** - * Equivalent to calling {@link #ArgScanner(PrintStream)} passing {@link System#out}. - */ - public ArgScanner() { - this(System.out); - } - - /** - * Creates a new ArgScanner that prints help on arg parse failure or when help is requested to - * {@code out} or else prints applied argument information to {@code out} when parsing is - * successful. - * - * @param out An output stream to write help and parsed argument info to. - */ - public ArgScanner(PrintStream out) { - this.out = Preconditions.checkNotNull(out); - } - - /** - * Applies the provided argument values to all {@literal @CmdLine} {@code Arg} fields discovered - * on the classpath. - * - * @param args Argument values to map, parse, validate, and apply. - * @return {@code true} if the given {@code args} were successfully applied to their corresponding - * {@link Arg} fields. - * @throws ArgScanException if there was a problem loading {@literal @CmdLine} argument - * definitions - * @throws IllegalArgumentException If the arguments provided are invalid based on the declared - * arguments found. - */ - public boolean parse(Iterable<String> args) { - return parse(ArgFilters.SELECT_ALL, ImmutableList.copyOf(args)); - } - - /** - * Applies the provided argument values to any {@literal @CmdLine} or {@literal @Positional} - * {@code Arg} fields discovered on the classpath and accepted by the given {@code filter}. - * - * @param filter A predicate that selects or rejects scanned {@literal @CmdLine} fields for - * argument application. - * @param args Argument values to map, parse, validate, and apply. - * @return {@code true} if the given {@code args} were successfully applied to their corresponding - * {@link Arg} fields. - * @throws ArgScanException if there was a problem loading {@literal @CmdLine} argument - * definitions - * @throws IllegalArgumentException If the arguments provided are invalid based on the declared - * arguments found. - */ - public boolean parse(Predicate<Field> filter, Iterable<String> args) { - Preconditions.checkNotNull(filter); - ImmutableList<String> arguments = ImmutableList.copyOf(args); - - Configuration configuration = load(); - ArgsInfo argsInfo = Args.fromConfiguration(configuration, filter); - return parse(argsInfo, arguments); - } - - /** - * Parse command line arguments given a {@link ArgsInfo} - * - * @param argsInfo A description of any optional and positional arguments to parse. - * @param args Argument values to map, parse, validate, and apply. - * @return {@code true} if the given {@code args} were successfully applied to their corresponding - * {@link Arg} fields. - * @throws ArgScanException if there was a problem loading {@literal @CmdLine} argument - * definitions - * @throws IllegalArgumentException If the arguments provided are invalid based on the declared - * arguments found. - */ - public boolean parse(ArgsInfo argsInfo, Iterable<String> args) { - Preconditions.checkNotNull(argsInfo); - ImmutableList<String> arguments = ImmutableList.copyOf(args); - - ParserOracle parserOracle = Parsers.fromConfiguration(argsInfo.getConfiguration()); - Verifiers verifiers = Verifiers.fromConfiguration(argsInfo.getConfiguration()); - Pair<ImmutableMap<String, String>, List<String>> results = mapArguments(arguments); - return process(parserOracle, verifiers, argsInfo, results.getFirst(), results.getSecond()); - } - - private Configuration load() { - try { - return Configuration.load(); - } catch (IOException e) { - throw new ArgScanException(e); - } - } - - @VisibleForTesting static List<String> joinKeysToValues(Iterable<String> args) { - List<String> joinedArgs = Lists.newArrayList(); - String unmappedKey = null; - for (String arg : args) { - if (unmappedKey == null) { - if (DANGLING_ASSIGNMENT_PATTERN.matcher(arg).matches()) { - // Beginning of a possible dangling assignment. - unmappedKey = arg; - } else { - joinedArgs.add(arg); - } - } else { - if (ASSIGNMENT_PATTERN.matcher(arg).matches()) { - // Full assignment, disassociate from dangling assignment. - joinedArgs.add(unmappedKey); - joinedArgs.add(arg); - unmappedKey = null; - } else if (DANGLING_ASSIGNMENT_PATTERN.matcher(arg).find()) { - // Another dangling assignment, this could be two sequential boolean args. - joinedArgs.add(unmappedKey); - unmappedKey = arg; - } else { - // Join the dangling key with its value. - joinedArgs.add(unmappedKey + "=" + arg); - unmappedKey = null; - } - } - } - - if (unmappedKey != null) { - joinedArgs.add(unmappedKey); - } - - return joinedArgs; - } - - private static String stripQuotes(String str) { - Matcher matcher = QUOTE_PATTERN.matcher(str); - return matcher.matches() ? matcher.group(2) : str; - } - - /** - * Scans through args, mapping keys to values even if the arg values are 'dangling' and reside - * in different array entries than the respective keys. - * - * @param args Arguments to build into a map. - * @return A map from argument key (arg name) to value paired with a list of any leftover - * positional arguments. - */ - private static Pair<ImmutableMap<String, String>, List<String>> mapArguments( - Iterable<String> args) { - - ImmutableMap.Builder<String, String> argMap = ImmutableMap.builder(); - List<String> positionalArgs = Lists.newArrayList(); - for (String arg : joinKeysToValues(args)) { - if (!arg.startsWith("-")) { - positionalArgs.add(arg); - } else { - Matcher matcher = ARG_PATTERN.matcher(arg); - checkArgument(matcher.matches(), - String.format("Argument '%s' does not match required format -arg_name=arg_value", arg)); - - String rawValue = matcher.group(2); - // An empty string denotes that the argument was passed with no value. - rawValue = rawValue == null ? "" : stripQuotes(rawValue); - argMap.put(matcher.group(1), rawValue); - } - } - - return Pair.of(argMap.build(), positionalArgs); - } - - private static <T> Set<T> dropCollisions(Iterable<T> input) { - Set<T> copy = Sets.newHashSet(); - Set<T> collisions = Sets.newHashSet(); - for (T entry : input) { - if (!copy.add(entry)) { - collisions.add(entry); - } - } - - copy.removeAll(collisions); - return copy; - } - - private static Set<String> getNoCollisions(Iterable<? extends OptionInfo<?>> optionInfos) { - Iterable<String> argShortNames = Iterables.transform(optionInfos, GET_OPTION_INFO_NAME); - Iterable<String> argShortNegNames = - Iterables.transform(Iterables.filter(optionInfos, IS_BOOLEAN), - GET_OPTION_INFO_NEGATED_NAME); - Iterable<String> argAllShortNames = Iterables.concat(argShortNames, argShortNegNames); - Set<String> argAllShortNamesNoCollisions = dropCollisions(argAllShortNames); - Set<String> collisionsDropped = Sets.difference(ImmutableSet.copyOf(argAllShortNames), - argAllShortNamesNoCollisions); - if (!collisionsDropped.isEmpty()) { - LOG.warning("Found argument name collisions, args must be referenced by canonical names: " - + collisionsDropped); - } - return argAllShortNamesNoCollisions; - } - - /** - * Applies argument values to fields based on their annotations. - * - * @param parserOracle ParserOracle available to parse raw args with. - * @param verifiers Verifiers available to verify argument constraints with. - * @param argsInfo Fields to apply argument values to. - * @param args Unparsed argument values. - * @param positionalArgs The unparsed positional arguments. - * @return {@code true} if the given {@code args} were successfully applied to their - * corresponding {@link com.twitter.common.args.Arg} fields. - */ - private boolean process(final ParserOracle parserOracle, - Verifiers verifiers, - ArgsInfo argsInfo, - Map<String, String> args, - List<String> positionalArgs) { - - if (!Sets.intersection(args.keySet(), ArgumentInfo.HELP_ARGS).isEmpty()) { - printHelp(verifiers, argsInfo); - return false; - } - - Optional<? extends PositionalInfo<?>> positionalInfoOptional = argsInfo.getPositionalInfo(); - checkArgument(positionalInfoOptional.isPresent() || positionalArgs.isEmpty(), - "Positional arguments have been supplied but there is no Arg annotated to received them."); - - Iterable<? extends OptionInfo<?>> optionInfos = argsInfo.getOptionInfos(); - - final Set<String> argsFailedToParse = Sets.newHashSet(); - final Set<String> argsConstraintsFailed = Sets.newHashSet(); - - Set<String> argAllShortNamesNoCollisions = getNoCollisions(optionInfos); - - final Map<String, OptionInfo<?>> argsByName = - ImmutableMap.<String, OptionInfo<?>>builder() - // Map by short arg name -> arg def. - .putAll(Maps.uniqueIndex(Iterables.filter(optionInfos, - Predicates.compose(Predicates.in(argAllShortNamesNoCollisions), GET_OPTION_INFO_NAME)), - GET_OPTION_INFO_NAME)) - // Map by canonical arg name -> arg def. - .putAll(Maps.uniqueIndex(optionInfos, GET_CANONICAL_ARG_NAME)) - // Map by negated short arg name (for booleans) - .putAll(Maps.uniqueIndex( - Iterables.filter(Iterables.filter(optionInfos, IS_BOOLEAN), - Predicates.compose(Predicates.in(argAllShortNamesNoCollisions), - GET_OPTION_INFO_NEGATED_NAME)), - GET_OPTION_INFO_NEGATED_NAME)) - // Map by negated canonical arg name (for booleans) - .putAll(Maps.uniqueIndex(Iterables.filter(optionInfos, IS_BOOLEAN), - GET_CANONICAL_NEGATED_ARG_NAME)) - .build(); - - // TODO(William Farner): Make sure to disallow duplicate arg specification by short and - // canonical names. - - // TODO(William Farner): Support non-atomic argument constraints. @OnlyIfSet, @OnlyIfNotSet, - // @ExclusiveOf to define inter-argument constraints. - - Set<String> recognizedArgs = Sets.intersection(argsByName.keySet(), args.keySet()); - - for (String argName : recognizedArgs) { - String argValue = args.get(argName); - OptionInfo<?> optionInfo = argsByName.get(argName); - - try { - optionInfo.load(parserOracle, argName, argValue); - } catch (IllegalArgumentException e) { - argsFailedToParse.add(argName + " - " + e.getMessage()); - } - } - - if (positionalInfoOptional.isPresent()) { - PositionalInfo<?> positionalInfo = positionalInfoOptional.get(); - positionalInfo.load(parserOracle, positionalArgs); - } - - Set<String> commandLineArgumentInfos = Sets.newTreeSet(); - - Iterable<? extends ArgumentInfo<?>> allArguments = argsInfo.getOptionInfos(); - - if (positionalInfoOptional.isPresent()) { - PositionalInfo<?> positionalInfo = positionalInfoOptional.get(); - allArguments = Iterables.concat(optionInfos, ImmutableList.of(positionalInfo)); - } - - for (ArgumentInfo<?> anArgumentInfo : allArguments) { - Arg<?> arg = anArgumentInfo.getArg(); - - commandLineArgumentInfos.add(String.format("%s (%s): %s", - anArgumentInfo.getName(), anArgumentInfo.getCanonicalName(), - arg.uncheckedGet())); - - try { - anArgumentInfo.verify(verifiers); - } catch (IllegalArgumentException e) { - argsConstraintsFailed.add(anArgumentInfo.getName() + " - " + e.getMessage()); - } - } - - ImmutableMultimap<String, String> warningMessages = - ImmutableMultimap.<String, String>builder() - .putAll("Unrecognized arguments", Sets.difference(args.keySet(), argsByName.keySet())) - .putAll("Failed to parse", argsFailedToParse) - .putAll("Value did not meet constraints", argsConstraintsFailed) - .build(); - - if (!warningMessages.isEmpty()) { - printHelp(verifiers, argsInfo); - StringBuilder sb = new StringBuilder(); - for (Map.Entry<String, Collection<String>> warnings : warningMessages.asMap().entrySet()) { - sb.append(warnings.getKey()).append(":\n\t").append(Joiner.on("\n\t") - .join(warnings.getValue())).append("\n"); - } - throw new IllegalArgumentException(sb.toString()); - } - - LOG.info("-------------------------------------------------------------------------"); - LOG.info("Command line argument values"); - for (String commandLineArgumentInfo : commandLineArgumentInfos) { - LOG.info(commandLineArgumentInfo); - } - LOG.info("-------------------------------------------------------------------------"); - return true; - } - - private void printHelp(Verifiers verifiers, ArgsInfo argsInfo) { - ImmutableList.Builder<String> requiredHelps = ImmutableList.builder(); - ImmutableList.Builder<String> optionalHelps = ImmutableList.builder(); - Optional<String> firstArgFileArgumentName = Optional.absent(); - for (OptionInfo<?> optionInfo - : ORDER_BY_NAME.immutableSortedCopy(argsInfo.getOptionInfos())) { - Arg<?> arg = optionInfo.getArg(); - Object defaultValue = arg.uncheckedGet(); - ImmutableList<String> constraints = optionInfo.collectConstraints(verifiers); - String help = formatHelp(optionInfo, constraints, defaultValue); - if (!arg.hasDefault()) { - requiredHelps.add(help); - } else { - optionalHelps.add(help); - } - if (optionInfo.argFile() && !firstArgFileArgumentName.isPresent()) { - firstArgFileArgumentName = Optional.of(optionInfo.getName()); - } - } - - infoLog("-------------------------------------------------------------------------"); - infoLog(String.format("%s to print this help message", - Joiner.on(" or ").join(Iterables.transform(ArgumentInfo.HELP_ARGS, ARG_NAME_TO_FLAG)))); - Optional<? extends PositionalInfo<?>> positionalInfoOptional = argsInfo.getPositionalInfo(); - if (positionalInfoOptional.isPresent()) { - infoLog("\nPositional args:"); - PositionalInfo<?> positionalInfo = positionalInfoOptional.get(); - Arg<?> arg = positionalInfo.getArg(); - Object defaultValue = arg.uncheckedGet(); - ImmutableList<String> constraints = positionalInfo.collectConstraints(verifiers); - infoLog(String.format("%s%s\n\t%s\n\t(%s)", - defaultValue != null ? "default " + defaultValue : "", - Iterables.isEmpty(constraints) - ? "" - : " [" + Joiner.on(", ").join(constraints) + "]", - positionalInfo.getHelp(), - positionalInfo.getCanonicalName())); - // TODO: https://github.com/twitter/commons/issues/353, in the future we may - // want to support @argfile format for positional arguments. We should check - // to update firstArgFileArgumentName for them as well. - } - ImmutableList<String> required = requiredHelps.build(); - if (!required.isEmpty()) { - infoLog("\nRequired flags:"); // yes - this should actually throw! - infoLog(Joiner.on('\n').join(required)); - } - ImmutableList<String> optional = optionalHelps.build(); - if (!optional.isEmpty()) { - infoLog("\nOptional flags:"); - infoLog(Joiner.on('\n').join(optional)); - } - if (firstArgFileArgumentName.isPresent()) { - infoLog(String.format("\n" - + "For arguments that support @argfile format: @argfile is a text file that contains " - + "cmdline argument values. For example: -%s=@/tmp/%s_value.txt. The format " - + "of the argfile content should be exactly the same as it would be specified on the " - + "cmdline.", firstArgFileArgumentName.get(), firstArgFileArgumentName.get())); - } - infoLog("-------------------------------------------------------------------------"); - } - - private String formatHelp(ArgumentInfo<?> argumentInfo, Iterable<String> constraints, - @Nullable Object defaultValue) { - - return String.format("-%s%s%s\n\t%s\n\t(%s)", - argumentInfo.getName(), - defaultValue != null ? "=" + defaultValue : "", - Iterables.isEmpty(constraints) - ? "" - : " [" + Joiner.on(", ").join(constraints) + "]", - argumentInfo.getHelp(), - argumentInfo.getCanonicalName()); - } - - private void infoLog(String msg) { - out.println(msg); - } - - /** - * Indicates a problem scanning {@literal @CmdLine} arg definitions. - */ - public static class ArgScanException extends RuntimeException { - public ArgScanException(Throwable cause) { - super(cause); - } - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/args/Args.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/args/Args.java b/commons/src/main/java/com/twitter/common/args/Args.java deleted file mode 100644 index 9fe546d..0000000 --- a/commons/src/main/java/com/twitter/common/args/Args.java +++ /dev/null @@ -1,224 +0,0 @@ -/** - * 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 com.twitter.common.args; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.logging.Logger; - -import javax.annotation.Nullable; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; - -import com.twitter.common.args.apt.Configuration; -import com.twitter.common.args.apt.Configuration.ArgInfo; - -import static com.twitter.common.args.apt.Configuration.ConfigurationException; - -/** - * Utility that can load static {@literal @CmdLine} and {@literal @Positional} arg field info from - * a configuration database or from explicitly listed containing classes or objects. - */ -public final class Args { - @VisibleForTesting - static final Function<ArgInfo, Optional<Field>> TO_FIELD = - new Function<ArgInfo, Optional<Field>>() { - @Override public Optional<Field> apply(ArgInfo info) { - try { - return Optional.of(Class.forName(info.className).getDeclaredField(info.fieldName)); - } catch (NoSuchFieldException e) { - throw new ConfigurationException(e); - } catch (ClassNotFoundException e) { - throw new ConfigurationException(e); - } catch (NoClassDefFoundError e) { - // A compilation had this class available at the time the ArgInfo was deposited, but - // the classes have been re-bundled with some subset including the class this ArgInfo - // points to no longer available. If the re-bundling is correct, then the arg truly is - // not needed. - LOG.fine(String.format("Not on current classpath, skipping %s", info)); - return Optional.absent(); - } - } - }; - - private static final Logger LOG = Logger.getLogger(Args.class.getName()); - - private static final Function<Field, OptionInfo<?>> TO_OPTION_INFO = - new Function<Field, OptionInfo<?>>() { - @Override public OptionInfo<?> apply(Field field) { - @Nullable CmdLine cmdLine = field.getAnnotation(CmdLine.class); - if (cmdLine == null) { - throw new ConfigurationException("No @CmdLine Arg annotation for field " + field); - } - return OptionInfo.createFromField(field); - } - }; - - private static final Function<Field, PositionalInfo<?>> TO_POSITIONAL_INFO = - new Function<Field, PositionalInfo<?>>() { - @Override public PositionalInfo<?> apply(Field field) { - @Nullable Positional positional = field.getAnnotation(Positional.class); - if (positional == null) { - throw new ConfigurationException("No @Positional Arg annotation for field " + field); - } - return PositionalInfo.createFromField(field); - } - }; - - /** - * An opaque container for all the positional and optional {@link Arg} metadata in-play for a - * command line parse. - */ - public static final class ArgsInfo { - private final Configuration configuration; - private final Optional<? extends PositionalInfo<?>> positionalInfo; - private final ImmutableList<? extends OptionInfo<?>> optionInfos; - - ArgsInfo(Configuration configuration, - Optional<? extends PositionalInfo<?>> positionalInfo, - Iterable<? extends OptionInfo<?>> optionInfos) { - - this.configuration = Preconditions.checkNotNull(configuration); - this.positionalInfo = Preconditions.checkNotNull(positionalInfo); - this.optionInfos = ImmutableList.copyOf(optionInfos); - } - - Configuration getConfiguration() { - return configuration; - } - - Optional<? extends PositionalInfo<?>> getPositionalInfo() { - return positionalInfo; - } - - ImmutableList<? extends OptionInfo<?>> getOptionInfos() { - return optionInfos; - } - } - - /** - * Hydrates configured {@literal @CmdLine} arg fields and selects a desired set with the supplied - * {@code filter}. - * - * @param configuration The configuration to find candidate {@literal @CmdLine} arg fields in. - * @param filter A predicate to select fields with. - * @return The desired hydrated {@literal @CmdLine} arg fields and optional {@literal @Positional} - * arg field. - */ - static ArgsInfo fromConfiguration(Configuration configuration, Predicate<Field> filter) { - ImmutableSet<Field> positionalFields = - ImmutableSet.copyOf(filterFields(configuration.positionalInfo(), filter)); - - if (positionalFields.size() > 1) { - throw new IllegalArgumentException( - String.format("Found %d fields marked for @Positional Args after applying filter - " - + "only 1 is allowed:\n\t%s", positionalFields.size(), - Joiner.on("\n\t").join(positionalFields))); - } - - Optional<? extends PositionalInfo<?>> positionalInfo = - Optional.fromNullable( - Iterables.getOnlyElement( - Iterables.transform(positionalFields, TO_POSITIONAL_INFO), null)); - - Iterable<? extends OptionInfo<?>> optionInfos = Iterables.transform( - filterFields(configuration.optionInfo(), filter), TO_OPTION_INFO); - - return new ArgsInfo(configuration, positionalInfo, optionInfos); - } - - private static Iterable<Field> filterFields(Iterable<ArgInfo> infos, Predicate<Field> filter) { - return Iterables.filter( - Optional.presentInstances(Iterables.transform(infos, TO_FIELD)), - filter); - } - - /** - * Equivalent to calling {@code from(Predicates.alwaysTrue(), Arrays.asList(sources)}. - */ - public static ArgsInfo from(Object... sources) throws IOException { - return from(ImmutableList.copyOf(sources)); - } - - /** - * Equivalent to calling {@code from(filter, Arrays.asList(sources)}. - */ - public static ArgsInfo from(Predicate<Field> filter, Object... sources) throws IOException { - return from(filter, ImmutableList.copyOf(sources)); - } - - /** - * Equivalent to calling {@code from(Predicates.alwaysTrue(), sources}. - */ - public static ArgsInfo from(Iterable<?> sources) throws IOException { - return from(Predicates.<Field>alwaysTrue(), sources); - } - - /** - * Loads arg info from the given sources in addition to the default compile-time configuration. - * - * @param filter A predicate to select fields with. - * @param sources Classes or object instances to scan for {@link Arg} fields. - * @return The args info describing all discovered {@link Arg args}. - * @throws IOException If there was a problem loading the default Args configuration. - */ - public static ArgsInfo from(Predicate<Field> filter, Iterable<?> sources) throws IOException { - Preconditions.checkNotNull(filter); - Preconditions.checkNotNull(sources); - - Configuration configuration = Configuration.load(); - ArgsInfo staticInfo = Args.fromConfiguration(configuration, filter); - - final ImmutableSet.Builder<PositionalInfo<?>> positionalInfos = - ImmutableSet.<PositionalInfo<?>>builder().addAll(staticInfo.getPositionalInfo().asSet()); - final ImmutableSet.Builder<OptionInfo<?>> optionInfos = - ImmutableSet.<OptionInfo<?>>builder().addAll(staticInfo.getOptionInfos()); - - for (Object source : sources) { - Class<?> clazz = source instanceof Class ? (Class) source : source.getClass(); - for (Field field : clazz.getDeclaredFields()) { - if (filter.apply(field)) { - boolean cmdLine = field.isAnnotationPresent(CmdLine.class); - boolean positional = field.isAnnotationPresent(Positional.class); - if (cmdLine && positional) { - throw new IllegalArgumentException( - "An Arg cannot be annotated with both @CmdLine and @Positional, found bad Arg " - + "field: " + field); - } else if (cmdLine) { - optionInfos.add(OptionInfo.createFromField(field, source)); - } else if (positional) { - positionalInfos.add(PositionalInfo.createFromField(field, source)); - } - } - } - } - - @Nullable PositionalInfo<?> positionalInfo = - Iterables.getOnlyElement(positionalInfos.build(), null); - return new ArgsInfo(configuration, Optional.fromNullable(positionalInfo), optionInfos.build()); - } - - private Args() { - // utility - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java b/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java deleted file mode 100644 index 528dece..0000000 --- a/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java +++ /dev/null @@ -1,247 +0,0 @@ -/** - * 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 com.twitter.common.args; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.List; - -import javax.annotation.Nullable; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.reflect.TypeToken; - -import com.twitter.common.args.constraints.NotNullVerifier; -import com.twitter.common.base.MorePreconditions; - -/** - * Description of a command line {@link Arg} instance. - */ -public abstract class ArgumentInfo<T> { - static final ImmutableSet<String> HELP_ARGS = ImmutableSet.of("h", "help"); - - /** - * Extracts the {@code Arg} from the given field. - * - * @param field The field containing the {@code Arg}. - * @param instance An optional object instance containing the field. - * @return The extracted {@code} Arg. - * @throws IllegalArgumentException If the field does not contain an arg. - */ - protected static Arg<?> getArgForField(Field field, Optional<?> instance) { - Preconditions.checkArgument(field.getType() == Arg.class, - "Field is annotated for argument parsing but is not of Arg type: " + field); - Preconditions.checkArgument(Modifier.isStatic(field.getModifiers()) || instance.isPresent(), - "Non-static argument fields are not supported, found " + field); - - field.setAccessible(true); - try { - return (Arg<?>) field.get(instance.orNull()); - } catch (IllegalAccessException e) { - throw new RuntimeException("Cannot get arg value for " + field); - } - } - - private final String canonicalName; - private final String name; - private final String help; - private final boolean argFile; - private final Arg<T> arg; - private final TypeToken<T> type; - private final List<Annotation> verifierAnnotations; - @Nullable private final Class<? extends Parser<? extends T>> parser; - - /** - * Creates a new {@code ArgsInfo}. - * - * @param canonicalName A fully qualified name for the argument. - * @param name The simple name for the argument. - * @param help Help string. - * @param argFile If argument file is allowed. - * @param arg Argument object. - * @param type Concrete argument type. - * @param verifierAnnotations {@link com.twitter.common.args.Verifier} annotations for this - * argument. - * @param parser Parser for the argument type. - */ - protected ArgumentInfo( - String canonicalName, - String name, - String help, - boolean argFile, - Arg<T> arg, - TypeToken<T> type, - List<Annotation> verifierAnnotations, - @Nullable Class<? extends Parser<? extends T>> parser) { - - this.canonicalName = MorePreconditions.checkNotBlank(canonicalName); - this.name = MorePreconditions.checkNotBlank(name); - this.help = MorePreconditions.checkNotBlank(help); - this.argFile = argFile; - this.arg = Preconditions.checkNotNull(arg); - this.type = Preconditions.checkNotNull(type); - this.verifierAnnotations = ImmutableList.copyOf(verifierAnnotations); - this.parser = parser; - } - - /** - * Return the name of the command line argument. In an optional argument, this is expressed on - * the command line by "-name=value"; whereas, for a positional argument, the name indicates - * the type/function. - */ - public final String getName() { - return name; - } - - /** - * Return the fully-qualified name of the command line argument. This is used as a command-line - * optional argument, as in: -prefix.name=value. Prefix is typically a java package and class like - * "com.twitter.myapp.MyClass". The difference between a canonical name and a regular name is that - * it is in some circumstances for two names to collide; the canonical name, then, disambiguates. - */ - public final String getCanonicalName() { - return canonicalName; - } - - /** - * Returns the instructions for this command-line argument. This is typically used when the - * executable is passed the -help flag. - */ - public String getHelp() { - return help; - } - - /** - * Returns whether an argument file is allowed for this argument. - */ - public boolean argFile() { - return argFile; - } - - /** - * Returns the Arg associated with this command-line argument. The Arg<?> is a mutable container - * cell that holds the value passed-in on the command line, after parsing and validation. - */ - public Arg<T> getArg() { - return arg; - } - - /** - * Sets the value of the {@link Arg} described by this {@code ArgumentInfo}. - * - * @param value The value to set. - */ - protected void setValue(@Nullable T value) { - arg.set(value); - } - - /** - * Returns the TypeToken that represents the type of this command-line argument. - */ - public TypeToken<T> getType() { - return type; - } - - @Override - public boolean equals(Object object) { - return (object instanceof ArgumentInfo) && arg.equals(((ArgumentInfo) object).arg); - } - - @Override - public int hashCode() { - return arg.hashCode(); - } - - /** - * Finds an appropriate parser for this args underlying value type. - * - * @param parserOracle The registry of known parsers. - * @return A parser that can parse strings into the underlying argument type. - * @throws IllegalArgumentException If no parser was found for the underlying argument type. - */ - protected Parser<? extends T> getParser(ParserOracle parserOracle) { - Preconditions.checkNotNull(parserOracle); - if (parser == null || NoParser.class.equals(parser)) { - return parserOracle.get(type); - } else { - try { - return parser.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException("Failed to instantiate parser " + parser); - } catch (IllegalAccessException e) { - throw new RuntimeException("No access to instantiate parser " + parser); - } - } - } - - static class ValueVerifier<T> { - private final Verifier<? super T> verifier; - private final Annotation annotation; - - ValueVerifier(Verifier<? super T> verifier, Annotation annotation) { - this.verifier = verifier; - this.annotation = annotation; - } - - void verify(@Nullable T value) { - if (value != null || verifier instanceof NotNullVerifier) { - verifier.verify(value, annotation); - } - } - - String toString(Class<? extends T> rawType) { - return verifier.toString(rawType, annotation); - } - } - - private Iterable<ValueVerifier<T>> getVerifiers(final Verifiers verifierOracle) { - Function<Annotation, Optional<ValueVerifier<T>>> toVerifier = - new Function<Annotation, Optional<ValueVerifier<T>>>() { - @Override public Optional<ValueVerifier<T>> apply(Annotation annotation) { - @Nullable Verifier<? super T> verifier = verifierOracle.get(type, annotation); - if (verifier != null) { - return Optional.of(new ValueVerifier<T>(verifier, annotation)); - } else { - return Optional.absent(); - } - } - }; - return Optional.presentInstances(Iterables.transform(verifierAnnotations, toVerifier)); - } - - void verify(Verifiers verifierOracle) { - @Nullable T value = getArg().uncheckedGet(); - for (ValueVerifier<T> valueVerifier : getVerifiers(verifierOracle)) { - valueVerifier.verify(value); - } - } - - ImmutableList<String> collectConstraints(Verifiers verifierOracle) { - @SuppressWarnings("unchecked") // type.getType() is T - final Class<? extends T> rawType = (Class<? extends T>) type.getRawType(); - return FluentIterable.from(getVerifiers(verifierOracle)).transform( - new Function<ValueVerifier<T>, String>() { - @Override public String apply(ValueVerifier<T> verifier) { - return verifier.toString(rawType); - } - }).toList(); - } -}