blob: bf84f4dce8867d0d5e8f1c4bc5485e03dd7cbaa3 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.sentry.hdfs;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.sentry.hdfs.service.thrift.TPathChanges;
import org.apache.sentry.hdfs.service.thrift.TPathsDump;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.sentry.hdfs.ServiceConstants.IMAGE_NUMBER_UPDATE_UNINITIALIZED;
import static org.apache.sentry.hdfs.ServiceConstants.SEQUENCE_NUMBER_UPDATE_UNINITIALIZED;
public class UpdateableAuthzPaths implements AuthzPaths, Updateable<PathsUpdate> {
private static final int MAX_UPDATES_PER_LOCK_USE = 99;
private static final String UPDATABLE_TYPE_NAME = "path_update";
private static final Logger LOG = LoggerFactory.getLogger(UpdateableAuthzPaths.class);
private volatile HMSPaths paths;
private final AtomicLong seqNum = new AtomicLong(SEQUENCE_NUMBER_UPDATE_UNINITIALIZED);
private final AtomicLong imgNum = new AtomicLong(IMAGE_NUMBER_UPDATE_UNINITIALIZED);
public UpdateableAuthzPaths(String[] pathPrefixes) {
this.paths = new HMSPaths(pathPrefixes);
}
UpdateableAuthzPaths(HMSPaths paths) {
this.paths = paths;
}
@Override
public boolean isUnderPrefix(String[] pathElements) {
return paths.isUnderPrefix(pathElements);
}
@Override
public Set<String> findAuthzObject(String[] pathElements) {
return paths.findAuthzObject(pathElements);
}
@Override
public Set<String> findAuthzObjectExactMatches(String[] pathElements) {
return paths.findAuthzObjectExactMatches(pathElements);
}
@Override
public UpdateableAuthzPaths updateFull(PathsUpdate update) {
UpdateableAuthzPaths other = getPathsDump().initializeFromDump(
update.toThrift().getPathsDump());
other.seqNum.set(update.getSeqNum());
other.imgNum.set(update.getImgNum());
return other;
}
@Override
public void updatePartial(Iterable<PathsUpdate> updates, ReadWriteLock lock) {
lock.writeLock().lock();
try {
int counter = 0;
for (PathsUpdate update : updates) {
applyPartialUpdate(update);
if (++counter > MAX_UPDATES_PER_LOCK_USE) {
counter = 0;
lock.writeLock().unlock();
lock.writeLock().lock();
}
seqNum.set(update.getSeqNum());
// Update the image ID only if the update has a new one
if (imgNum.get() < update.getImgNum()) {
imgNum.set(update.getImgNum());
}
LOG.debug("##### Updated paths seq Num [{}] img Num [{}]", seqNum.get(), imgNum.get());
}
} finally {
lock.writeLock().unlock();
}
}
private void applyPartialUpdate(PathsUpdate update) {
// Handle alter table rename : will have exactly 2 path changes
// 1 is add path and the other is del path and oldName != newName
if (update.getPathChanges().size() == 2) {
List<TPathChanges> pathChanges = update.getPathChanges();
TPathChanges newPathInfo = null;
TPathChanges oldPathInfo = null;
if (pathChanges.get(0).getAddPathsSize() == 1
&& pathChanges.get(1).getDelPathsSize() == 1) {
newPathInfo = pathChanges.get(0);
oldPathInfo = pathChanges.get(1);
} else if (pathChanges.get(1).getAddPathsSize() == 1
&& pathChanges.get(0).getDelPathsSize() == 1) {
newPathInfo = pathChanges.get(1);
oldPathInfo = pathChanges.get(0);
}
if (newPathInfo != null && oldPathInfo != null &&
!newPathInfo.getAuthzObj().equalsIgnoreCase(oldPathInfo.getAuthzObj())) {
LOG.info("Renaming Object:{} to {}", oldPathInfo.getAuthzObj(), newPathInfo.getAuthzObj());
paths.renameAuthzObject(
oldPathInfo.getAuthzObj(), oldPathInfo.getDelPaths(),
newPathInfo.getAuthzObj(), newPathInfo.getAddPaths());
return;
}
}
List<TPathChanges> deletePathChanges = new ArrayList<TPathChanges>();
List<TPathChanges> addPathChanges = new ArrayList<TPathChanges>();
for (TPathChanges pathChanges : update.getPathChanges()) {
if (pathChanges.getDelPaths() != null && pathChanges.getDelPaths().size() != 0) {
deletePathChanges.add(pathChanges);
}
if (pathChanges.getAddPaths() != null && pathChanges.getAddPaths().size() != 0) {
addPathChanges.add(pathChanges);
}
}
for (TPathChanges pathChanges : deletePathChanges) {
List<List<String>> delPaths = pathChanges.getDelPaths();
if (delPaths.size() == 1 && delPaths.get(0).size() == 1
&& delPaths.get(0).get(0).equals(PathsUpdate.ALL_PATHS)) {
// Remove all paths.. eg. drop table
LOG.info("Applying Path update. Deleting all paths for authz obj {}", pathChanges.getAuthzObj());
paths.deleteAuthzObject(pathChanges.getAuthzObj());
} else {
LOG.info("Applying Path update. Deleting path for authz object: {} authz path: {}",
pathChanges.getAuthzObj(), pathChanges.getDelPaths());
paths.deletePathsFromAuthzObject(pathChanges.getAuthzObj(), pathChanges
.getDelPaths());
}
}
for (TPathChanges pathChanges : addPathChanges) {
LOG.info("Applying Path update. Adding path for authz object {} authz path {}",
pathChanges.getAuthzObj(), pathChanges.getAddPaths());
applyAddChanges(pathChanges.getAuthzObj(), pathChanges.getAddPaths());
}
}
public void applyAddChanges(String objName, List<List<String>> changes) {
paths.addPathsToAuthzObject(objName, changes, true);
}
@Override
public long getLastUpdatedSeqNum() {
return seqNum.get();
}
@Override
public long getLastUpdatedImgNum() {
return imgNum.get();
}
@Override
public PathsUpdate createFullImageUpdate(long currSeqNum) {
PathsUpdate pathsUpdate = new PathsUpdate(currSeqNum, true);
pathsUpdate.toThrift().setPathsDump(getPathsDump().createPathsDump(true));
return pathsUpdate;
}
@Override
public AuthzPathsDumper<UpdateableAuthzPaths> getPathsDump() {
return new AuthzPathsDumper<UpdateableAuthzPaths>() {
@Override
public TPathsDump createPathsDump(boolean minimizeSize) {
return UpdateableAuthzPaths.this.paths.getPathsDump().
createPathsDump(minimizeSize);
}
@Override
public UpdateableAuthzPaths initializeFromDump(TPathsDump pathsDump) {
return new UpdateableAuthzPaths(UpdateableAuthzPaths.this.paths
.getPathsDump().initializeFromDump(pathsDump));
}
};
}
@Override
public String getUpdateableTypeName() {
return UPDATABLE_TYPE_NAME;
}
@Override
public String toString() {
return String.format("%s(%s, %s, %s)", getClass().getSimpleName(), seqNum, imgNum, paths);
}
@Override
public String getSequenceInfo() {
return String.format("%s(Path: Sequence Number %s, Image Number: %s)", getClass().getSimpleName(), seqNum, imgNum);
}
public String dumpContent() {
return String.format("%s(%s, %s) ", getClass().getSimpleName(), seqNum, imgNum) + paths.dumpContent();
}
}