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.TableName;
024import org.apache.hadoop.hbase.TableNotDisabledException;
025import org.apache.hadoop.hbase.TableNotEnabledException;
026import org.apache.hadoop.hbase.TableNotFoundException;
027import org.apache.hadoop.hbase.UnknownRegionException;
028import org.apache.hadoop.hbase.client.DoNotRetryRegionException;
029import org.apache.hadoop.hbase.client.RegionInfo;
030import org.apache.hadoop.hbase.client.TableState;
031import org.apache.hadoop.hbase.master.MasterServices;
032import org.apache.hadoop.hbase.master.TableStateManager;
033import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
034import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
035import org.apache.hadoop.hbase.security.User;
036import org.apache.hadoop.hbase.util.CommonFSUtils;
037import org.apache.yetus.audience.InterfaceAudience;
038
039/**
040 * Base class for all the Table procedures that want to use a StateMachineProcedure.
041 * It provides helpers like basic locking, sync latch, and toStringClassDetails().
042 */
043@InterfaceAudience.Private
044public abstract class AbstractStateMachineTableProcedure<TState>
045    extends StateMachineProcedure<MasterProcedureEnv, TState>
046    implements TableProcedureInterface {
047
048  // used for compatibility with old clients
049  private final ProcedurePrepareLatch syncLatch;
050
051  private User user;
052
053  protected AbstractStateMachineTableProcedure() {
054    // Required by the Procedure framework to create the procedure on replay
055    syncLatch = null;
056  }
057
058  protected AbstractStateMachineTableProcedure(final MasterProcedureEnv env) {
059    this(env, null);
060  }
061
062  /**
063   * @param env Uses this to set Procedure Owner at least.
064   */
065  protected AbstractStateMachineTableProcedure(final MasterProcedureEnv env,
066      final ProcedurePrepareLatch latch) {
067    if (env != null) {
068      this.user = env.getRequestUser();
069      this.setOwner(user);
070    }
071    // used for compatibility with clients without procedures
072    // they need a sync TableExistsException, TableNotFoundException, TableNotDisabledException, ...
073    this.syncLatch = latch;
074  }
075
076  @Override
077  public abstract TableName getTableName();
078
079  @Override
080  public abstract TableOperationType getTableOperationType();
081
082  @Override
083  public void toStringClassDetails(final StringBuilder sb) {
084    sb.append(getClass().getSimpleName());
085    sb.append(" table=");
086    sb.append(getTableName());
087  }
088
089  @Override
090  protected boolean waitInitialized(MasterProcedureEnv env) {
091    return env.waitInitialized(this);
092  }
093
094  @Override
095  protected LockState acquireLock(final MasterProcedureEnv env) {
096    if (env.getProcedureScheduler().waitTableExclusiveLock(this, getTableName())) {
097      return LockState.LOCK_EVENT_WAIT;
098    }
099    return LockState.LOCK_ACQUIRED;
100  }
101
102  @Override
103  protected void releaseLock(final MasterProcedureEnv env) {
104    env.getProcedureScheduler().wakeTableExclusiveLock(this, getTableName());
105  }
106
107  protected User getUser() {
108    return user;
109  }
110
111  protected void setUser(final User user) {
112    this.user = user;
113  }
114
115  protected void releaseSyncLatch() {
116    ProcedurePrepareLatch.releaseLatch(syncLatch, this);
117  }
118
119  /**
120   * Check whether a table is modifiable - exists and either offline or online with config set
121   * @param env MasterProcedureEnv
122   */
123  protected void checkTableModifiable(final MasterProcedureEnv env) throws IOException {
124    // Checks whether the table exists
125    if (!env.getMasterServices().getTableDescriptors().exists(getTableName())) {
126      throw new TableNotFoundException(getTableName());
127    }
128  }
129
130  protected final Path getWALRegionDir(MasterProcedureEnv env, RegionInfo region)
131      throws IOException {
132    return CommonFSUtils.getWALRegionDir(env.getMasterConfiguration(),
133        region.getTable(), region.getEncodedName());
134  }
135
136  /**
137   * Check that cluster is up and master is running. Check table is modifiable.
138   * If <code>enabled</code>, check table is enabled else check it is disabled.
139   * Call in Procedure constructor so can pass any exception to caller.
140   * @param enabled If true, check table is enabled and throw exception if not. If false, do the
141   *                inverse. If null, do no table checks.
142   */
143  protected void preflightChecks(MasterProcedureEnv env, Boolean enabled) throws HBaseIOException {
144    MasterServices master = env.getMasterServices();
145    if (!master.isClusterUp()) {
146      throw new HBaseIOException("Cluster not up!");
147    }
148    if (master.isStopping() || master.isStopped()) {
149      throw new HBaseIOException("Master stopping=" + master.isStopping() +
150          ", stopped=" + master.isStopped());
151    }
152    if (enabled == null) {
153      // Don't do any table checks.
154      return;
155    }
156    try {
157      // Checks table exists and is modifiable.
158      checkTableModifiable(env);
159      TableName tn = getTableName();
160      TableStateManager tsm = master.getTableStateManager();
161      TableState ts = tsm.getTableState(tn);
162      if (enabled) {
163        if (!ts.isEnabledOrEnabling()) {
164          throw new TableNotEnabledException(tn);
165        }
166      } else {
167        if (!ts.isDisabledOrDisabling()) {
168          throw new TableNotDisabledException(tn);
169        }
170      }
171    } catch (IOException ioe) {
172      if (ioe instanceof HBaseIOException) {
173        throw (HBaseIOException)ioe;
174      }
175      throw new HBaseIOException(ioe);
176    }
177  }
178
179  protected boolean isTableEnabled(MasterProcedureEnv env) {
180    return env.getMasterServices().getTableStateManager().isTableState(getTableName(),
181      TableState.State.ENABLED);
182  }
183
184  /**
185   * Check region is online.
186   */
187  protected static void checkOnline(MasterProcedureEnv env, RegionInfo ri)
188      throws DoNotRetryRegionException {
189    RegionStateNode regionNode =
190      env.getAssignmentManager().getRegionStates().getRegionStateNode(ri);
191    if (regionNode == null) {
192      throw new UnknownRegionException("No RegionState found for " + ri.getEncodedName());
193    }
194    regionNode.checkOnline();
195  }
196}