/** The base implementation of {@link NetworkService} */
public abstract class NetworkServiceBase implements NetworkService {

  private volatile boolean started;
  private Map<Integer, NetworkClient> registry = C.newMap();

  public synchronized void register(int port, NetworkClient client) {
    E.NPE(client);
    E.illegalArgumentIf(registry.containsKey(port), "Port %s has been registered already", port);
    registry.put(port, client);
    if (started) {
      clientRegistered(client, port);
    }
  }

  @Override
  public void start() {
    bootUp();
    for (int port : registry.keySet()) {
      NetworkClient client = registry.get(port);
      clientRegistered(client, port);
    }
  }

  @Override
  public void shutdown() {
    close();
    registry.clear();
  }

  protected abstract void clientRegistered(NetworkClient client, int port);

  protected abstract void bootUp();

  protected abstract void close();
}
@ActComponent
public class StringValueResolverManager extends AppServiceBase<StringValueResolverManager> {

  private Map<Class, StringValueResolver> resolvers = C.newMap();

  public StringValueResolverManager(App app) {
    super(app);
    registerPredefinedResolvers();
    registerBuiltInResolvers(app.config());
  }

  @Override
  protected void releaseResources() {
    resolvers.clear();
  }

  public <T> StringValueResolverManager register(
      Class<T> targetType, StringValueResolver<T> resolver) {
    resolvers.put(targetType, resolver);
    return this;
  }

  public Object resolve(String strVal, Class<?> targetType) {
    StringValueResolver r = resolvers.get(targetType);
    if (null != r) {
      return r.resolve(strVal);
    }
    if (null != strVal && Enum.class.isAssignableFrom(targetType)) {
      return Enum.valueOf(((Class<Enum>) targetType), strVal);
    }
    return null;
  }

  private void registerPredefinedResolvers() {
    resolvers.putAll(StringValueResolver.predefined());
  }

  private void registerBuiltInResolvers(AppConfig config) {
    resolvers.put(Date.class, new DateResolver(config));
    resolvers.put(DateTime.class, new JodaDateResolver(config));
  }
}