    001/*
    002 * Copyright (c) 2016-2017 Chris K Wensel <chris@wensel.net>. All Rights Reserved.
    003 * Copyright (c) 2007-2017 Xplenty, Inc. All Rights Reserved.
    004 *
    005 * Project and contact information: http://www.cascading.org/
    006 *
    007 * This file is part of the Cascading project.
    008 *
    009 * Licensed under the Apache License, Version 2.0 (the "License");
    010 * you may not use this file except in compliance with the License.
    011 * You may obtain a copy of the License at
    012 *
    013 *     http://www.apache.org/licenses/LICENSE-2.0
    014 *
    015 * Unless required by applicable law or agreed to in writing, software
    016 * distributed under the License is distributed on an "AS IS" BASIS,
    017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    018 * See the License for the specific language governing permissions and
    019 * limitations under the License.
    020 */
    021
    022package cascading.tuple;
    023
    024import java.io.Closeable;
    025import java.io.Flushable;
    026import java.io.IOException;
    027
    028import cascading.flow.FlowProcess;
    029import cascading.scheme.ConcreteCall;
    030import cascading.scheme.Scheme;
    031import cascading.tap.Tap;
    032import cascading.tap.TapException;
    033
    034/**
    035 * Class TupleEntrySchemeCollector is a helper class for wrapping a {@link Scheme} instance, calling
    036 * {@link Scheme#sink(cascading.flow.FlowProcess, cascading.scheme.SinkCall)} on every call to {@link #add(TupleEntry)}
    037 * or {@link #add(Tuple)}.
    038 * <p/>
    039 * Use this class inside a custom {@link cascading.tap.Tap} when overriding the
    040 * {@link cascading.tap.Tap#openForWrite(cascading.flow.FlowProcess)} method.
    041 */
    042public class TupleEntrySchemeCollector<Config, Output> extends TupleEntryCollector
    043  {
    044  private final FlowProcess<? extends Config> flowProcess;
    045  private final Scheme scheme;
    046  private String loggableIdentifier;
    047
    048  protected final ConcreteCall<Object, Output> sinkCall;
    049  private boolean prepared = false;
    050
    051  @Deprecated
    052  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Scheme scheme )
    053    {
    054    this( flowProcess, scheme, null, null );
    055    }
    056
    057  @Deprecated
    058  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Scheme scheme, String loggableIdentifier )
    059    {
    060    this( flowProcess, scheme, null, loggableIdentifier );
    061    }
    062
    063  @Deprecated
    064  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Scheme scheme, Output output )
    065    {
    066    this( flowProcess, scheme, output, null );
    067    }
    068
    069  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Tap tap, Output output )
    070    {
    071    this( flowProcess, tap, tap.getScheme(), output, tap.getIdentifier() );
    072    }
    073
    074  /**
    075   * @param flowProcess
    076   * @param scheme
    077   * @param output
    078   * @param loggableIdentifier is only used for logging purposes
    079   */
    080  @Deprecated
    081  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Scheme scheme, Output output, String loggableIdentifier )
    082    {
    083    this( flowProcess, null, scheme, null, null );
    084    }
    085
    086  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Tap tap, Scheme scheme )
    087    {
    088    this( flowProcess, tap, scheme, null, null );
    089    }
    090
    091  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Tap tap, Scheme scheme, String loggableIdentifier )
    092    {
    093    this( flowProcess, tap, scheme, null, loggableIdentifier );
    094    }
    095
    096  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Tap tap, Scheme scheme, Output output )
    097    {
    098    this( flowProcess, tap, scheme, output, null );
    099    }
    100
    101  /**
    102   * @param flowProcess
    103   * @param scheme
    104   * @param output
    105   * @param loggableIdentifier is only used for logging purposes
    106   */
    107  public TupleEntrySchemeCollector( FlowProcess<? extends Config> flowProcess, Tap tap, Scheme scheme, Output output, String loggableIdentifier )
    108    {
    109    super( Fields.asDeclaration( scheme.getSinkFields() ) );
    110    this.flowProcess = flowProcess;
    111    this.scheme = scheme;
    112    this.loggableIdentifier = loggableIdentifier; // only used for logging
    113
    114    this.sinkCall = new ConcreteCall();
    115    this.sinkCall.setTap( tap );
    116    this.sinkCall.setOutgoingEntry( this.tupleEntry ); // created in super ctor
    117
    118    if( output != null )
    119      setOutput( output );
    120    }
    121
    122  protected FlowProcess<? extends Config> getFlowProcess()
    123    {
    124    return flowProcess;
    125    }
    126
    127  @Override
    128  public void setFields( Fields declared )
    129    {
    130    super.setFields( declared );
    131
    132    if( this.sinkCall != null )
    133      this.sinkCall.setOutgoingEntry( this.tupleEntry );
    134    }
    135
    136  protected Output getOutput()
    137    {
    138    return sinkCall.getOutput();
    139    }
    140
    141  protected void setOutput( Output output )
    142    {
    143    sinkCall.setOutput( wrapOutput( output ) );
    144    }
    145
    146  protected Output wrapOutput( Output output )
    147    {
    148    return output;
    149    }
    150
    151  /** Need to defer preparing the scheme till after the fields have been resolved */
    152  protected void prepare()
    153    {
    154    try
    155      {
    156      scheme.sinkPrepare( flowProcess, sinkCall );
    157      }
    158    catch( IOException exception )
    159      {
    160      throw new TapException( "could not prepare scheme", exception );
    161      }
    162
    163    prepared = true;
    164    }
    165
    166  @Override
    167  public void add( TupleEntry tupleEntry )
    168    {
    169    if( !prepared )
    170      prepare();
    171
    172    super.add( tupleEntry );
    173    }
    174
    175  @Override
    176  public void add( Tuple tuple )
    177    {
    178    if( !prepared ) // this is unfortunate
    179      prepare();
    180
    181    super.add( tuple );
    182    }
    183
    184  @Override
    185  protected void collect( TupleEntry tupleEntry ) throws IOException
    186    {
    187    sinkCall.setOutgoingEntry( tupleEntry );
    188
    189    try
    190      {
    191      scheme.sink( flowProcess, sinkCall );
    192      }
    193    catch( Exception exception )
    194      {
    195      if( loggableIdentifier == null || loggableIdentifier.isEmpty() )
    196        loggableIdentifier = "'unknown'";
    197
    198      throw new TupleException( "unable to sink into output identifier: " + loggableIdentifier, exception );
    199      }
    200    }
    201
    202  @Override
    203  public void close()
    204    {
    205    try
    206      {
    207      if( sinkCall == null )
    208        return;
    209
    210      try
    211        {
    212        if( prepared )
    213          scheme.sinkCleanup( flowProcess, sinkCall );
    214        }
    215      catch( IOException exception )
    216        {
    217        throw new TupleException( "unable to cleanup sink for output identifier: " + loggableIdentifier, exception );
    218        }
    219      }
    220    finally
    221      {
    222      try
    223        {
    224        if( getOutput() instanceof Flushable )
    225          ( (Flushable) getOutput() ).flush();
    226        }
    227      catch( IOException exception )
    228        {
    229        // do nothing
    230        }
    231
    232      try
    233        {
    234        if( getOutput() instanceof Closeable )
    235          ( (Closeable) getOutput() ).close();
    236        }
    237      catch( IOException exception )
    238        {
    239        // do nothing
    240        }
    241
    242      super.close();
    243      }
    244    }
    245  }