Java tutorial
/* * Copyright 2014 EMC Corporation. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * or in the "license" file accompanying this file. This file 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.emc.vipr.sync.target; import com.emc.vipr.sync.ViPRSync; import com.emc.vipr.sync.filter.SyncFilter; import com.emc.vipr.sync.model.SyncObject; import com.emc.vipr.sync.source.CasSource; import com.emc.vipr.sync.source.SyncSource; import com.emc.vipr.sync.util.CasUtil; import com.emc.vipr.sync.util.ClipTag; import com.emc.vipr.sync.util.ConfigurationException; import com.emc.vipr.sync.util.TimingUtil; import com.filepool.fplibrary.*; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.log4j.LogMF; import org.apache.log4j.Logger; import org.springframework.util.Assert; import java.util.Iterator; import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * TODO: make this compatible with any source */ public class CasTarget extends SyncTarget { private static final Logger l4j = Logger.getLogger(CasTarget.class); protected static final String APPLICATION_NAME = CasTarget.class.getName(); protected static final String APPLICATION_VERSION = ViPRSync.class.getPackage().getImplementationVersion(); protected static final int CLIP_OPTIONS = 0; protected String connectionString; protected FPPool pool; @Override public boolean canHandleTarget(String targetUri) { return targetUri.matches(CasUtil.URI_PATTERN); } @Override public Options getCustomOptions() { return new Options(); } @Override protected void parseCustomOptions(CommandLine line) { Pattern p = Pattern.compile(CasUtil.URI_PATTERN); Matcher m = p.matcher(targetUri); if (!m.matches()) throw new ConfigurationException(String.format("%s does not match %s", targetUri, p)); connectionString = targetUri.replaceFirst("^" + CasUtil.URI_PREFIX, ""); } @Override public void configure(SyncSource source, Iterator<SyncFilter> filters, SyncTarget target) { if (!(source instanceof CasSource)) throw new ConfigurationException("CasTarget is currently only compatible with CasSource"); Assert.hasText(connectionString); try { FPPool.RegisterApplication(APPLICATION_NAME, APPLICATION_VERSION); // Check connection pool = new FPPool(connectionString); FPPool.PoolInfo info = pool.getPoolInfo(); LogMF.info(l4j, "Connected to target: {0} ({1}) using CAS v.{2}", info.getClusterName(), info.getClusterID(), info.getVersion()); // verify we have appropriate privileges if (pool.getCapability(FPLibraryConstants.FP_WRITE, FPLibraryConstants.FP_ALLOWED).equals("False")) throw new IllegalArgumentException("WRITE is not supported for this pool connection"); } catch (FPLibraryException e) { throw new RuntimeException("error creating pool", e); } } @Override public void filter(final SyncObject obj) { timeOperationStart(CasUtil.OPERATION_TOTAL); if (!(obj instanceof CasSource.ClipSyncObject)) throw new UnsupportedOperationException("sync object was not a CAS clip"); final CasSource.ClipSyncObject clipSync = (CasSource.ClipSyncObject) obj; FPClip clip = null; FPTag tag = null; int targetTagNum = 0; try { // first clone the clip via CDF raw write clip = TimingUtil.time(this, CasUtil.OPERATION_WRITE_CDF, new Callable<FPClip>() { @Override public FPClip call() throws Exception { return new FPClip(pool, clipSync.getRawSourceIdentifier(), clipSync.getInputStream(), CLIP_OPTIONS); } }); clipSync.setTargetIdentifier(clipSync.getRawSourceIdentifier()); // next write the blobs for (ClipTag sourceTag : clipSync.getTags()) { tag = clip.FetchNext(); // this should sync the tag indexes if (sourceTag.isBlobAttached()) { // only stream if the tag has a blob timedStreamBlob(tag, sourceTag); } tag.Close(); tag = null; } final FPClip fClip = clip; String destClipId = TimingUtil.time(this, CasUtil.OPERATION_WRITE_CLIP, new Callable<String>() { @Override public String call() throws Exception { return fClip.Write(); } }); if (!destClipId.equals(clipSync.getRawSourceIdentifier())) throw new RuntimeException(String.format("clip IDs do not match\n [%s != %s]", clipSync.getRawSourceIdentifier(), destClipId)); LogMF.debug(l4j, "Wrote source {0} to dest {1}", clipSync.getSourceIdentifier(), clipSync.getTargetIdentifier()); timeOperationComplete(CasUtil.OPERATION_TOTAL); } catch (Throwable t) { timeOperationFailed(CasUtil.OPERATION_TOTAL); if (t instanceof RuntimeException) throw (RuntimeException) t; throw new RuntimeException("Failed to store object: " + t.getMessage(), t); } finally { // close current tag ref try { if (tag != null) tag.Close(); } catch (Throwable t) { l4j.warn("could not close tag " + clipSync.getRawSourceIdentifier() + "." + targetTagNum, t); } // close clip try { if (clip != null) clip.Close(); } catch (Throwable t) { l4j.warn("could not close clip " + clipSync.getRawSourceIdentifier(), t); } } } @Override public void cleanup() { super.cleanup(); if (pool != null) try { pool.Close(); } catch (Throwable t) { l4j.warn("could not close pool: " + t.getMessage()); } pool = null; } @Override public String getName() { return "CAS Target"; } @Override public String getDocumentation() { return "The CAS target plugin is triggered by the target pattern:\n" + "cas://host[:port][,host[:port]...]?name=<name>,secret=<secret>\n" + "or cas://host[:port][,host[:port]...]?<pea_file>\n" + "Note that <name> should be of the format <subtenant_id>:<uid>. " + "This is passed to the CAS API as the connection string " + "(you can use primary=, secondary=, etc. in the server hints).\n" + "When used with CasSource, clips are transferred using their " + "raw CDFs to facilitate transparent data migration."; } public String getConnectionString() { return connectionString; } public void setConnectionString(String connectionString) { this.connectionString = connectionString; } protected void timedStreamBlob(final FPTag tag, final ClipTag blob) throws Exception { TimingUtil.time(CasTarget.this, CasUtil.OPERATION_STREAM_BLOB, new Callable<Void>() { @Override public Void call() throws Exception { blob.writeToTag(tag); return null; } }); } }