Package org.springframework.xd.dirt.container.store

Source Code of org.springframework.xd.dirt.container.store.ZooKeeperContainerRepository

/*
* Copyright 2014 the original author or authors.
*
* 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 org.springframework.xd.dirt.container.store;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.utils.ThreadUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import org.springframework.xd.dirt.cluster.Container;
import org.springframework.xd.dirt.cluster.DetailedContainer;
import org.springframework.xd.dirt.cluster.NoSuchContainerException;
import org.springframework.xd.dirt.module.store.ModuleMetadata;
import org.springframework.xd.dirt.module.store.ZooKeeperModuleMetadataRepository;
import org.springframework.xd.dirt.util.PagingUtility;
import org.springframework.xd.dirt.zookeeper.Paths;
import org.springframework.xd.dirt.zookeeper.ZooKeeperConnection;
import org.springframework.xd.dirt.zookeeper.ZooKeeperUtils;

/**
* ZooKeeper backed repository for runtime info about Containers. This
* implementation uses {@link PathChildrenCache} to cache container
* info from ZooKeeper. The cache is used for all <em>reads</em>, whereas
* all <em>writes</em> are written directly to ZooKeeper. This means
* that a read that immediately follows a write <em>may</em> return
* {@code null} if the cache has not been updated yet.
*
* @author Mark Fisher
* @author David Turanski
* @author Ilayaperumal Gopinathan
* @author Patrick Peralta
*/
public class ZooKeeperContainerRepository implements ContainerRepository, ApplicationListener<ApplicationEvent> {

  /**
   * ZooKeeper connection.
   */
  private final ZooKeeperConnection zkConnection;

  private final ZooKeeperModuleMetadataRepository zkModuleMetadataRepository;

  /**
   * Paging support for this repository.
   */
  private final PagingUtility<Container> pagingUtility = new PagingUtility<Container>();

  /**
   * Paging support for detailed containers.
   */
  private final PagingUtility<DetailedContainer> detailedContainersUtil = new PagingUtility<DetailedContainer>();

  /**
   * Atomic reference to the {@link PathChildrenCache} for containers
   * under the {@link Paths#CONTAINERS} node. This reference should
   * <em>not</em> be used directly; instead use {@link #ensureCache}
   * to ensure the cache is initialized.
   *
   * @see #ensureCache
   */
  private final AtomicReference<PathChildrenCache> cacheRef = new AtomicReference<PathChildrenCache>();

  /**
   * Construct a {@code ZooKeeperContainerRepository}.
   *
   * @param zkConnection the ZooKeeper connection
   * @param moduleMetadataRepository the module metadata repository
   */
  @Autowired
  public ZooKeeperContainerRepository(ZooKeeperConnection zkConnection,
      ZooKeeperModuleMetadataRepository moduleMetadataRepository) {
    this.zkConnection = zkConnection;
    this.zkModuleMetadataRepository = moduleMetadataRepository;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ContextStoppedEvent || event instanceof ContextClosedEvent) {
      closeCache();
    }
  }

  /**
   * Close the {@link PathChildrenCache container cache} and null out
   * the {@link #cacheRef atmoic reference}.
   */
  private void closeCache() {
    PathChildrenCache cache = cacheRef.get();
    if (cache != null) {
      try {
        cache.close();
      }
      catch (Exception e) {
        // ignore exception on close
      }
      finally {
        cacheRef.compareAndSet(cache, null);
      }
    }
  }

  /**
   * Return a {@link PathChildrenCache} for containers, creating and
   * initializing a new instance if necessary.
   *
   * @return a {@code PathChildrenCache} for containers
   * @throws java.lang.IllegalStateException if the cache could not be initialized
   *        (likely due to a ZooKeeper connection error)
   */
  private PathChildrenCache ensureCache() {
    if (cacheRef.get() == null) {
      synchronized (cacheRef) {
        if (cacheRef.get() == null) {
          CuratorFramework client = zkConnection.getClient();
          PathChildrenCache cache = new PathChildrenCache(client, Paths.CONTAINERS,
              true, ThreadUtils.newThreadFactory("ContainerCache"));
          cache.getListenable().addListener(new PathChildrenCacheListener() {

            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) {
              // shut down the cache if ZooKeeper connection goes away
              if (event.getType() == PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED ||
                  event.getType() == PathChildrenCacheEvent.Type.CONNECTION_LOST) {
                closeCache();
              }
            }
          });
          try {
            Paths.ensurePath(client, Paths.CONTAINERS);
            cacheRef.set(cache);
            cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
          }
          catch (Exception e) {
            try {
              cache.close();
            }
            catch (Exception ce) {
              // ignore exception on close
            }
            finally {
              cacheRef.compareAndSet(cache, null);
            }
            throw ZooKeeperUtils.wrapThrowable(e);
          }
        }
      }
    }

    PathChildrenCache cache = cacheRef.get();
    Assert.state(cache != null, "Container cache not initialized " +
        "(likely as a result of a ZooKeeper connection error)");
    return cache;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Iterable<Container> findAll(Sort sort) {
    // todo: add support for sort
    return findAll();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Page<Container> findAll(Pageable pageable) {
    return pagingUtility.getPagedData(pageable, findAll());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public <S extends Container> S save(S entity) {
    CuratorFramework client = zkConnection.getClient();
    String path = Paths.build(Paths.CONTAINERS, entity.getName());

    try {
      client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
          .forPath(path, ZooKeeperUtils.mapToBytes(entity.getAttributes()));
      return entity;
    }
    catch (Exception e) {
      throw ZooKeeperUtils.wrapThrowable(e);
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void update(Container entity) {
    CuratorFramework client = zkConnection.getClient();
    String path = Paths.build(Paths.CONTAINERS, entity.getName());

    try {
      Stat stat = client.checkExists().forPath(path);
      if (stat == null) {
        throw new NoSuchContainerException("Could not find container with id " + entity.getName());
      }
      client.setData().forPath(path, ZooKeeperUtils.mapToBytes(entity.getAttributes()));
    }
    catch (Exception e) {
      throw ZooKeeperUtils.wrapThrowable(e, e.getMessage());
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public <S extends Container> Iterable<S> save(Iterable<S> entities) {
    List<S> results = new ArrayList<S>();
    for (S entity : entities) {
      results.add(save(entity));
    }
    return results;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Container findOne(String id) {
    Container container = null;
    String containerPath = path(id);
    ChildData childData = ensureCache().getCurrentData(containerPath);
    byte[] data = null;

    if (childData != null) {
      data = childData.getData();
    }
    if (data != null) {
      container = new Container(id, ZooKeeperUtils.bytesToMap(data));
    }

    return container;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean exists(String id) {
    return ensureCache().getCurrentData(path(id)) != null;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List<Container> findAll() {
    List<Container> results = new ArrayList<Container>();
    List<ChildData> children = new ArrayList<ChildData>(ensureCache().getCurrentData());

    // sort containers by node creation date (cluster join time)
    Collections.sort(children, new Comparator<ChildData>() {

      @Override
      public int compare(ChildData c1, ChildData c2) {
        long t1 = c1.getStat().getCtime();
        long t2 = c2.getStat().getCtime();
        return (t1 < t2) ? -1 : ((t1 == t2) ? 0 : 1);
      }
    });

    for (ChildData childData : children) {
      results.add(findOne(Paths.stripPath(childData.getPath())));
    }

    return results;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Iterable<Container> findAll(Iterable<String> ids) {
    List<Container> results = new ArrayList<Container>();
    for (String id : ids) {
      Container entity = findOne(id);
      if (entity != null) {
        results.add(entity);
      }
    }
    return results;
  }

  /**
   * Find all the {@link DetailedContainer}s in the XD cluster.
   *
   * @param pageable the pagination info
   * @return the paged list of {@link DetailedContainer}s
   */
  @Override
  public Page<DetailedContainer> findAllRuntimeContainers(Pageable pageable) {
    List<DetailedContainer> results = new ArrayList<DetailedContainer>();
    List<Container> containers = this.findAll();

    for (Container container : containers) {
      DetailedContainer runtimeContainer = new DetailedContainer(container);
      List<ModuleMetadata> deployedModules = zkModuleMetadataRepository.findAllByContainerId(container.getName());
      runtimeContainer.setDeployedModules(deployedModules);
      runtimeContainer.setDeploymentSize(deployedModules.size());
      results.add(runtimeContainer);
    }
    return detailedContainersUtil.getPagedData(pageable, results);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public long count() {
    return ensureCache().getCurrentData().size();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void delete(String id) {
    // Container metadata is "deleted" when a Container departs
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void delete(Container entity) {
    // Container metadata is "deleted" when a Container departs
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void delete(Iterable<? extends Container> entities) {
    // Container metadata is "deleted" when a Container departs
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void deleteAll() {
    // Container metadata is "deleted" when a Container departs
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Iterable<Container> findAllInRange(String from, boolean fromInclusive, String to,
      boolean toInclusive) {
    throw new UnsupportedOperationException("Auto-generated method stub");
  }

  /**
   * Return the path for a container.
   *
   * @param id container id
   * @return path for the container
   * @see Paths#build
   */
  private String path(String id) {
    return Paths.build(Paths.CONTAINERS, id);
  }

}
TOP

Related Classes of org.springframework.xd.dirt.container.store.ZooKeeperContainerRepository

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.