001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master.procedure;
019
020import java.io.IOException;
021import org.apache.hadoop.fs.Path;
022import org.apache.hadoop.hbase.HBaseIOException;
023import org.apache.hadoop.hbase.MetaTableAccessor;
024import org.apache.hadoop.hbase.TableName;
025import org.apache.hadoop.hbase.TableNotDisabledException;
026import org.apache.hadoop.hbase.TableNotEnabledException;
027import org.apache.hadoop.hbase.TableNotFoundException;
028import org.apache.hadoop.hbase.UnknownRegionException;
029import org.apache.hadoop.hbase.client.DoNotRetryRegionException;
030import org.apache.hadoop.hbase.client.RegionInfo;
031import org.apache.hadoop.hbase.client.RegionOfflineException;
032import org.apache.hadoop.hbase.client.TableState;
033import org.apache.hadoop.hbase.master.MasterServices;
034import org.apache.hadoop.hbase.master.RegionState;
035import org.apache.hadoop.hbase.master.TableStateManager;
036import org.apache.hadoop.hbase.master.assignment.RegionStates;
037import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
038import org.apache.hadoop.hbase.security.User;
039import org.apache.hadoop.hbase.util.FSUtils;
040import org.apache.yetus.audience.InterfaceAudience;
041
042/**
043 * Base class for all the Table procedures that want to use a StateMachineProcedure.
044 * It provides helpers like basic locking, sync latch, and toStringClassDetails().
045 */
046@InterfaceAudience.Private
047public abstract class AbstractStateMachineTableProcedure<TState>
048    extends StateMachineProcedure<MasterProcedureEnv, TState>
049    implements TableProcedureInterface {
050
051  // used for compatibility with old clients
052  private final ProcedurePrepareLatch syncLatch;
053
054  private User user;
055
056  protected AbstractStateMachineTableProcedure() {
057    // Required by the Procedure framework to create the procedure on replay
058    syncLatch = null;
059  }
060
061  protected AbstractStateMachineTableProcedure(final MasterProcedureEnv env) {
062    this(env, null);
063  }
064
065  /**
066   * @param env Uses this to set Procedure Owner at least.
067   */
068  protected AbstractStateMachineTableProcedure(final MasterProcedureEnv env,
069      final ProcedurePrepareLatch latch) {
070    if (env != null) {
071      this.user = env.getRequestUser();
072      this.setOwner(user);
073    }
074    // used for compatibility with clients without procedures
075    // they need a sync TableExistsException, TableNotFoundException, TableNotDisabledException, ...
076    this.syncLatch = latch;
077  }
078
079  @Override
080  public abstract TableName getTableName();
081
082  @Override
083  public abstract TableOperationType getTableOperationType();
084
085  @Override
086  public void toStringClassDetails(final StringBuilder sb) {
087    sb.append(getClass().getSimpleName());
088    sb.append(" table=");
089    sb.append(getTableName());
090  }
091
092  @Override
093  protected boolean waitInitialized(MasterProcedureEnv env) {
094    return env.waitInitialized(this);
095  }
096
097  @Override
098  protected LockState acquireLock(final MasterProcedureEnv env) {
099    if (env.getProcedureScheduler().waitTableExclusiveLock(this, getTableName())) {
100      return LockState.LOCK_EVENT_WAIT;
101    }
102    return LockState.LOCK_ACQUIRED;
103  }
104
105  @Override
106  protected void releaseLock(final MasterProcedureEnv env) {
107    env.getProcedureScheduler().wakeTableExclusiveLock(this, getTableName());
108  }
109
110  protected User getUser() {
111    return user;
112  }
113
114  protected void setUser(final User user) {
115    this.user = user;
116  }
117
118  protected void releaseSyncLatch() {
119    ProcedurePrepareLatch.releaseLatch(syncLatch, this);
120  }
121
122  /**
123   * Check whether a table is modifiable - exists and either offline or online with config set
124   * @param env MasterProcedureEnv
125   * @throws IOException
126   */
127  protected void checkTableModifiable(final MasterProcedureEnv env) throws IOException {
128    // Checks whether the table exists
129    if (!MetaTableAccessor.tableExists(env.getMasterServices().getConnection(), getTableName())) {
130      throw new TableNotFoundException(getTableName());
131    }
132  }
133
134  protected final Path getWALRegionDir(MasterProcedureEnv env, RegionInfo region)
135      throws IOException {
136    return FSUtils.getWALRegionDir(env.getMasterConfiguration(),
137        region.getTable(), region.getEncodedName());
138  }
139
140  /**
141   * Check that cluster is up and master is running. Check table is modifiable.
142   * If <code>enabled</code>, check table is enabled else check it is disabled.
143   * Call in Procedure constructor so can pass any exception to caller.
144   * @param enabled If true, check table is enabled and throw exception if not. If false, do the
145   *                inverse. If null, do no table checks.
146   */
147  protected void preflightChecks(MasterProcedureEnv env, Boolean enabled) throws HBaseIOException {
148    MasterServices master = env.getMasterServices();
149    if (!master.isClusterUp()) {
150      throw new HBaseIOException("Cluster not up!");
151    }
152    if (master.isStopping() || master.isStopped()) {
153      throw new HBaseIOException("Master stopping=" + master.isStopping() +
154          ", stopped=" + master.isStopped());
155    }
156    if (enabled == null) {
157      // Don't do any table checks.
158      return;
159    }
160    try {
161      // Checks table exists and is modifiable.
162      checkTableModifiable(env);
163      TableName tn = getTableName();
164      TableStateManager tsm = master.getTableStateManager();
165      TableState ts = tsm.getTableState(tn);
166      if (enabled) {
167        if (!ts.isEnabledOrEnabling()) {
168          throw new TableNotEnabledException(tn);
169        }
170      } else {
171        if (!ts.isDisabledOrDisabling()) {
172          throw new TableNotDisabledException(tn);
173        }
174      }
175    } catch (IOException ioe) {
176      if (ioe instanceof HBaseIOException) {
177        throw (HBaseIOException)ioe;
178      }
179      throw new HBaseIOException(ioe);
180    }
181  }
182
183  /**
184   * Check region is online.
185   */
186  protected static void checkOnline(MasterProcedureEnv env, final RegionInfo ri)
187      throws DoNotRetryRegionException {
188    RegionStates regionStates = env.getAssignmentManager().getRegionStates();
189    RegionState rs = regionStates.getRegionState(ri);
190    if (rs == null) {
191      throw new UnknownRegionException("No RegionState found for " + ri.getEncodedName());
192    }
193    if (!rs.isOpened()) {
194      throw new DoNotRetryRegionException(ri.getEncodedName() + " is not OPEN; regionState=" + rs);
195    }
196    if (ri.isSplitParent()) {
197      throw new DoNotRetryRegionException(ri.getEncodedName() +
198          " is not online (splitParent=true)");
199    }
200    if (ri.isSplit()) {
201      throw new DoNotRetryRegionException(ri.getEncodedName() + " has split=true");
202    }
203    if (ri.isOffline()) {
204      // RegionOfflineException is not instance of DNRIOE so wrap it.
205      throw new DoNotRetryRegionException(new RegionOfflineException(ri.getEncodedName()));
206    }
207  }
208}