/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.uberfire.io.impl.cluster;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.uberfire.commons.async.DescriptiveRunnable;
import org.uberfire.commons.async.SimpleAsyncExecutorService;
import org.uberfire.commons.cluster.ClusterService;
import org.uberfire.commons.cluster.ClusterServiceFactory;
import org.uberfire.commons.cluster.LockExecuteNotifySyncReleaseTemplate;
import org.uberfire.commons.data.Pair;
import org.uberfire.commons.lifecycle.PriorityDisposableRegistry;
import org.uberfire.commons.lock.LockExecuteReleaseTemplate;
import org.uberfire.commons.message.AsyncCallback;
import org.uberfire.commons.message.MessageHandler;
import org.uberfire.commons.message.MessageHandlerResolver;
import org.uberfire.commons.message.MessageType;
import org.uberfire.io.IOService;
import org.uberfire.io.impl.IOServiceLockable;
import org.uberfire.java.nio.IOException;
import org.uberfire.java.nio.base.FileSystemId;
import org.uberfire.java.nio.base.FileSystemState;
import org.uberfire.java.nio.base.FileSystemStateAware;
import org.uberfire.java.nio.base.SeekableByteChannelWrapperImpl;
import org.uberfire.java.nio.channels.SeekableByteChannel;
import org.uberfire.java.nio.file.AtomicMoveNotSupportedException;
import org.uberfire.java.nio.file.CopyOption;
import org.uberfire.java.nio.file.DeleteOption;
import org.uberfire.java.nio.file.DirectoryNotEmptyException;
import org.uberfire.java.nio.file.DirectoryStream;
import org.uberfire.java.nio.file.FileAlreadyExistsException;
import org.uberfire.java.nio.file.FileSystem;
import org.uberfire.java.nio.file.FileSystemAlreadyExistsException;
import org.uberfire.java.nio.file.FileSystemNotFoundException;
import org.uberfire.java.nio.file.NoSuchFileException;
import org.uberfire.java.nio.file.NotDirectoryException;
import org.uberfire.java.nio.file.OpenOption;
import org.uberfire.java.nio.file.Option;
import org.uberfire.java.nio.file.Path;
import org.uberfire.java.nio.file.ProviderNotFoundException;
import org.uberfire.java.nio.file.attribute.FileAttribute;
import org.uberfire.java.nio.file.attribute.FileAttributeView;
import org.uberfire.java.nio.file.attribute.FileTime;
import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull;
import static org.uberfire.commons.validation.Preconditions.checkInstanceOf;
import static org.uberfire.io.impl.cluster.ClusterMessageType.NEW_FS;
import static org.uberfire.io.impl.cluster.ClusterMessageType.QUERY_FOR_FS;
import static org.uberfire.io.impl.cluster.ClusterMessageType.QUERY_FOR_FS_RESULT;
import static org.uberfire.io.impl.cluster.ClusterMessageType.SYNC_FS;
public class IOServiceClusterImpl implements IOService {
private static final Logger logger = LoggerFactory.getLogger(IOServiceClusterImpl.class);
protected final Set<String> batchFileSystems = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
protected IOServiceLockable service;
protected ClusterService clusterService;
private NewFileSystemListener newFileSystemListener = null;
IOServiceClusterImpl() {
}
public IOServiceClusterImpl(final IOService service,
final ClusterServiceFactory clusterServiceFactory) {
this(service,
clusterServiceFactory,
true);
}
public IOServiceClusterImpl(final IOService service,
final ClusterServiceFactory clusterServiceFactory,
final boolean autoStart) {
checkNotNull("clusterServiceFactory",
clusterServiceFactory);
this.service = checkInstanceOf("service",
service,
IOServiceLockable.class);
logger.debug("Creating instance of cluster service with auto start {}",
autoStart);
this.clusterService = clusterServiceFactory.build(new MessageHandlerResolver() {
final MessageHandler newFs = new NewFileSystemMessageHandler();
final MessageHandler syncFs = new SyncFileSystemMessageHandler();
final MessageHandler queryFs = new QueryFileSystemMessageHandler();
@Override
public String getServiceId() {
return IOServiceClusterImpl.this.service.getId();
}
@Override
public MessageHandler resolveHandler(final String serviceId,
final MessageType type) {
if (serviceId.equals(IOServiceClusterImpl.this.service.getId())) {
if (NEW_FS.equals(type)) {
return newFs;
} else if (SYNC_FS.equals(type)) {
return syncFs;
} else if (QUERY_FOR_FS.equals(type)) {
return queryFs;
}
}
return null;
}
});
PriorityDisposableRegistry.register(this);
start();
}
private void start() {
logger.debug("Starting IO Cluster service {}",
this);
//New cluster members are executed within locked
new LockExecuteReleaseTemplate<Void>().execute(clusterService,
new FutureTask<Void>(new Callable<Void>() {
@Override
public Void call() throws Exception {
// 10 seconds
int timeout = 10000;
final AtomicBoolean msgAnsweredOrTimedout = new AtomicBoolean(false);
final AtomicBoolean onSync = new AtomicBoolean(false);
final Map<Integer, FileSystemInfo> fileSystems = new HashMap<Integer, FileSystemInfo>();
clusterService.broadcastAndWait(service.getId(),
QUERY_FOR_FS,
Collections.<String, String>emptyMap(),
timeout,
new AsyncCallback() {
@Override
public void onTimeOut() {
msgAnsweredOrTimedout.set(true);
}
@Override
public void onReply(final MessageType type,
final Map<String, String> content) {
if (msgAnsweredOrTimedout.get() || onSync.get()) {
return;
}
onSync.set(true);
SimpleAsyncExecutorService.getUnmanagedInstance().execute(new DescriptiveRunnable() {
@Override
public String getDescription() {
return "Cluster Messaging Reply [" + service.getId() + "/QUERY_FOR_FS]";
}
@Override
public void run() {
for (final Map.Entry<String, String> entry : content.entrySet()) {
if (entry.getKey().startsWith("fs_")) {
int index = Integer.valueOf(entry.getKey().substring(entry.getKey().lastIndexOf("_") + 1));
if (!fileSystems.containsKey(index)) {
fileSystems.put(index,
new FileSystemInfo());
}
final FileSystemInfo fsInfo = fileSystems.get(index);
if (entry.getKey().startsWith("fs_id_")) {
fsInfo.setId(entry.getValue());
} else if (entry.getKey().startsWith("fs_scheme_")) {
fsInfo.setScheme(entry.getValue());
} else if (entry.getKey().startsWith("fs_uri_")) {
fsInfo.setUri(entry.getValue());
}
}
}
for (final FileSystemInfo fileSystemInfo : new HashSet<FileSystemInfo>(fileSystems.values())) {
try {
final URI newFS = URI.create(fileSystemInfo.getScheme() + "://" + fileSystemInfo.getId());
service.newFileSystem(newFS,
Collections.<String, Object>emptyMap());
} catch (FileSystemAlreadyExistsException ex) {
}
final URI fs = URI.create(fileSystemInfo.getScheme() + "://" + fileSystemInfo.getId() + "?sync=" + fileSystemInfo.getUri().split("\n")[0] + "&force");
service.getFileSystem(fs);
}
msgAnsweredOrTimedout.set(true);
}
});
}
});
while (!msgAnsweredOrTimedout.get()) {
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {
}
}
return null;
}
}));
}
@Override
public void startBatch(FileSystem fs) {
startBatch(new FileSystem[]{fs});
}
@Override
public void startBatch(FileSystem[] fs,
final Option... options) {
clusterService.lock();
for (final FileSystem _f : fs) {
final FileSystem f = _f.getRootDirectories().iterator().next().getFileSystem();
if (f instanceof FileSystemId) {
batchFileSystems.add(((FileSystemId) f).id());
}
}
service.startBatch(fs,
options);
}
@Override
public void startBatch(final FileSystem _fs,
final Option... options) {
clusterService.lock();
final FileSystem fs = _fs.getRootDirectories().iterator().next().getFileSystem();
if (fs instanceof FileSystemId) {
batchFileSystems.add(((FileSystemId) fs).id());
}
service.startBatch(fs,
options);
}
@Override
public void startBatch(final FileSystem... fs) {
clusterService.lock();
for (final FileSystem _f : fs) {
final FileSystem f = _f.getRootDirectories().iterator().next().getFileSystem();
if (f instanceof FileSystemId) {
batchFileSystems.add(((FileSystemId) f).id());
}
}
service.startBatch(fs);
}
@Override
public void endBatch() {
service.endBatch();
if (service.getLockControl().getHoldCount() == 0) {
final AtomicInteger process = new AtomicInteger(batchFileSystems.size());
for (final FileSystem _fs : service.getFileSystems()) {
final FileSystem fs = _fs.getRootDirectories().iterator().next().getFileSystem();
if (fs instanceof FileSystemId &&
batchFileSystems.contains(((FileSystemId) fs).id())) {
try {
new FileSystemSyncNonLock<Void>(service.getId(),
fs).execute(clusterService,
new FutureTask<Void>(new Callable<Void>() {
@Override
public Void call() throws Exception {
if (process.decrementAndGet() == 0) {
clusterService.unlock();
}
return null;
}
}));
} catch (Exception ex) {
logger.error("End batch error",
ex);
if (process.decrementAndGet() == 0) {
clusterService.unlock();
}
}
}
}
batchFileSystems.clear();
} else {
clusterService.unlock();
}
}
@Override
public FileAttribute<?>[] convert(final Map<String, ?> attrs) {
return service.convert(attrs);
}
@Override
public Path get(final String first,
final String... more) throws IllegalArgumentException {
return service.get(first,
more);
}
@Override
public Path get(final URI uri) throws IllegalArgumentException, FileSystemNotFoundException, SecurityException {
return service.get(uri);
}
@Override
public Iterable<FileSystem> getFileSystems() {
return service.getFileSystems();
}
@Override
public FileSystem getFileSystem(final URI uri) throws IllegalArgumentException, FileSystemNotFoundException, ProviderNotFoundException, SecurityException {
return service.getFileSystem(uri);
}
@Override
public FileSystem newFileSystem(final URI uri,
final Map<String, ?> env) throws IllegalArgumentException, FileSystemAlreadyExistsException, ProviderNotFoundException, IOException, SecurityException {
if (env.containsKey("internal")) {
return service.newFileSystem(uri,
env);
}
return new LockExecuteNotifySyncReleaseTemplate<FileSystem>() {
@Override
public MessageType getMessageType() {
return NEW_FS;
}
@Override
public String getServiceId() {
return service.getId();
}
@Override
public Map<String, String> buildContent() {
return new HashMap<String, String>() {{
put("uri",
uri.toString());
for (final Map.Entry<String, ?> entry : env.entrySet()) {
put(entry.getKey(),
entry.getValue().toString());
}
}};
}
@Override
public int timeOut() {
return TIMEOUT;
}
}.execute(clusterService,
new FutureTask<FileSystem>(new Callable<FileSystem>() {
@Override
public FileSystem call() throws Exception {
return service.newFileSystem(uri,
new HashMap<String, Object>(env) {{
put("clusterService",
clusterService);
}});
}
}));
}
@Override
public void onNewFileSystem(NewFileSystemListener listener) {
this.newFileSystemListener = listener;
}
@Override
public InputStream newInputStream(final Path path,
final OpenOption... options) throws IllegalArgumentException, NoSuchFileException, UnsupportedOperationException, IOException, SecurityException {
return service.newInputStream(path,
options);
}
@Override
public DirectoryStream<Path> newDirectoryStream(final Path dir) throws IllegalArgumentException, NotDirectoryException, IOException, SecurityException {
return service.newDirectoryStream(dir);
}
@Override
public DirectoryStream<Path> newDirectoryStream(final Path dir,
final DirectoryStream.Filter<Path> filter) throws IllegalArgumentException, NotDirectoryException, IOException, SecurityException {
return service.newDirectoryStream(dir,
filter);
}
@Override
public Path createFile(final Path path,
final FileAttribute<?>... attrs) throws IllegalArgumentException, UnsupportedOperationException, FileAlreadyExistsException, IOException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.createFile(path,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.createFile(path,
attrs);
}
}));
}
@Override
public Path createDirectory(final Path dir,
final FileAttribute<?>... attrs) throws IllegalArgumentException, UnsupportedOperationException, FileAlreadyExistsException, IOException, SecurityException {
if (isBatch(dir.getFileSystem())) {
return service.createDirectory(dir,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
dir.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.createDirectory(dir,
attrs);
}
}));
}
@Override
public Path createDirectories(final Path dir,
final FileAttribute<?>... attrs) throws UnsupportedOperationException, FileAlreadyExistsException, IOException, SecurityException {
if (isBatch(dir.getFileSystem())) {
return service.createDirectories(dir,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
dir.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.createDirectories(dir,
attrs);
}
}));
}
@Override
public Path createDirectory(final Path dir,
final Map<String, ?> attrs) throws IllegalArgumentException, UnsupportedOperationException, FileAlreadyExistsException, IOException, SecurityException {
if (isBatch(dir.getFileSystem())) {
return service.createDirectory(dir,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
dir.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.createDirectory(dir,
attrs);
}
}));
}
@Override
public Path createDirectories(final Path dir,
final Map<String, ?> attrs) throws UnsupportedOperationException, FileAlreadyExistsException, IOException, SecurityException {
if (isBatch(dir.getFileSystem())) {
return service.createDirectories(dir,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
dir.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.createDirectories(dir,
attrs);
}
}));
}
@Override
public void delete(final Path path,
final DeleteOption... options) throws IllegalArgumentException, NoSuchFileException, DirectoryNotEmptyException, IOException, SecurityException {
if (isBatch(path.getFileSystem())) {
service.delete(path,
options);
} else {
new FileSystemSyncLock<Void>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Void>(new Callable<Void>() {
@Override
public Void call() throws Exception {
service.delete(path,
options);
return null;
}
}));
}
}
@Override
public boolean deleteIfExists(final Path path,
final DeleteOption... options) throws IllegalArgumentException, DirectoryNotEmptyException, IOException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.deleteIfExists(path,
options);
}
return new FileSystemSyncLock<Boolean>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Boolean>(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return service.deleteIfExists(path,
options);
}
}));
}
@Override
public Path createTempFile(final String prefix,
final String suffix,
final FileAttribute<?>... attrs) throws IllegalArgumentException, UnsupportedOperationException, IOException, SecurityException {
return service.createTempFile(prefix,
suffix,
attrs);
}
@Override
public Path createTempFile(final Path dir,
final String prefix,
final String suffix,
final FileAttribute<?>... attrs) throws IllegalArgumentException, UnsupportedOperationException, IOException, SecurityException {
return service.createTempFile(dir,
prefix,
suffix,
attrs);
}
@Override
public Path createTempDirectory(final String prefix,
final FileAttribute<?>... attrs) throws IllegalArgumentException, UnsupportedOperationException, IOException, SecurityException {
return service.createTempDirectory(prefix,
attrs);
}
@Override
public Path createTempDirectory(final Path dir,
final String prefix,
final FileAttribute<?>... attrs) throws IllegalArgumentException, UnsupportedOperationException, IOException, SecurityException {
return service.createTempDirectory(dir,
prefix,
attrs);
}
@Override
public Path copy(final Path source,
final Path target,
final CopyOption... options) throws UnsupportedOperationException, FileAlreadyExistsException, DirectoryNotEmptyException, IOException, SecurityException {
if (isBatch(source.getFileSystem())) {
return service.copy(source,
target,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
target.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.copy(source,
target,
options);
}
}));
}
@Override
public long copy(final InputStream in,
final Path target,
final CopyOption... options) throws IOException, FileAlreadyExistsException, DirectoryNotEmptyException, UnsupportedOperationException, SecurityException {
if (isBatch(target.getFileSystem())) {
return service.copy(in,
target,
options);
}
return new FileSystemSyncLock<Long>(service.getId(),
target.getFileSystem()).execute(clusterService,
new FutureTask<Long>(new Callable<Long>() {
@Override
public Long call() throws Exception {
return service.copy(in,
target,
options);
}
}));
}
@Override
public long copy(final Path source,
final OutputStream out) throws IOException, SecurityException {
return service.copy(source,
out);
}
@Override
public Path move(final Path source,
final Path target,
final CopyOption... options) throws UnsupportedOperationException, FileAlreadyExistsException, DirectoryNotEmptyException, AtomicMoveNotSupportedException, IOException, SecurityException {
if (isBatch(source.getFileSystem())) {
return service.move(source,
target,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
source.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return new FileSystemSyncLock<Path>(service.getId(),
target.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.move(source,
target,
options);
}
}));
}
}));
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(final Path path,
final Class<V> type) throws IllegalArgumentException {
return service.getFileAttributeView(path,
type);
}
@Override
public Map<String, Object> readAttributes(final Path path) throws UnsupportedOperationException, NoSuchFileException, IllegalArgumentException, IOException, SecurityException {
return service.readAttributes(path);
}
@Override
public Map<String, Object> readAttributes(final Path path,
final String attributes) throws UnsupportedOperationException, NoSuchFileException, IllegalArgumentException, IOException, SecurityException {
return service.readAttributes(path,
attributes);
}
@Override
public Path setAttributes(final Path path,
final FileAttribute<?>... attrs) throws UnsupportedOperationException, IllegalArgumentException, ClassCastException, IOException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.setAttributes(path,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.setAttributes(path,
attrs);
}
}));
}
@Override
public Path setAttributes(final Path path,
final Map<String, Object> attrs) throws UnsupportedOperationException, IllegalArgumentException, ClassCastException, IOException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.setAttributes(path,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.setAttributes(path,
attrs);
}
}));
}
@Override
public Path setAttribute(final Path path,
final String attribute,
final Object value) throws UnsupportedOperationException, IllegalArgumentException, ClassCastException, IOException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.setAttribute(path,
attribute,
value);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.setAttribute(path,
attribute,
value);
}
}));
}
@Override
public Object getAttribute(final Path path,
final String attribute) throws UnsupportedOperationException, IllegalArgumentException, IOException, SecurityException {
return service.getAttribute(path,
attribute);
}
@Override
public FileTime getLastModifiedTime(final Path path) throws IllegalArgumentException, IOException, SecurityException {
return service.getLastModifiedTime(path);
}
@Override
public long size(final Path path) throws IllegalArgumentException, IOException, SecurityException {
return service.size(path);
}
@Override
public boolean exists(final Path path) throws IllegalArgumentException, SecurityException {
return service.exists(path);
}
@Override
public boolean notExists(final Path path) throws IllegalArgumentException, SecurityException {
return service.notExists(path);
}
@Override
public boolean isSameFile(final Path path,
final Path path2) throws IllegalArgumentException, IOException, SecurityException {
return service.isSameFile(path,
path2);
}
@Override
public BufferedReader newBufferedReader(final Path path,
final Charset cs) throws IllegalArgumentException, NoSuchFileException, IOException, SecurityException {
return service.newBufferedReader(path,
cs);
}
@Override
public byte[] readAllBytes(final Path path) throws IOException, OutOfMemoryError, SecurityException {
return service.readAllBytes(path);
}
@Override
public List<String> readAllLines(final Path path) throws IllegalArgumentException, NoSuchFileException, IOException, SecurityException {
return service.readAllLines(path);
}
@Override
public List<String> readAllLines(final Path path,
final Charset cs) throws IllegalArgumentException, NoSuchFileException, IOException, SecurityException {
return service.readAllLines(path,
cs);
}
@Override
public String readAllString(final Path path,
final Charset cs) throws IllegalArgumentException, NoSuchFileException, IOException {
return service.readAllString(path,
cs);
}
@Override
public String readAllString(final Path path) throws IllegalArgumentException, NoSuchFileException, IOException {
return service.readAllString(path);
}
@Override
public Path write(final Path path,
final byte[] bytes,
final OpenOption... options) throws IOException, UnsupportedOperationException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
bytes,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
bytes,
options);
}
}));
}
@Override
public Path write(final Path path,
final byte[] bytes,
final Map<String, ?> attrs,
final OpenOption... options) throws IOException, UnsupportedOperationException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
bytes,
attrs,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
bytes,
attrs,
options);
}
}));
}
@Override
public Path write(final Path path,
final byte[] bytes,
final Set<? extends OpenOption> options,
final FileAttribute<?>... attrs) throws IllegalArgumentException, IOException, UnsupportedOperationException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
bytes,
options,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
bytes,
options,
attrs);
}
}));
}
@Override
public Path write(final Path path,
final Iterable<? extends CharSequence> lines,
final Charset cs,
final OpenOption... options) throws IllegalArgumentException, IOException, UnsupportedOperationException, SecurityException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
lines,
cs,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
lines,
cs,
options);
}
}));
}
@Override
public Path write(final Path path,
final String content,
final OpenOption... options) throws IllegalArgumentException, IOException, UnsupportedOperationException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
content,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
content,
options);
}
}));
}
@Override
public Path write(final Path path,
final String content,
final Charset cs,
final OpenOption... options) throws IllegalArgumentException, IOException, UnsupportedOperationException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
content,
cs,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
content,
cs,
options);
}
}));
}
@Override
public Path write(final Path path,
final String content,
final Set<? extends OpenOption> options,
final FileAttribute<?>... attrs) throws IllegalArgumentException, IOException, UnsupportedOperationException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
content,
options,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
content,
options,
attrs);
}
}));
}
@Override
public Path write(final Path path,
final String content,
final Charset cs,
final Set<? extends OpenOption> options,
final FileAttribute<?>... attrs) throws IllegalArgumentException, IOException, UnsupportedOperationException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
content,
cs,
options,
attrs);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
content,
cs,
options,
attrs);
}
}));
}
@Override
public Path write(final Path path,
final String content,
final Map<String, ?> attrs,
final OpenOption... options) throws IllegalArgumentException, IOException, UnsupportedOperationException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
content,
attrs,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
content,
attrs,
options);
}
}));
}
@Override
public Path write(final Path path,
final String content,
final Charset cs,
final Map<String, ?> attrs,
final OpenOption... options) throws IllegalArgumentException, IOException, UnsupportedOperationException {
if (isBatch(path.getFileSystem())) {
return service.write(path,
content,
cs,
attrs,
options);
}
return new FileSystemSyncLock<Path>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Path>(new Callable<Path>() {
@Override
public Path call() throws Exception {
return service.write(path,
content,
cs,
attrs,
options);
}
}));
}
@Override
public OutputStream newOutputStream(final Path path,
final OpenOption... options) throws IllegalArgumentException, UnsupportedOperationException, IOException, SecurityException {
final OutputStream out = service.newOutputStream(path,
options);
return new OutputStream() {
@Override
public void write(final int b) throws java.io.IOException {
out.write(b);
}
@Override
public void close() throws java.io.IOException {
if (isBatch(path.getFileSystem())) {
out.close();
} else {
new FileSystemSyncLock<Void>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Void>(new Callable<Void>() {
@Override
public Void call() throws Exception {
out.close();
return null;
}
}));
}
}
};
}
@Override
public SeekableByteChannel newByteChannel(final Path path,
final OpenOption... options) throws IllegalArgumentException, UnsupportedOperationException, FileAlreadyExistsException, IOException, SecurityException {
final SeekableByteChannel sbc = service.newByteChannel(path,
options);
return new SeekableByteChannelWrapperImpl(sbc) {
@Override
public void close() throws java.io.IOException {
if (isBatch(path.getFileSystem())) {
sbc.close();
} else {
new FileSystemSyncLock<Void>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Void>(new Callable<Void>() {
@Override
public Void call() throws Exception {
sbc.close();
return null;
}
}));
}
}
};
}
@Override
public SeekableByteChannel newByteChannel(final Path path,
final Set<? extends OpenOption> options,
final FileAttribute<?>... attrs) throws IllegalArgumentException, UnsupportedOperationException, FileAlreadyExistsException, IOException, SecurityException {
final SeekableByteChannel sbc = service.newByteChannel(path,
options,
attrs);
return new SeekableByteChannelWrapperImpl(sbc) {
@Override
public void close() throws java.io.IOException {
if (isBatch(path.getFileSystem())) {
sbc.close();
} else {
new FileSystemSyncLock<Void>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Void>(new Callable<Void>() {
@Override
public Void call() throws Exception {
sbc.close();
return null;
}
}));
}
}
};
}
@Override
public BufferedWriter newBufferedWriter(final Path path,
final Charset cs,
final OpenOption... options) throws IllegalArgumentException, IOException, UnsupportedOperationException, SecurityException {
return new BufferedWriter(service.newBufferedWriter(path,
cs,
options)) {
@Override
public void close() throws java.io.IOException {
if (isBatch(path.getFileSystem())) {
superClose();
} else {
new FileSystemSyncLock<Void>(service.getId(),
path.getFileSystem()).execute(clusterService,
new FutureTask<Void>(new Callable<Void>() {
@Override
public Void call() throws Exception {
superClose();
return null;
}
}));
}
}
private void superClose() {
try {
super.close();
} catch (java.io.IOException e) {
throw new RuntimeException(e);
}
}
};
}
private boolean isBatch(final FileSystem fs) {
return fs instanceof FileSystemStateAware && ((FileSystemStateAware) (fs)).getState().equals(FileSystemState.BATCH);
}
@Override
public void dispose() {
service.dispose();
}
@Override
public int priority() {
return service.priority() - 1;
}
static class FileSystemInfo {
private String id;
private String scheme;
private String uri;
FileSystemInfo() {
}
FileSystemInfo(String id,
String scheme,
String uri) {
this.id = id;
this.scheme = scheme;
this.uri = uri;
}
String getId() {
return id;
}
void setId(String id) {
this.id = id;
}
String getScheme() {
return scheme;
}
void setScheme(String scheme) {
this.scheme = scheme;
}
String getUri() {
return uri;
}
void setUri(String uri) {
this.uri = uri;
}
}
class NewFileSystemMessageHandler implements MessageHandler {
@Override
public Pair<MessageType, Map<String, String>> handleMessage(final MessageType type,
final Map<String, String> content) {
if (NEW_FS.equals(type)) {
final String _uri = content.get("uri");
final Map<String, String> env = new HashMap<String, String>();
for (final Map.Entry<String, String> entry : content.entrySet()) {
if (!(entry.getKey().equals("uri") || entry.getKey().equals("type"))) {
env.put(entry.getKey(),
entry.getValue());
}
}
final URI uri = URI.create(_uri);
final FileSystem fs = service.newFileSystem(uri,
env);
if (newFileSystemListener != null) {
newFileSystemListener.execute(fs,
uri.getScheme(),
((FileSystemId) fs).id(),
env);
}
}
return null;
}
}
class SyncFileSystemMessageHandler implements MessageHandler {
@Override
public Pair<MessageType, Map<String, String>> handleMessage(final MessageType type,
final Map<String, String> content) {
if (SYNC_FS.equals(type)) {
final String scheme = content.get("fs_scheme");
final String id = content.get("fs_id");
final String[] supportedUris = cleanup(content.get("fs_uri").split("\n"));
for (final String supportedUri : supportedUris) {
try {
String origin;
try {
origin = URLEncoder.encode(supportedUri,
"UTF-8");
} catch (UnsupportedEncodingException e) {
origin = supportedUri;
}
final URI fs = URI.create(scheme + "://" + id + "?sync=" + origin + "&force");
service.getFileSystem(fs);
break;
} catch (Exception e) {
// try the other supported uri in case of failure
logger.warn("File system synchronization for origin {} failed with error {}, trying another if available",
supportedUri,
e.getMessage());
}
}
}
return null;
}
private String[] cleanup(final String... split) {
final List<String> result = new ArrayList<String>(split.length);
for (final String s : split) {
if (s.startsWith("git://")) {
result.add(s);
}
}
return result.toArray(new String[result.size()]);
}
}
class QueryFileSystemMessageHandler implements MessageHandler {
@Override
public Pair<MessageType, Map<String, String>> handleMessage(final MessageType type,
final Map<String, String> content) {
if (QUERY_FOR_FS.equals(type)) {
Map<String, String> replyContent = new HashMap<String, String>();
int i = 0;
final Set<FileSystem> fileSystems = new HashSet<FileSystem>();
for (FileSystem fs : service.getFileSystems()) {
fileSystems.add(fs);
}
for (final FileSystem fs : fileSystems) {
replyContent.put("fs_scheme_" + i,
fs.getRootDirectories().iterator().next().toUri().getScheme());
replyContent.put("fs_id_" + i,
((FileSystemId) fs).id());
replyContent.put("fs_uri_" + i,
fs.toString());
i++;
}
return new Pair<MessageType, Map<String, String>>(QUERY_FOR_FS_RESULT,
replyContent);
}
return null;
}
}
}