package org.apache.blur.store.hdfs;
/**
* 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.
*/
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.blur.log.Log;
import org.apache.blur.log.LogFactory;
import org.apache.blur.trace.Trace;
import org.apache.blur.trace.Tracer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathFilter;
import org.apache.lucene.store.Lock;
import org.apache.lucene.store.LockFactory;
public class BlurLockFactory extends LockFactory {
private static final Log LOG = LogFactory.getLog(BlurLockFactory.class);
private final Configuration _configuration;
private final String _baseLockKey;
private final Path _dir;
private final FileSystem _fileSystem;
public BlurLockFactory(Configuration configuration, Path dir, String host, String pid) throws IOException {
_configuration = configuration;
_dir = dir;
_baseLockKey = host + "/" + pid;
_fileSystem = _dir.getFileSystem(_configuration);
_fileSystem.mkdirs(_dir);
}
static class BlurLock extends Lock {
private static final String UTF_8 = "UTF-8";
private final String _lockName;
private final Path _dir;
private final String _baseLockKey;
private final FileSystem _fileSystem;
private boolean _set;
private long _id;
BlurLock(FileSystem fileSystem, Path dir, String lockName, String baseLockKey) {
_fileSystem = fileSystem;
_dir = dir;
_lockName = lockName;
_baseLockKey = baseLockKey;
}
@Override
public boolean obtain() throws IOException {
Tracer trace = Trace.trace("filesystem - obtain", Trace.param("dir", _dir), Trace.param("lockName", _lockName));
try {
if (_set) {
throw new IOException("Lock for [" + _baseLockKey + "] can only be set once.");
}
try {
long id = findNextLockId();
try {
LOG.info("Writing lock [{0}] with id [{1}] for [{2}]", _lockName, id, _dir);
FSDataOutputStream outputStream = _fileSystem.create(getLockPath(id), false);
outputStream.write(_baseLockKey.getBytes(UTF_8));
outputStream.close();
_id = id;
cleanupOldLocks();
return true;
} catch (IOException e) {
return false;
}
} finally {
_set = true;
}
} finally {
trace.done();
}
}
private void cleanupOldLocks() throws IOException {
FileStatus[] fileStatusForLock = getFileStatusForLock(_fileSystem, _dir, _lockName);
for (FileStatus fileStatus : fileStatusForLock) {
long id = getId(fileStatus);
if (id < _id) {
LOG.info("Deleting old lock [{0}] with id [{1}] for [{2}]", _lockName, id, _dir);
_fileSystem.delete(getLockPath(id), false);
}
}
}
private Path getLockPath(long id) {
return new Path(_dir, _lockName + "." + id);
}
private long findNextLockId() throws IOException {
FileStatus[] listStatus = getFileStatusForLock(_fileSystem, _dir, _lockName);
if (listStatus == null || listStatus.length == 0) {
return 0L;
}
long largestId = -1L;
for (FileStatus fileStatus : listStatus) {
long id = getId(fileStatus);
if (id > largestId) {
largestId = id;
}
}
return largestId + 1L;
}
private long getId(FileStatus fileStatus) {
String name = fileStatus.getPath().getName();
int lastIndexOf = name.lastIndexOf('.');
long id = Long.parseLong(name.substring(lastIndexOf + 1));
return id;
}
@Override
public boolean isLocked() throws IOException {
Tracer trace = Trace.trace("filesystem - isLocked", Trace.param("dir", _dir), Trace.param("lockName", _lockName));
try {
// test my file
Path myLockPath = getLockPath(_id);
Path nextLockPath = getLockPath(_id + 1);
boolean e1 = _fileSystem.exists(myLockPath);
if (!e1) {
LOG.info("Lock lost [{0}] with id [{1}] for [{2}]", _lockName, _id, _dir);
return false;
}
// test next file
boolean e2 = _fileSystem.exists(nextLockPath);
if (e2) {
LOG.info("Lock lost [{0}] with id [{1}] for [{2}]", _lockName, _id, _dir);
return false;
}
return true;
} finally {
trace.done();
}
}
@Override
public void release() throws IOException {
Tracer trace = Trace.trace("filesystem - release", Trace.param("dir", _dir), Trace.param("lockName", _lockName));
try {
LOG.info("Releasing Lock [{0}] with id [{1}] for [{2}]", _lockName, _id, _dir);
_fileSystem.delete(getLockPath(_id), false);
} finally {
trace.done();
}
}
}
@Override
public Lock makeLock(String lockName) {
return new BlurLock(_fileSystem, _dir, lockName, _baseLockKey);
}
@Override
public void clearLock(String lockName) throws IOException {
LOG.info("Clearing lock [{0}] with for [{2}]", lockName, _dir);
FileStatus[] fileStatusForLock = getFileStatusForLock(_fileSystem, _dir, lockName);
for (FileStatus fileStatus : fileStatusForLock) {
_fileSystem.delete(fileStatus.getPath(), false);
}
}
private static FileStatus[] getFileStatusForLock(FileSystem fileSystem, Path dir, final String lockName)
throws FileNotFoundException, IOException {
FileStatus[] listStatus = fileSystem.listStatus(dir, new PathFilter() {
@Override
public boolean accept(Path path) {
return path.getName().startsWith(lockName + ".");
}
});
return listStatus;
}
}