/*
 * Copyright DataStax, 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.datastax.oss.driver.internal.core.metadata.schema.refresh;

import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata;
import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
import com.datastax.oss.driver.internal.core.metadata.DefaultMetadata;
import com.datastax.oss.driver.internal.core.metadata.MetadataRefresh;
import com.datastax.oss.driver.internal.core.metadata.schema.events.AggregateChangeEvent;
import com.datastax.oss.driver.internal.core.metadata.schema.events.FunctionChangeEvent;
import com.datastax.oss.driver.internal.core.metadata.schema.events.KeyspaceChangeEvent;
import com.datastax.oss.driver.internal.core.metadata.schema.events.TableChangeEvent;
import com.datastax.oss.driver.internal.core.metadata.schema.events.TypeChangeEvent;
import com.datastax.oss.driver.internal.core.metadata.schema.events.ViewChangeEvent;
import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import com.datastax.oss.driver.shaded.guava.common.collect.Sets;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import net.jcip.annotations.ThreadSafe;

@ThreadSafe
public class SchemaRefresh implements MetadataRefresh {

  @VisibleForTesting public final Map<CqlIdentifier, KeyspaceMetadata> newKeyspaces;

  public SchemaRefresh(Map<CqlIdentifier, KeyspaceMetadata> newKeyspaces) {
    this.newKeyspaces = newKeyspaces;
  }

  @Override
  public Result compute(
      DefaultMetadata oldMetadata, boolean tokenMapEnabled, InternalDriverContext context) {
    ImmutableList.Builder<Object> events = ImmutableList.builder();

    Map<CqlIdentifier, KeyspaceMetadata> oldKeyspaces = oldMetadata.getKeyspaces();
    for (CqlIdentifier removedKey : Sets.difference(oldKeyspaces.keySet(), newKeyspaces.keySet())) {
      events.add(KeyspaceChangeEvent.dropped(oldKeyspaces.get(removedKey)));
    }
    for (Map.Entry<CqlIdentifier, KeyspaceMetadata> entry : newKeyspaces.entrySet()) {
      CqlIdentifier key = entry.getKey();
      computeEvents(oldKeyspaces.get(key), entry.getValue(), events);
    }

    return new Result(
        oldMetadata.withSchema(this.newKeyspaces, tokenMapEnabled, context), events.build());
  }

  
Computes the exact set of events to emit when a keyspace has changed.

We can't simply emit KeyspaceChangeEvent.updated(KeyspaceMetadata, KeyspaceMetadata) because this method might be called as part of a full schema refresh, or a keyspace refresh initiated by coalesced child element refreshes. We need to traverse all children to check what has exactly changed.

/** * Computes the exact set of events to emit when a keyspace has changed. * * <p>We can't simply emit {@link KeyspaceChangeEvent#updated(KeyspaceMetadata, KeyspaceMetadata)} * because this method might be called as part of a full schema refresh, or a keyspace refresh * initiated by coalesced child element refreshes. We need to traverse all children to check what * has exactly changed. */
private void computeEvents( KeyspaceMetadata oldKeyspace, KeyspaceMetadata newKeyspace, ImmutableList.Builder<Object> events) { if (oldKeyspace == null) { events.add(KeyspaceChangeEvent.created(newKeyspace)); } else { if (!oldKeyspace.shallowEquals(newKeyspace)) { events.add(KeyspaceChangeEvent.updated(oldKeyspace, newKeyspace)); } computeChildEvents(oldKeyspace, newKeyspace, events); } } private void computeChildEvents( KeyspaceMetadata oldKeyspace, KeyspaceMetadata newKeyspace, ImmutableList.Builder<Object> events) { computeChildEvents( oldKeyspace.getTables(), newKeyspace.getTables(), TableChangeEvent::dropped, TableChangeEvent::created, TableChangeEvent::updated, events); computeChildEvents( oldKeyspace.getViews(), newKeyspace.getViews(), ViewChangeEvent::dropped, ViewChangeEvent::created, ViewChangeEvent::updated, events); computeChildEvents( oldKeyspace.getUserDefinedTypes(), newKeyspace.getUserDefinedTypes(), TypeChangeEvent::dropped, TypeChangeEvent::created, TypeChangeEvent::updated, events); computeChildEvents( oldKeyspace.getFunctions(), newKeyspace.getFunctions(), FunctionChangeEvent::dropped, FunctionChangeEvent::created, FunctionChangeEvent::updated, events); computeChildEvents( oldKeyspace.getAggregates(), newKeyspace.getAggregates(), AggregateChangeEvent::dropped, AggregateChangeEvent::created, AggregateChangeEvent::updated, events); } private <K, V> void computeChildEvents( Map<K, V> oldChildren, Map<K, V> newChildren, Function<V, Object> newDroppedEvent, Function<V, Object> newCreatedEvent, BiFunction<V, V, Object> newUpdatedEvent, ImmutableList.Builder<Object> events) { for (K removedKey : Sets.difference(oldChildren.keySet(), newChildren.keySet())) { events.add(newDroppedEvent.apply(oldChildren.get(removedKey))); } for (Map.Entry<K, V> entry : newChildren.entrySet()) { K key = entry.getKey(); V newChild = entry.getValue(); V oldChild = oldChildren.get(key); if (oldChild == null) { events.add(newCreatedEvent.apply(newChild)); } else if (!oldChild.equals(newChild)) { events.add(newUpdatedEvent.apply(oldChild, newChild)); } } } }