/* * 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.KeeperException; 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.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; /** * Paging support for this repository. */ private final PagingUtility<Container> pagingUtility = new PagingUtility<Container>(); /** * 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 */ // @Autowired // public ZooKeeperContainerRepository/*(ZooKeeperConnection zkConnection*/) { // this.zkConnection = zkConnection; // } /** * {@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 { // if (client.checkExists().forPath(path) != null) { // // if this container disconnected from ZooKeeper // // and reconnects, the ephemeral node may not // // have been cleaned up yet...this can happen // // in cases where the machine running both the // // container and ZooKeeper goes to sleep // client.delete().forPath(path); // } // } // catch (Exception e) { // // trapping the case where the ephemeral node exists // // but is removed by ZK before this container gets // // the chance to remove it // ZooKeeperUtils.wrapAndThrowIgnoring(e, KeeperException.NoNodeException.class); // } // // try { // client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL) // .forPath(path, ZooKeeperUtils.mapToBytes(entity.getAttributes())); // return entity; // } // catch (Exception e) { // throw ZooKeeperUtils.wrapThrowable(e); // } return null; } /** * {@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; return null; } /** * {@inheritDoc} */ @Override public boolean exists(String id) { //return ensureCache().getCurrentData(path(id)) != null; return true; } /** * {@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; } /** * {@inheritDoc} */ @Override public long count() { //return ensureCache().getCurrentData().size(); return 0; } /** * {@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); // } }