com.jivesoftware.os.amza.sync.deployable.AmzaSyncMain.java Source code

Java tutorial

Introduction

Here is the source code for com.jivesoftware.os.amza.sync.deployable.AmzaSyncMain.java

Source

/*
 * Copyright 2013 Jive Software, Inc
 *
 * 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.jivesoftware.os.amza.sync.deployable;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.collect.Sets;
import com.jivesoftware.os.amza.api.AmzaInterner;
import com.jivesoftware.os.amza.api.partition.Consistency;
import com.jivesoftware.os.amza.api.partition.Durability;
import com.jivesoftware.os.amza.api.partition.PartitionName;
import com.jivesoftware.os.amza.api.partition.PartitionProperties;
import com.jivesoftware.os.amza.api.stream.RowType;
import com.jivesoftware.os.amza.client.aquarium.AmzaClientAquariumProvider;
import com.jivesoftware.os.amza.client.collection.AmzaMarshaller;
import com.jivesoftware.os.amza.client.http.AmzaClientProvider;
import com.jivesoftware.os.amza.client.http.HttpPartitionClientFactory;
import com.jivesoftware.os.amza.client.http.HttpPartitionHostsProvider;
import com.jivesoftware.os.amza.client.http.RingHostHttpClientProvider;
import com.jivesoftware.os.amza.sync.api.AmzaSyncPartitionConfig;
import com.jivesoftware.os.amza.sync.api.AmzaSyncPartitionTuple;
import com.jivesoftware.os.amza.sync.api.AmzaSyncSenderConfig;
import com.jivesoftware.os.amza.sync.deployable.endpoints.AmzaSyncApiEndpoints;
import com.jivesoftware.os.amza.sync.deployable.endpoints.AmzaSyncEndpoints;
import com.jivesoftware.os.amza.sync.deployable.oauth.AmzaSyncOAuthValidatorInitializer;
import com.jivesoftware.os.amza.sync.deployable.oauth.AmzaSyncOAuthValidatorInitializer.AmzaSyncOAuthValidatorConfig;
import com.jivesoftware.os.amza.ui.soy.SoyRenderer;
import com.jivesoftware.os.amza.ui.soy.SoyRendererInitializer;
import com.jivesoftware.os.amza.ui.soy.SoyRendererInitializer.SoyRendererConfig;
import com.jivesoftware.os.aquarium.AquariumStats;
import com.jivesoftware.os.aquarium.Member;
import com.jivesoftware.os.jive.utils.ordered.id.ConstantWriterIdProvider;
import com.jivesoftware.os.jive.utils.ordered.id.JiveEpochTimestampProvider;
import com.jivesoftware.os.jive.utils.ordered.id.OrderIdProviderImpl;
import com.jivesoftware.os.jive.utils.ordered.id.SnowflakeIdPacker;
import com.jivesoftware.os.jive.utils.ordered.id.TimestampedOrderIdProvider;
import com.jivesoftware.os.routing.bird.deployable.Deployable;
import com.jivesoftware.os.routing.bird.deployable.DeployableHealthCheckRegistry;
import com.jivesoftware.os.routing.bird.deployable.ErrorHealthCheckConfig;
import com.jivesoftware.os.routing.bird.deployable.InstanceConfig;
import com.jivesoftware.os.routing.bird.deployable.TenantAwareHttpClientHealthCheck;
import com.jivesoftware.os.routing.bird.endpoints.base.FullyOnlineVersion;
import com.jivesoftware.os.routing.bird.endpoints.base.HasUI;
import com.jivesoftware.os.routing.bird.endpoints.base.HasUI.UI;
import com.jivesoftware.os.routing.bird.endpoints.base.LoadBalancerHealthCheckEndpoints;
import com.jivesoftware.os.routing.bird.health.api.HealthFactory;
import com.jivesoftware.os.routing.bird.health.checkers.FileDescriptorCountHealthChecker;
import com.jivesoftware.os.routing.bird.health.checkers.GCLoadHealthChecker;
import com.jivesoftware.os.routing.bird.health.checkers.GCPauseHealthChecker;
import com.jivesoftware.os.routing.bird.health.checkers.LoadAverageHealthChecker;
import com.jivesoftware.os.routing.bird.health.checkers.ServiceStartupHealthCheck;
import com.jivesoftware.os.routing.bird.health.checkers.SystemCpuHealthChecker;
import com.jivesoftware.os.routing.bird.http.client.HttpClient;
import com.jivesoftware.os.routing.bird.http.client.HttpDeliveryClientHealthProvider;
import com.jivesoftware.os.routing.bird.http.client.HttpRequestHelperUtils;
import com.jivesoftware.os.routing.bird.http.client.TailAtScaleStrategy;
import com.jivesoftware.os.routing.bird.http.client.TenantAwareHttpClient;
import com.jivesoftware.os.routing.bird.http.client.TenantRoutingHttpClientInitializer;
import com.jivesoftware.os.routing.bird.server.oauth.validator.AuthValidator;
import com.jivesoftware.os.routing.bird.server.util.Resource;
import com.jivesoftware.os.routing.bird.shared.BoundedExecutor;
import com.jivesoftware.os.routing.bird.shared.ConnectionDescriptor;
import com.jivesoftware.os.routing.bird.shared.ConnectionDescriptors;
import com.jivesoftware.os.routing.bird.shared.HttpClientException;
import com.jivesoftware.os.routing.bird.shared.TenantRoutingProvider;
import com.jivesoftware.os.routing.bird.shared.TenantsServiceConnectionDescriptorProvider;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import org.glassfish.jersey.oauth1.signature.OAuth1Request;
import org.glassfish.jersey.oauth1.signature.OAuth1Signature;

public class AmzaSyncMain {

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

    public void run(String[] args) throws Exception {
        ServiceStartupHealthCheck serviceStartupHealthCheck = new ServiceStartupHealthCheck();
        try {
            final Deployable deployable = new Deployable(args);
            InstanceConfig instanceConfig = deployable.config(InstanceConfig.class);
            HealthFactory.initialize(deployable::config, new DeployableHealthCheckRegistry(deployable));
            deployable.addManageInjectables(HasUI.class, new HasUI(Arrays.asList(new UI("Sync", "main", "/ui"))));
            deployable.addHealthCheck(new GCPauseHealthChecker(
                    deployable.config(GCPauseHealthChecker.GCPauseHealthCheckerConfig.class)));
            deployable.addHealthCheck(new GCLoadHealthChecker(
                    deployable.config(GCLoadHealthChecker.GCLoadHealthCheckerConfig.class)));
            deployable.addHealthCheck(new SystemCpuHealthChecker(
                    deployable.config(SystemCpuHealthChecker.SystemCpuHealthCheckerConfig.class)));
            deployable.addHealthCheck(new LoadAverageHealthChecker(
                    deployable.config(LoadAverageHealthChecker.LoadAverageHealthCheckerConfig.class)));
            deployable.addHealthCheck(new FileDescriptorCountHealthChecker(deployable
                    .config(FileDescriptorCountHealthChecker.FileDescriptorCountHealthCheckerConfig.class)));
            deployable.addHealthCheck(serviceStartupHealthCheck);
            deployable.addErrorHealthChecks(deployable.config(ErrorHealthCheckConfig.class));
            deployable.addManageInjectables(FullyOnlineVersion.class, (FullyOnlineVersion) () -> {
                if (serviceStartupHealthCheck.startupHasSucceeded()) {
                    return instanceConfig.getVersion();
                } else {
                    return null;
                }
            });
            deployable.buildManageServer().start();

            HttpDeliveryClientHealthProvider clientHealthProvider = new HttpDeliveryClientHealthProvider(
                    instanceConfig.getInstanceKey(), HttpRequestHelperUtils.buildRequestHelper(false, false, null,
                            instanceConfig.getRoutesHost(), instanceConfig.getRoutesPort()),
                    instanceConfig.getConnectionsHealth(), 5_000, 100);

            TenantRoutingProvider tenantRoutingProvider = deployable.getTenantRoutingProvider();

            TenantsServiceConnectionDescriptorProvider syncDescriptorProvider = tenantRoutingProvider
                    .getConnections(instanceConfig.getServiceName(), "main", 10_000); // TODO config

            TenantRoutingHttpClientInitializer<String> tenantRoutingHttpClientInitializer = deployable
                    .getTenantRoutingHttpClientInitializer();

            TenantAwareHttpClient<String> amzaClient = tenantRoutingHttpClientInitializer
                    .builder(deployable.getTenantRoutingProvider().getConnections("amza", "main", 10_000), // TODO config
                            clientHealthProvider)
                    .deadAfterNErrors(10).checkDeadEveryNMillis(10_000).socketTimeoutInMillis(60_000).build(); // TODO expose to conf

            deployable.addHealthCheck(new TenantAwareHttpClientHealthCheck("amzaClient", amzaClient));

            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

            AmzaSyncConfig syncConfig = deployable.config(AmzaSyncConfig.class);

            TailAtScaleStrategy tailAtScaleStrategy = new TailAtScaleStrategy(
                    deployable.newBoundedExecutor(1024, "tas"), 100, // TODO config
                    95, // TODO config
                    1000 // TODO config
            );

            AmzaInterner amzaInterner = new AmzaInterner();
            AmzaClientProvider<HttpClient, HttpClientException> amzaClientProvider = new AmzaClientProvider<>(
                    new HttpPartitionClientFactory(),
                    new HttpPartitionHostsProvider(amzaClient, tailAtScaleStrategy, mapper),
                    new RingHostHttpClientProvider(amzaClient),
                    deployable.newBoundedExecutor(syncConfig.getAmzaCallerThreadPoolSize(), "amza-client"),
                    syncConfig.getAmzaAwaitLeaderElectionForNMillis(), -1, -1);

            TimestampedOrderIdProvider orderIdProvider = new OrderIdProviderImpl(
                    new ConstantWriterIdProvider(instanceConfig.getInstanceName()), new SnowflakeIdPacker(),
                    new JiveEpochTimestampProvider());
            AmzaClientAquariumProvider amzaClientAquariumProvider = new AmzaClientAquariumProvider(
                    new AquariumStats(), instanceConfig.getServiceName(), amzaClientProvider, orderIdProvider,
                    new Member(instanceConfig.getInstanceKey().getBytes(StandardCharsets.UTF_8)), count -> {
                        ConnectionDescriptors descriptors = syncDescriptorProvider.getConnections("");
                        int ringSize = descriptors.getConnectionDescriptors().size();
                        return count > ringSize / 2;
                    }, () -> {
                        Set<Member> members = Sets.newHashSet();
                        ConnectionDescriptors descriptors = syncDescriptorProvider.getConnections("");
                        for (ConnectionDescriptor connectionDescriptor : descriptors.getConnectionDescriptors()) {
                            members.add(new Member(connectionDescriptor.getInstanceDescriptor().instanceKey
                                    .getBytes(StandardCharsets.UTF_8)));
                        }
                        return members;
                    }, 128, //TODO config
                    128, //TODO config
                    5_000L, //TODO config
                    100L, //TODO config
                    60_000L, //TODO config
                    10_000L, //TODO config
                    Executors.newSingleThreadExecutor(), 100L, //TODO config
                    1_000L, //TODO config
                    10_000L, //TODO config
                    syncConfig.getAquariumUseSolutionLog());

            ObjectMapper miruSyncMapper = new ObjectMapper();
            miruSyncMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            miruSyncMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            AmzaMarshaller<String> stringMarshaller = new AmzaMarshaller<String>() {
                @Override
                public String fromBytes(byte[] bytes) throws Exception {
                    return new String(bytes, StandardCharsets.UTF_8);
                }

                @Override
                public byte[] toBytes(String value) throws Exception {
                    return value == null ? null : value.getBytes(StandardCharsets.UTF_8);
                }
            };

            AmzaMarshaller<AmzaSyncSenderConfig> amzaSyncSenderConfigMarshaller = new AmzaMarshaller<AmzaSyncSenderConfig>() {
                @Override
                public AmzaSyncSenderConfig fromBytes(byte[] bytes) throws Exception {
                    return mapper.readValue(bytes, AmzaSyncSenderConfig.class);
                }

                @Override
                public byte[] toBytes(AmzaSyncSenderConfig value) throws Exception {
                    return mapper.writeValueAsBytes(value);
                }
            };

            AmzaSyncSenderMap senderConfigStorage = new AmzaSyncSenderMap(amzaClientProvider,
                    "amza-sync-sender-config",
                    new PartitionProperties(Durability.fsync_async, 0, 0, 0, 0, 0, 0, 0, 0, false,
                            Consistency.leader_quorum, true, true, false, RowType.snappy_primary, "lab", -1, null,
                            -1, -1),
                    stringMarshaller, amzaSyncSenderConfigMarshaller);

            AmzaMarshaller<AmzaSyncPartitionTuple> tupleMarshaller = new AmzaMarshaller<AmzaSyncPartitionTuple>() {
                @Override
                public AmzaSyncPartitionTuple fromBytes(byte[] bytes) throws Exception {
                    return AmzaSyncPartitionTuple.fromBytes(bytes, 0, amzaInterner);
                }

                @Override
                public byte[] toBytes(AmzaSyncPartitionTuple value) throws Exception {
                    return AmzaSyncPartitionTuple.toBytes(value);
                }
            };

            AmzaMarshaller<AmzaSyncPartitionConfig> partitionConfigMarshaller = new AmzaMarshaller<AmzaSyncPartitionConfig>() {
                @Override
                public AmzaSyncPartitionConfig fromBytes(byte[] bytes) throws Exception {
                    return mapper.readValue(bytes, AmzaSyncPartitionConfig.class);
                }

                @Override
                public byte[] toBytes(AmzaSyncPartitionConfig value) throws Exception {
                    return mapper.writeValueAsBytes(value);
                }
            };

            AmzaSyncPartitionConfigStorage syncPartitionConfigStorage = new AmzaSyncPartitionConfigStorage(
                    amzaClientProvider, "amza-sync-partitions-config-",
                    new PartitionProperties(Durability.fsync_async, 0, 0, 0, 0, 0, 0, 0, 0, false,
                            Consistency.leader_quorum, true, true, false, RowType.snappy_primary, "lab", -1, null,
                            -1, -1),
                    tupleMarshaller, partitionConfigMarshaller);

            AmzaSyncStats stats = new AmzaSyncStats();

            AmzaSyncReceiver syncReceiver = new AmzaSyncReceiver(amzaClientProvider,
                    syncConfig.getSyncReceiverUseSolutionLog());

            AmzaSyncSenders syncSenders = null;
            if (syncConfig.getSyncSenderEnabled()) {
                ScheduledExecutorService executorService = Executors
                        .newScheduledThreadPool(syncConfig.getSyncSendersThreadCount());
                syncSenders = new AmzaSyncSenders(stats, syncConfig, syncReceiver, executorService,
                        amzaClientProvider, amzaClientAquariumProvider, amzaInterner, mapper, orderIdProvider,
                        senderConfigStorage, syncPartitionConfigStorage, 30_000); // TODO config
            }

            amzaClientAquariumProvider.start();
            if (syncSenders != null) {
                syncSenders.start();
            }

            SoyRendererConfig rendererConfig = deployable.config(SoyRendererConfig.class);

            File staticResourceDir = new File(System.getProperty("user.dir"));
            System.out.println("Static resources rooted at " + staticResourceDir.getAbsolutePath());
            Resource sourceTree = new Resource(staticResourceDir)
                    .addResourcePath(rendererConfig.getPathToStaticResources()).setDirectoryListingAllowed(false)
                    .setContext("/ui/static");
            deployable.addResource(sourceTree);

            SoyRenderer renderer = new SoyRendererInitializer().initialize(rendererConfig);

            AmzaSyncUIService amzaSyncUIService = new AmzaSyncUIServiceInitializer().initialize(renderer,
                    syncSenders, stats, syncConfig.getSyncSenderEnabled(), syncConfig.getSyncReceiverEnabled(),
                    mapper);

            deployable.addEndpoints(LoadBalancerHealthCheckEndpoints.class);
            deployable.addNoAuth("/health/check");
            if (instanceConfig.getMainServiceAuthEnabled()) {
                if (syncConfig.getSyncReceiverEnabled()) {
                    AmzaSyncOAuthValidatorConfig oAuthValidatorConfig = deployable
                            .config(AmzaSyncOAuthValidatorConfig.class);
                    AuthValidator<OAuth1Signature, OAuth1Request> syncOAuthValidator = new AmzaSyncOAuthValidatorInitializer()
                            .initialize(oAuthValidatorConfig);
                    deployable.addCustomOAuth(syncOAuthValidator, "/api/*");
                }
                deployable.addRouteOAuth("/amza/*", "/api/*");
                deployable.addSessionAuth("/ui/*", "/amza/*", "/api/*");
            } else {
                deployable.addNoAuth("/amza/*", "/api/*");
                deployable.addSessionAuth("/ui/*");
            }

            deployable.addEndpoints(AmzaSyncEndpoints.class);
            deployable.addInjectables(AmzaInterner.class, amzaInterner);
            if (syncSenders != null) {
                deployable.addInjectables(AmzaSyncSenders.class, syncSenders);
            }

            deployable.addEndpoints(AmzaSyncUIEndpoints.class);
            deployable.addInjectables(AmzaSyncUIService.class, amzaSyncUIService);

            if (syncConfig.getSyncReceiverEnabled()) {
                deployable.addEndpoints(AmzaSyncApiEndpoints.class);
                deployable.addInjectables(AmzaSyncReceiver.class, syncReceiver);
            }
            deployable.addInjectables(ObjectMapper.class, mapper);
            deployable.addInjectables(AmzaSyncSenderMap.class, senderConfigStorage);
            deployable.addInjectables(AmzaSyncPartitionConfigStorage.class, syncPartitionConfigStorage);

            deployable.buildServer().start();
            clientHealthProvider.start();
            serviceStartupHealthCheck.success();

        } catch (Throwable t) {
            serviceStartupHealthCheck.info("Encountered the following failure during startup.", t);
        }
    }

    private PartitionName extractPartition(String s) {
        if (s.contains("/")) {
            String[] parts = s.split("/");
            return new PartitionName(false, parts[0].trim().getBytes(StandardCharsets.UTF_8),
                    parts[1].trim().getBytes(StandardCharsets.UTF_8));
        } else {
            throw new IllegalArgumentException("Provide partition names in the format ringName/name");
        }
    }
}