/** * Copyright 2012 LiveRamp * * 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.liveramp.hank.coordinator.zk; import com.liveramp.hank.coordinator.*; import com.liveramp.hank.generated.DomainVersionMetadata; import com.liveramp.hank.generated.PartitionMetadata; import com.liveramp.hank.zookeeper.WatchedThriftNode; import com.liveramp.hank.zookeeper.ZkPath; import com.liveramp.hank.zookeeper.ZooKeeperPlus; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class ZkDomainVersion extends AbstractDomainVersion implements DomainVersion { final WatchedThriftNode<DomainVersionMetadata> metadata; private final int versionNumber; private final ZooKeeperPlus zk; private final String path; private final DomainVersionPropertiesSerialization domainVersionPropertiesSerialization; public static ZkDomainVersion create(ZooKeeperPlus zk, String domainPath, int versionNumber, DomainVersionProperties domainVersionProperties, DomainVersionPropertiesSerialization domainVersionPropertiesSerialization) throws InterruptedException, KeeperException, IOException { String versionPath = ZkPath.append(domainPath, ZkDomain.VERSIONS_PATH, getPathName(versionNumber)); DomainVersionMetadata initialMetadata = new DomainVersionMetadata(null, new HashMap<Integer, PartitionMetadata>(), false, 0); setProperties(domainVersionPropertiesSerialization, domainVersionProperties, initialMetadata); return new ZkDomainVersion(zk, versionPath, domainVersionPropertiesSerialization, true, initialMetadata); } public ZkDomainVersion(ZooKeeperPlus zk, String path, DomainVersionPropertiesSerialization domainVersionPropertiesSerialization) throws InterruptedException, KeeperException { this(zk, path, domainVersionPropertiesSerialization, false, null); } public ZkDomainVersion(ZooKeeperPlus zk, String path, DomainVersionPropertiesSerialization domainVersionPropertiesSerialization, boolean create, DomainVersionMetadata initialMetadata) throws KeeperException, InterruptedException { this.zk = zk; this.path = path; this.domainVersionPropertiesSerialization = domainVersionPropertiesSerialization; this.versionNumber = Integer.parseInt(ZkPath.getFilename(path)); metadata = new WatchedThriftNode<DomainVersionMetadata>(zk, path, true, create ? CreateMode.PERSISTENT : null, initialMetadata, new DomainVersionMetadata()); } @Override public int getVersionNumber() { return versionNumber; } @Override public Long getClosedAt() throws IOException { long result = metadata.get().get_closed_at(); if (result <= 0) { return null; } else { return result; } } @Override public void close() throws IOException { try { metadata.update(metadata.new Updater() { @Override public void updateCopy(DomainVersionMetadata currentCopy) { currentCopy.set_closed_at(System.currentTimeMillis()); } }); } catch (InterruptedException e) { throw new IOException(e); } catch (KeeperException e) { throw new IOException(e); } } @Override public void cancel() throws IOException { if (!DomainVersions.isClosed(this)) { try { zk.deleteNodeRecursively(path); } catch (Exception e) { throw new IOException(e); } } } @Override public Collection<PartitionMetadata> getPartitionsMetadata() throws IOException { Map<Integer, PartitionMetadata> result = metadata.get().get_partitions(); if (result == null) { return Collections.emptyList(); } else { return result.values(); } } @Override public void addPartitionProperties(final int partNum, final long numBytes, final long numRecords) throws IOException { try { metadata.update(metadata.new Updater() { @Override public void updateCopy(DomainVersionMetadata currentCopy) { Map<Integer, PartitionMetadata> partitionsMetadata = currentCopy.get_partitions(); if (partitionsMetadata == null) { currentCopy.set_partitions(new HashMap<Integer, PartitionMetadata>()); } currentCopy.get_partitions().put(partNum, new PartitionMetadata(numBytes, numRecords)); } }); } catch (InterruptedException e) { throw new IOException(e); } catch (KeeperException e) { throw new IOException(e); } } @Override public boolean isDefunct() throws IOException { return metadata.get().is_defunct(); } @Override public void setDefunct(final boolean isDefunct) throws IOException { try { metadata.update(metadata.new Updater() { @Override public void updateCopy(DomainVersionMetadata currentCopy) { currentCopy.set_defunct(isDefunct); } }); } catch (InterruptedException e) { throw new IOException(e); } catch (KeeperException e) { throw new IOException(e); } } @Override public DomainVersionProperties getProperties() throws IOException { byte[] serializedProperties = metadata.get().get_properties(); if (serializedProperties == null || domainVersionPropertiesSerialization == null) { return null; } else { return domainVersionPropertiesSerialization.deserializeProperties(serializedProperties); } } private static void setProperties(final DomainVersionPropertiesSerialization domainVersionPropertiesSerialization, final DomainVersionProperties properties, final DomainVersionMetadata metadata) { if (properties != null && domainVersionPropertiesSerialization == null) { throw new RuntimeException("Cannot set properties when the given properties serialization is null."); } if (properties == null) { metadata.set_properties((byte[]) null); } else { try { metadata.set_properties(domainVersionPropertiesSerialization.serializeProperties(properties)); } catch (IOException e) { throw new RuntimeException(e); } } } @Override public void setProperties(final DomainVersionProperties properties) throws IOException { try { metadata.update(metadata.new Updater() { @Override public void updateCopy(DomainVersionMetadata currentCopy) { setProperties(domainVersionPropertiesSerialization, properties, currentCopy); } }); } catch (InterruptedException e) { throw new IOException(e); } catch (KeeperException e) { throw new IOException(e); } } public static String getPathName(int versionNumber) { return Integer.toString(versionNumber); } public boolean delete() throws IOException { try { zk.delete(path, -1); return true; } catch (Exception e) { throw new IOException(e); } } }