All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ratpack.registry.Registry Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc-1
Show newest version
/*
 * Copyright 2013 the original author or authors.
 *
 * 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 ratpack.registry;

import com.google.common.reflect.TypeToken;
import ratpack.func.Action;
import ratpack.func.Function;
import ratpack.registry.internal.*;

import java.util.Optional;
import java.util.function.Supplier;

/**
 * An object registry.
 * 

* Registries are primarily used for inter {@link ratpack.handling.Handler} communication in request processing. * The {@link ratpack.handling.Context} object that handlers operate on implements the {@link ratpack.registry.Registry} interface. *

 * import ratpack.handling.Handler;
 * import ratpack.handling.Context;
 * import ratpack.registry.Registry;
 *
 * import static org.junit.Assert.assertTrue;
 *
 * public class Thing {
 *   private final String name
 *   public Thing(String name) { this.name = name; }
 *   public String getName() { return name; }
 * }
 *
 * public class UpstreamHandler implements Handler {
 *   public void handle(Context context) {
 *     context.next(Registry.single(new Thing("foo")));
 *   }
 * }
 *
 * public class DownstreamHandler implements Handler {
 *   public void handle(Context context) {
 *     assertTrue(context instanceof Registry);
 *     Thing thing = context.get(Thing.class);
 *     context.render(thing.getName());
 *   }
 * }
 *
 * import ratpack.test.handling.HandlingResult;
 * import ratpack.test.handling.RequestFixture;
 *
 * import static ratpack.handling.Handlers.chain;
 * import static ratpack.func.Action.noop;
 *
 * import static org.junit.Assert.assertEquals;
 *
 * Handler chain = chain(new UpstreamHandler(), new DownstreamHandler());
 * HandlingResult result = RequestFixture.handle(chain, noop());
 *
 * assertEquals("foo", result.rendered(String.class));
 * 
*

Thread safety

*

* Registry objects are assumed to be thread safe. * No external synchronization is performed around registry access. * As registry objects may be used across multiple requests, they should be thread safe. *

* Registries that are created per request however do not need to be thread safe. * *

Ordering

*

* Registry objects are returned in the reverse order that they were added (i.e. Last-In-First-Out). * *

{@code
 * import com.google.common.base.Joiner;
 * import ratpack.registry.Registry;
 *
 * import static org.junit.Assert.assertEquals;
 *
 * public class Example {
 *   public static void main(String... args) throws Exception {
 *     Registry registry = Registry.of(r -> r
 *         .add("Ratpack")
 *         .add("foo")
 *         .add("bar")
 *     );
 *
 *     assertEquals("bar", registry.get(String.class));
 *
 *     String joined = Joiner.on(", ").join(registry.getAll(String.class));
 *     assertEquals("bar, foo, Ratpack", joined);
 *   }
 * }
 * }
*

* While this is strictly the case for the core registry implementations in Ratpack, adapted implementations (e.g. Guice, Spring etc.) may have more nuanced ordering semantics. * To the greatest extent possible, registry implementations should strive to honor LIFO ordering. */ public interface Registry { /** * Provides an object of the specified type, or throws an exception if no object of that type is available. * * @param type The type of the object to provide * @param The type of the object to provide * @return An object of the specified type * @throws NotInRegistryException If no object of this type can be returned */ default O get(Class type) throws NotInRegistryException { return get(TypeCaching.typeToken(type)); } /** * Provides an object of the specified type, or throws an exception if no object of that type is available. * * @param type The type of the object to provide * @param The type of the object to provide * @return An object of the specified type * @throws NotInRegistryException If no object of this type can be returned */ default O get(TypeToken type) throws NotInRegistryException { Optional o = maybeGet(type); if (o.isPresent()) { return o.get(); } else { throw new NotInRegistryException(type); } } /** * Does the same thing as {@link #get(Class)}, except returns null instead of throwing an exception. * * @param type The type of the object to provide * @param The type of the object to provide * @return An object of the specified type, or null if no object of this type is available. */ default Optional maybeGet(Class type) { return maybeGet(TypeCaching.typeToken(type)); } /** * Does the same thing as {@link #get(Class)}, except returns null instead of throwing an exception. * * @param type The type of the object to provide * @param The type of the object to provide * @return An optional of an object of the specified type */ Optional maybeGet(TypeToken type); /** * Returns all of the objects whose declared type is assignment compatible with the given type. * * @param type the type of objects to search for * @param the type of objects to search for * @return All objects of the given type */ default Iterable getAll(Class type) { return getAll(TypeCaching.typeToken(type)); } /** * Returns all of the objects whose declared type is assignment compatible with the given type. * * @param type the type of objects to search for * @param the type of objects to search for * @return All objects of the given type */ Iterable getAll(TypeToken type); /** * Find and transform an item. *

* This method will apply the given function to items in the order returned by {@link #getAll(TypeToken)} until the function returns a non null value. * The first non null value will be returned by this method immediately (as an optional). * If the function returns a null value for every item, an empty optional is returned. * * @param type the type of object to search for * @param function a function to apply to each item * @param the type of the object to search for * @param the type of transformed object * @throws Exception any thrown by the function * @return An optional of the object of the specified type that satisfied the specified predicate */ default Optional first(TypeToken type, Function function) throws Exception { Iterable all = getAll(type); for (T t : all) { O out = function.apply(t); if (out != null) { return Optional.of(out); } } return Optional.empty(); } /** * A convenience method for {@link #first(TypeToken, Function)}. * * @param type the type of object to search for * @param function a function to apply to each item * @param the type of the object to search for * @param the type of transformed object * @throws Exception any thrown by the function * @return An optional of the object of the specified type that satisfied the specified predicate * @see #first(TypeToken, Function) */ default Optional first(Class type, Function function) throws Exception { return first(TypeCaching.typeToken(type), function); } /** * Creates a new registry by joining {@code this} registry with the given registry *

* The returned registry is effectively the union of the two registries, with the {@code child} registry taking precedence. * This means that child entries are effectively “returned first”. *

{@code
   * import ratpack.registry.Registry;
   *
   * import java.util.List;
   * import com.google.common.collect.Lists;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *
   *   public static interface Thing {
   *     String getName();
   *   }
   *
   *   public static class ThingImpl implements Thing {
   *     private final String name;
   *
   *     public ThingImpl(String name) {
   *       this.name = name;
   *     }
   *
   *     public String getName() {
   *       return name;
   *     }
   *   }
   *
   *   public static void main(String[] args) {
   *     Registry child = Registry.builder().add(Thing.class, new ThingImpl("child-1")).add(Thing.class, new ThingImpl("child-2")).build();
   *     Registry parent = Registry.builder().add(Thing.class, new ThingImpl("parent-1")).add(Thing.class, new ThingImpl("parent-2")).build();
   *     Registry joined = parent.join(child);
   *
   *     assertEquals("child-2", joined.get(Thing.class).getName());
   *     List all = Lists.newArrayList(joined.getAll(Thing.class));
   *     assertEquals("child-2", all.get(0).getName());
   *     assertEquals("child-1", all.get(1).getName());
   *     assertEquals("parent-2", all.get(2).getName());
   *     assertEquals("parent-1", all.get(3).getName());
   *   }
   * }
   * }
* * @param child the child registry * @return a registry which is the combination of the {@code this} and the given child */ default Registry join(Registry child) { if (this == EmptyRegistry.INSTANCE) { return child; } else if (child == EmptyRegistry.INSTANCE) { return this; } else { return new HierarchicalRegistry(this, child); } } /** * Returns an empty registry. * * @return an empty registry */ static Registry empty() { return EmptyRegistry.INSTANCE; } /** * Creates a new {@link RegistryBuilder registry builder}. * * @return a new registry builder * @see RegistryBuilder */ static RegistryBuilder builder() { return new DefaultRegistryBuilder(); } /** * Builds a registry from the given action. * * @param action the action that defines the registry * @return a registry created by the given action * @throws Exception any thrown by the action */ static Registry of(Action action) throws Exception { RegistryBuilder builder = builder(); action.execute(builder); return builder.build(); } /** * Creates a new registry instance that is backed by a RegistryBacking implementation. * The registry instance caches lookups. * * @param registryBacking the implementation that returns instances for the registry * @return a new registry */ static Registry backedBy(final RegistryBacking registryBacking) { return new CachingBackedRegistry(registryBacking); } /** * Deprecated, use {@link #singleLazy(Class, Supplier)}. * * @param publicType the public type of the entry * @param supplier the supplier for the object * @param the public type of the entry * @return a new single entry registry * @see RegistryBuilder#addLazy(Class, Supplier) * @deprecated use {@link #singleLazy(Class, Supplier)} */ @Deprecated static Registry single(Class publicType, Supplier supplier) { return builder().addLazy(publicType, supplier).build(); } /** * Creates a single lazily created entry registry, using {@link RegistryBuilder#addLazy(Class, Supplier)}. * * @param publicType the public type of the entry * @param supplier the supplier for the object * @param the public type of the entry * @return a new single entry registry * @see RegistryBuilder#addLazy(Class, Supplier) * @since 1.1 */ static Registry singleLazy(Class publicType, Supplier supplier) { return builder().addLazy(publicType, supplier).build(); } /** * Creates a single entry registry, using {@link RegistryBuilder#add(Object)}. * * @param object the entry object * @return a new single entry registry * @see RegistryBuilder#add(java.lang.Object) */ static Registry single(Object object) { return builder().add(object).build(); } /** * Creates a single entry registry, using {@link RegistryBuilder#add(Class, Object)}. * * @param publicType the public type of the entry * @param implementation the entry object * @param the public type of the entry * @return a new single entry registry * @see RegistryBuilder#add(Class, Object) */ static Registry single(Class publicType, T implementation) { return builder().add(publicType, implementation).build(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy