/*
* Copyright (c) 2015 Spotify AB.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.spotify.heroic.cluster;
import static com.spotify.heroic.common.Optionals.pickOptional;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.spotify.heroic.common.ServiceInfo;
import com.spotify.heroic.dagger.PrimaryComponent;
import com.spotify.heroic.lifecycle.LifeCycle;
import com.spotify.heroic.lifecycle.LifeCycleManager;
import com.spotify.heroic.metadata.MetadataComponent;
import com.spotify.heroic.metric.MetricComponent;
import com.spotify.heroic.statistics.HeroicReporter;
import com.spotify.heroic.statistics.QueryReporter;
import com.spotify.heroic.suggest.SuggestComponent;
import dagger.Module;
import dagger.Provides;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import javax.inject.Named;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
/**
* @author udoprog
*/
@Data
@Module
public class ClusterManagerModule {
public static final boolean DEFAULT_USE_LOCAL = true;
private final UUID id;
private final Map<String, String> tags;
private final boolean useLocal;
private final ClusterDiscoveryModule discovery;
private final List<RpcProtocolModule> protocols;
private final Set<Map<String, String>> topology;
private final Optional<NodeMetadataFactory> metadataFactory;
@Provides
@ClusterScope
@Named("local")
public ClusterNode localClusterNode(LocalClusterNode local) {
return local;
}
@Provides
@ClusterScope
public NodeMetadata localMetadata(final ServiceInfo service) {
return new NodeMetadata(0, id, tags, service);
}
@Provides
@ClusterScope
public NodeMetadataProvider metadataProvider(final NodeMetadata localMetadata) {
return metadataFactory
.map(fn -> fn.buildProvider(localMetadata))
.orElseGet(() -> NodeMetadataProvider.staticProvider(localMetadata));
}
@Provides
@ClusterScope
@Named("useLocal")
public Boolean useLocal() {
return useLocal;
}
@Provides
@ClusterScope
@Named("topology")
public Set<Map<String, String>> topology() {
return topology;
}
@Provides
@ClusterScope
public QueryReporter queryReporter(HeroicReporter heroicReporter) {
return heroicReporter.newQueryReporter();
}
@Provides
@ClusterScope
public List<Pair<String, RpcProtocolComponent>> protocolComponents(
final NodeMetadataProvider metadataProvider,
@Named("local") final ClusterNode localClusterNode, final PrimaryComponent primary,
final MetricComponent metric, final MetadataComponent metadata,
final SuggestComponent suggest
) {
final ImmutableList.Builder<Pair<String, RpcProtocolComponent>> protocolComponents =
ImmutableList.builder();
/* build up a local component which defines all dependencies for a child component */
final RpcProtocolModule.Dependencies dependencies = DaggerRpcProtocolModule_Dependencies
.builder()
.primaryComponent(primary)
.metricComponent(metric)
.metadataComponent(metadata)
.suggestComponent(suggest)
.provided(new RpcProtocolModule.Provided() {
@Override
public NodeMetadataProvider metadataProvider() {
return metadataProvider;
}
@Override
public ClusterNode localClusterNode() {
return localClusterNode;
}
})
.build();
for (final RpcProtocolModule m : protocols) {
protocolComponents.add(Pair.of(m.scheme(), m.module(dependencies)));
}
return protocolComponents.build();
}
@Provides
@ClusterScope
public Map<String, RpcProtocol> protocols(
List<Pair<String, RpcProtocolComponent>> protocols
) {
final Map<String, RpcProtocol> map = new HashMap<>();
for (final Pair<String, RpcProtocolComponent> m : protocols) {
map.put(m.getLeft(), m.getRight().rpcProtocol());
}
return map;
}
@Provides
@ClusterScope
@Named("cluster")
LifeCycle clusterLife(
LifeCycleManager manager, CoreClusterManager cluster,
List<Pair<String, RpcProtocolComponent>> protocols
) {
final List<LifeCycle> life = new ArrayList<>();
life.add(manager.build(cluster));
protocols.stream().map(p -> p.getRight().life()).forEach(life::add);
return LifeCycle.combined(life);
}
public static Builder builder() {
return new Builder();
}
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Builder {
private Optional<UUID> id = empty();
private Optional<Map<String, String>> tags = empty();
private Optional<Boolean> useLocal = empty();
private Optional<ClusterDiscoveryModule> discovery = empty();
private Optional<List<RpcProtocolModule>> protocols = empty();
private Optional<Set<Map<String, String>>> topology = empty();
private Optional<NodeMetadataFactory> metadataFactory = empty();
@JsonCreator
public Builder(
@JsonProperty("id") Optional<UUID> id,
@JsonProperty("tags") Optional<Map<String, String>> tags,
@JsonProperty("useLocal") Optional<Boolean> useLocal,
@JsonProperty("discovery") Optional<ClusterDiscoveryModule> discovery,
@JsonProperty("protocols") Optional<List<RpcProtocolModule>> protocols,
@JsonProperty("topology") Optional<Set<Map<String, String>>> topology
) {
this.id = id;
this.tags = tags;
this.useLocal = useLocal;
this.discovery = discovery;
this.protocols = protocols;
this.topology = topology;
}
public Builder id(UUID id) {
this.id = of(id);
return this;
}
public Builder tags(Map<String, String> tags) {
this.tags = of(tags);
return this;
}
public Builder useLocal(Boolean useLocal) {
this.useLocal = of(useLocal);
return this;
}
public Builder discovery(ClusterDiscoveryModule discovery) {
this.discovery = of(discovery);
return this;
}
public Builder protocols(List<RpcProtocolModule> protocols) {
this.protocols = of(protocols);
return this;
}
public Builder topology(Set<Map<String, String>> topology) {
this.topology = of(topology);
return this;
}
/**
* Set the metadata factory.
*
* Only used for testing purposes.
*
* @param metadataFactory factory to use
* @return this builder
*/
public Builder metadataFactory(NodeMetadataFactory metadataFactory) {
this.metadataFactory = of(metadataFactory);
return this;
}
public Builder merge(Builder o) {
// @formatter:off
return new Builder(
pickOptional(id, o.id),
pickOptional(tags, o.tags),
pickOptional(useLocal, o.useLocal),
pickOptional(discovery, o.discovery),
pickOptional(protocols, o.protocols),
pickOptional(topology, o.topology),
pickOptional(metadataFactory, o.metadataFactory)
);
// @formatter:on
}
public ClusterManagerModule build() {
// @formatter:off
return new ClusterManagerModule(
id.orElseGet(UUID::randomUUID),
tags.orElseGet(ImmutableMap::of),
useLocal.orElse(DEFAULT_USE_LOCAL),
discovery.orElseGet(ClusterDiscoveryModule::nullModule),
protocols.orElseGet(ImmutableList::of),
topology.orElseGet(ImmutableSet::of),
metadataFactory
);
// @formatter:on
}
}
}