package com.palantir.atlasdb.endpoint;

import java.lang.reflect.Proxy;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.google.common.base.Supplier;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.partition.map.InKvsPartitionMapService;
import com.palantir.atlasdb.keyvalue.partition.map.PartitionMapService;
import com.palantir.atlasdb.keyvalue.partition.server.EndpointServer;
import com.palantir.atlasdb.keyvalue.rdbms.PostgresKeyValueConfiguration;
import com.palantir.atlasdb.keyvalue.rdbms.PostgresKeyValueService;
import com.palantir.atlasdb.keyvalue.remoting.InsufficientConsistencyExceptionMapper;
import com.palantir.atlasdb.keyvalue.remoting.KeyAlreadyExistsExceptionMapper;
import com.palantir.atlasdb.keyvalue.remoting.RemotingKeyValueService;
import com.palantir.atlasdb.keyvalue.remoting.VersionTooOldExceptionMapper;
import com.palantir.atlasdb.keyvalue.remoting.outofband.InboxPopulatingContainerRequestFilter;
import com.palantir.common.proxy.AbstractDelegatingInvocationHandler;

import io.dropwizard.Application;
import io.dropwizard.setup.Environment;

public class PostgresEndpoint extends Application<EndpointServerConfiguration> {

    private final ObjectMapper mapper = RemotingKeyValueService.kvsMapper();

    public static void main(String[] args) throws Exception {
        new PostgresEndpoint().run(args);
    }

    private PostgresEndpoint() {
    }

    @Override
    public void run(EndpointServerConfiguration configuration,
            Environment environment) throws Exception {
        PostgresKeyValueConfiguration config = mapper.treeToValue(configuration.extraConfig.get("postgresConfig"), PostgresKeyValueConfiguration.class);
        PostgresKeyValueService kvs = PostgresKeyValueService.create(config);
        kvs.initializeFromFreshInstance();
        final InKvsPartitionMapService pms = InKvsPartitionMapService.create(kvs);

        final EndpointServer server = new EndpointServer(RemotingKeyValueService.createServerSide(kvs, new Supplier<Long>() {
            @Override
            public Long get() {
                return pms.getMapVersion();
            }
        }), pms);

        // Wrap server with two proxies.
        // Otherwise Jersey will not handle properly a single
        // object that implements two annotated interfaces.

        KeyValueService kvsProxy = (KeyValueService) Proxy.newProxyInstance(KeyValueService.class.getClassLoader(), new Class<?>[] { KeyValueService.class }, new AbstractDelegatingInvocationHandler() {
            @Override
            public Object getDelegate() {
                return server;
            }
        });
        PartitionMapService pmsProxy = (PartitionMapService) Proxy.newProxyInstance(PartitionMapService.class.getClassLoader(), new Class<?>[] { PartitionMapService.class }, new AbstractDelegatingInvocationHandler() {
            @Override
            public Object getDelegate() {
                return server;
            }
        });

        environment.jersey().register(new InboxPopulatingContainerRequestFilter(mapper));
        environment.jersey().register(KeyAlreadyExistsExceptionMapper.instance());
        environment.jersey().register(InsufficientConsistencyExceptionMapper.instance());
        environment.jersey().register(VersionTooOldExceptionMapper.instance());
        environment.getObjectMapper().registerModule(RemotingKeyValueService.kvsModule());
        environment.getObjectMapper().registerModule(new GuavaModule());
        environment.jersey().register(kvsProxy);
        environment.jersey().register(pmsProxy);
    }

}

@@ -1269,16 +1269,457 @@
 ion%3E %7B%0A%0A
+    @SuppressWarnings(%22unchecked%22)%0A    static %3CT%3E T identityProxy(final T delegate, Class%3CT%3E delegateClass) %7B%0A        return (T) Proxy.newProxyInstance(%0A                delegateClass.getClassLoader(),%0A                new Class%3C?%3E%5B%5D %7B delegateClass %7D,%0A                new AbstractDelegatingInvocationHandler() %7B%0A            @Override%0A            public Object getDelegate() %7B%0A                return delegate;%0A            %7D%0A        %7D);%0A    %7D%0A%0A
     priv
@@ -2890,632 +2890,140 @@
 y = 
-(KeyValueService) Proxy.newProxyInstance(KeyValueService.class.getClassLoader(), new Class%3C?%3E%5B%5D %7B KeyValueService.class %7D, new AbstractDelegatingInvocationHandler() %7B%0A            @Override%0A            public Object getDelegate() %7B%0A                return server;%0A            %7D%0A        %7D);%0A        PartitionMapService pmsProxy = (PartitionMapService) Proxy.newProxyInstance(PartitionMapService.class.getClassLoader(), new Class%3C?%3E%5B%5D %7B PartitionMapService.class %7D, new AbstractDelegatingInvocationHandler() %7B%0A            @Override%0A            public Object getDelegate() %7B%0A                return server;%0A            %7D%0A        %7D
+identityProxy(server, KeyValueService.class);%0A        PartitionMapService pmsProxy = identityProxy(server, PartitionMapService.class
 );%0A%0A

Refactor PostgresEndpoint.