/*
* 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.ignite.internal.processors.hadoop.impl.igfs;
import java.io.Closeable;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteFileSystem;
import org.apache.ignite.Ignition;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.igfs.IgfsBlockLocation;
import org.apache.ignite.igfs.IgfsFile;
import org.apache.ignite.igfs.IgfsInputStream;
import org.apache.ignite.igfs.IgfsOutputStream;
import org.apache.ignite.igfs.IgfsPath;
import org.apache.ignite.igfs.IgfsPathSummary;
import org.apache.ignite.igfs.IgfsUserContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.IgnitionEx;
import org.apache.ignite.internal.processors.igfs.IgfsEx;
import org.apache.ignite.internal.processors.igfs.IgfsHandshakeResponse;
import org.apache.ignite.internal.processors.igfs.IgfsImpl;
import org.apache.ignite.internal.processors.igfs.IgfsModeResolver;
import org.apache.ignite.internal.processors.igfs.IgfsStatus;
import org.apache.ignite.internal.processors.igfs.IgfsUtils;
import org.apache.ignite.internal.processors.resource.GridSpringResourceContext;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteOutClosure;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.IgniteState.STARTED;
/**
* Communication with grid in the same process.
*/
public class HadoopIgfsInProc implements HadoopIgfsEx {
/** Ignite client reference counters (node name, reference count). */
private static final Map<String, Integer> REF_CTRS = new HashMap<>();
/** Reference count monitor. */
private static final Object REF_CTR_MUX = new Object();
/** Target IGFS. */
private final IgfsEx igfs;
/** Buffer size. */
private final int bufSize;
/** Event listeners. */
private final Map<HadoopIgfsStreamDelegate, HadoopIgfsStreamEventListener> lsnrs =
new ConcurrentHashMap<>();
/** Logger. */
private final Log log;
/** The user this Igfs works on behalf of. */
private final String user;
/**
* Constructor.
*
* @param igfs Target IGFS.
* @param log Log.
* @param userName User name.
*/
private HadoopIgfsInProc(IgfsEx igfs, Log log, String userName) {
this.igfs = igfs;
this.log = log;
bufSize = igfs.configuration().getBlockSize() * 2;
user = IgfsUtils.fixUserName(userName);
}
/**
* Creates instance of the HadoopIgfsInProcWithIgniteRefsCount by IGFS name.
*
* @param igfsName Target IGFS name.
* @param log Log.
* @param userName User name.
* @return HadoopIgfsInProcWithIgniteRefsCount instance. {@code null} if the IGFS not fount in the current VM.
*/
public static HadoopIgfsInProc create(String igfsName, Log log, String userName) {
synchronized (REF_CTR_MUX) {
for (Ignite ignite : Ignition.allGrids()) {
HadoopIgfsInProc delegate = create0(ignite, igfsName, log, userName);
if (delegate != null)
return delegate;
}
}
return null;
}
/**
* Creates instance of the HadoopIgfsInProcWithIgniteRefsCount by IGFS name, ignite client node is created
* if necessary.
*
* @param igniteCfgPath Path to ignite configuration.
* @param igfsName Target IGFS name.
* @param log Log.
* @param userName User name.
* @return HadoopIgfsInProcWithIgniteRefsCount instance. {@code null} if the IGFS not fount in the current VM.
* @throws IgniteCheckedException On error.
*/
public static HadoopIgfsInProc create(String igniteCfgPath, String igfsName, Log log, String userName)
throws IgniteCheckedException {
IgniteBiTuple<IgniteConfiguration, GridSpringResourceContext> cfgPair =
IgnitionEx.loadConfiguration(igniteCfgPath);
IgniteConfiguration cfg = cfgPair.get1();
cfg.setClientMode(true);
String nodeName = cfg.getIgniteInstanceName();
synchronized (REF_CTR_MUX) {
T2<Ignite, Boolean> startRes = IgnitionEx.getOrStart(cfg);
boolean newNodeStarted = startRes.get2();
if (newNodeStarted) {
assert !REF_CTRS.containsKey(nodeName) : "The ignite instance already exists in the ref count map";
REF_CTRS.put(nodeName, 0);
}
HadoopIgfsInProc hadoop = create0(startRes.get1(), igfsName, log, userName);
if (hadoop == null) {
if (newNodeStarted) {
REF_CTRS.remove(nodeName);
Ignition.stop(nodeName, true);
}
throw new HadoopIgfsCommunicationException("Ignite client node doesn't have IGFS with the " +
"given name: " + igfsName);
}
return hadoop;
}
}
/**
* Creates instance of the HadoopIgfsInProcWithIgniteRefsCount by IGFS name.
*
* @param ignite Ignite instance.
* @param igfsName Target IGFS name.
* @param log Log.
* @param userName User name.
* @return HadoopIgfsInProcWithIgniteRefsCount instance. {@code null} if the IGFS not found
* in the specified ignite instance.
*/
private static HadoopIgfsInProc create0(Ignite ignite, String igfsName, Log log, String userName) {
assert Thread.holdsLock(REF_CTR_MUX);
assert ignite != null;
if (Ignition.state(ignite.name()) == STARTED) {
try {
for (IgniteFileSystem fs : ignite.fileSystems()) {
if (F.eq(fs.name(), igfsName)) {
Integer ctr = REF_CTRS.get(ignite.name());
if (ctr != null)
REF_CTRS.put(ignite.name(), ctr + 1);
return new HadoopIgfsInProc((IgfsEx)fs, log, userName);
}
}
}
catch (IllegalStateException ignore) {
// May happen if the grid state has changed:
}
}
return null;
}
/** {@inheritDoc} */
@Override public IgfsHandshakeResponse handshake(final String logDir) {
return IgfsUserContext.doAs(user, new IgniteOutClosure<IgfsHandshakeResponse>() {
@Override public IgfsHandshakeResponse apply() {
igfs.clientLogDirectory(logDir);
return new IgfsHandshakeResponse(igfs.name(), igfs.groupBlockSize(), igfs.globalSampling());
}
});
}
/**
* Call onClose for all listeners.
*/
private void notifyListenersOnClose() {
// Perform cleanup.
for (HadoopIgfsStreamEventListener lsnr : lsnrs.values()) {
try {
lsnr.onClose();
}
catch (IgniteCheckedException e) {
if (log.isDebugEnabled())
log.debug("Failed to notify stream event listener", e);
}
}
}
/** {@inheritDoc} */
@Override public void close(boolean force) {
notifyListenersOnClose();
String gridName = igfs.context().kernalContext().grid().name();
synchronized (REF_CTR_MUX) {
Integer cnt = REF_CTRS.get(gridName);
if (cnt != null) {
// The node was created by this HadoopIgfsWrapper.
// The node must be stopped when there are not opened filesystems that are used one.
if (cnt > 1)
REF_CTRS.put(gridName, cnt - 1);
else {
REF_CTRS.remove(gridName);
G.stop(gridName, false);
}
}
}
}
/** {@inheritDoc} */
@Override public IgfsFile info(final IgfsPath path) throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<IgfsFile>() {
@Override public IgfsFile apply() {
return igfs.info(path);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to get file info because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public IgfsFile update(final IgfsPath path, final Map<String, String> props) throws
IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<IgfsFile>() {
@Override public IgfsFile apply() {
return igfs.update(path, props);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to update file because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public Boolean setTimes(final IgfsPath path, final long accessTime, final long modificationTime)
throws IgniteCheckedException {
try {
IgfsUserContext.doAs(user, new IgniteOutClosure<Void>() {
@Override public Void apply() {
igfs.setTimes(path, modificationTime, accessTime);
return null;
}
});
return true;
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to set path times because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public Boolean rename(final IgfsPath src, final IgfsPath dest) throws IgniteCheckedException {
try {
IgfsUserContext.doAs(user, new IgniteOutClosure<Void>() {
@Override public Void apply() {
igfs.rename(src, dest);
return null;
}
});
return true;
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to rename path because Grid is stopping: " + src);
}
}
/** {@inheritDoc} */
@Override public Boolean delete(final IgfsPath path, final boolean recursive) throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<Boolean>() {
@Override public Boolean apply() {
return igfs.delete(path, recursive);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to delete path because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public IgfsStatus fsStatus() throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new Callable<IgfsStatus>() {
@Override public IgfsStatus call() throws IgniteCheckedException {
return igfs.globalSpace();
}
});
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to get file system status because Grid is stopping.");
}
catch (IgniteCheckedException | RuntimeException | Error e) {
throw e;
}
catch (Exception ignored) {
throw new AssertionError("Must never go there.");
}
}
/** {@inheritDoc} */
@Override public Collection<IgfsPath> listPaths(final IgfsPath path) throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<Collection<IgfsPath>>() {
@Override public Collection<IgfsPath> apply() {
return igfs.listPaths(path);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to list paths because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public Collection<IgfsFile> listFiles(final IgfsPath path) throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<Collection<IgfsFile>>() {
@Override public Collection<IgfsFile> apply() {
return igfs.listFiles(path);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to list files because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public Boolean mkdirs(final IgfsPath path, final Map<String, String> props)
throws IgniteCheckedException {
try {
IgfsUserContext.doAs(user, new IgniteOutClosure<Void>() {
@Override public Void apply() {
igfs.mkdirs(path, props);
return null;
}
});
return true;
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to findIgfsAndCreate directory because Grid is stopping: " +
path);
}
}
/** {@inheritDoc} */
@Override public IgfsPathSummary contentSummary(final IgfsPath path) throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<IgfsPathSummary>() {
@Override public IgfsPathSummary apply() {
return igfs.summary(path);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to get content summary because Grid is stopping: " +
path);
}
}
/** {@inheritDoc} */
@Override public Collection<IgfsBlockLocation> affinity(final IgfsPath path, final long start, final long len)
throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<Collection<IgfsBlockLocation>>() {
@Override public Collection<IgfsBlockLocation> apply() {
return igfs.affinity(path, start, len);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to get affinity because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public HadoopIgfsStreamDelegate open(final IgfsPath path) throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<HadoopIgfsStreamDelegate>() {
@Override public HadoopIgfsStreamDelegate apply() {
IgfsInputStream stream = igfs.open(path, bufSize);
return new HadoopIgfsStreamDelegate(HadoopIgfsInProc.this, stream, stream.length());
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to open file because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public HadoopIgfsStreamDelegate open(final IgfsPath path, final int seqReadsBeforePrefetch)
throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<HadoopIgfsStreamDelegate>() {
@Override public HadoopIgfsStreamDelegate apply() {
IgfsInputStream stream = igfs.open(path, bufSize, seqReadsBeforePrefetch);
return new HadoopIgfsStreamDelegate(HadoopIgfsInProc.this, stream, stream.length());
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to open file because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public HadoopIgfsStreamDelegate create(final IgfsPath path, final boolean overwrite,
final boolean colocate, final int replication, final long blockSize, final @Nullable Map<String, String> props)
throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<HadoopIgfsStreamDelegate>() {
@Override public HadoopIgfsStreamDelegate apply() {
IgfsOutputStream stream = igfs.create(path, bufSize, overwrite,
colocate ? igfs.nextAffinityKey() : null, replication, blockSize, props);
return new HadoopIgfsStreamDelegate(HadoopIgfsInProc.this, stream);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to findIgfsAndCreate file because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public HadoopIgfsStreamDelegate append(final IgfsPath path, final boolean create,
final @Nullable Map<String, String> props) throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<HadoopIgfsStreamDelegate>() {
@Override public HadoopIgfsStreamDelegate apply() {
IgfsOutputStream stream = igfs.append(path, bufSize, create, props);
return new HadoopIgfsStreamDelegate(HadoopIgfsInProc.this, stream);
}
});
}
catch (IgniteException e) {
throw new IgniteCheckedException(e);
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to append file because Grid is stopping: " + path);
}
}
/** {@inheritDoc} */
@Override public IgniteInternalFuture<byte[]> readData(HadoopIgfsStreamDelegate delegate, long pos, int len,
@Nullable byte[] outBuf, int outOff, int outLen) {
IgfsInputStream stream = delegate.target();
try {
byte[] res = null;
if (outBuf != null) {
int outTailLen = outBuf.length - outOff;
if (len <= outTailLen)
stream.readFully(pos, outBuf, outOff, len);
else {
stream.readFully(pos, outBuf, outOff, outTailLen);
int remainderLen = len - outTailLen;
res = new byte[remainderLen];
stream.readFully(pos, res, 0, remainderLen);
}
} else {
res = new byte[len];
stream.readFully(pos, res, 0, len);
}
return new GridFinishedFuture<>(res);
}
catch (IllegalStateException | IOException e) {
HadoopIgfsStreamEventListener lsnr = lsnrs.get(delegate);
if (lsnr != null)
lsnr.onError(e.getMessage());
return new GridFinishedFuture<>(e);
}
}
/** {@inheritDoc} */
@Override public void writeData(HadoopIgfsStreamDelegate delegate, byte[] data, int off, int len)
throws IOException {
try {
IgfsOutputStream stream = delegate.target();
stream.write(data, off, len);
}
catch (IllegalStateException | IOException e) {
HadoopIgfsStreamEventListener lsnr = lsnrs.get(delegate);
if (lsnr != null)
lsnr.onError(e.getMessage());
if (e instanceof IllegalStateException)
throw new IOException("Failed to write data to IGFS stream because Grid is stopping.", e);
else
throw e;
}
}
/** {@inheritDoc} */
@Override public void flush(HadoopIgfsStreamDelegate delegate) throws IOException {
try {
IgfsOutputStream stream = delegate.target();
stream.flush();
}
catch (IllegalStateException | IOException e) {
HadoopIgfsStreamEventListener lsnr = lsnrs.get(delegate);
if (lsnr != null)
lsnr.onError(e.getMessage());
if (e instanceof IllegalStateException)
throw new IOException("Failed to flush data to IGFS stream because Grid is stopping.", e);
else
throw e;
}
}
/** {@inheritDoc} */
@Override public void closeStream(HadoopIgfsStreamDelegate desc) throws IOException {
Closeable closeable = desc.target();
try {
closeable.close();
}
catch (IllegalStateException e) {
throw new IOException("Failed to close IGFS stream because Grid is stopping.", e);
}
}
/** {@inheritDoc} */
@Override public void addEventListener(HadoopIgfsStreamDelegate delegate,
HadoopIgfsStreamEventListener lsnr) {
HadoopIgfsStreamEventListener lsnr0 = lsnrs.put(delegate, lsnr);
assert lsnr0 == null || lsnr0 == lsnr;
if (log.isDebugEnabled())
log.debug("Added stream event listener [delegate=" + delegate + ']');
}
/** {@inheritDoc} */
@Override public void removeEventListener(HadoopIgfsStreamDelegate delegate) {
HadoopIgfsStreamEventListener lsnr0 = lsnrs.remove(delegate);
if (lsnr0 != null && log.isDebugEnabled())
log.debug("Removed stream event listener [delegate=" + delegate + ']');
}
/** {@inheritDoc} */
@Override public String user() {
return user;
}
/** {@inheritDoc} */
@Override public IgfsModeResolver modeResolver() throws IgniteCheckedException {
try {
return IgfsUserContext.doAs(user, new IgniteOutClosure<IgfsModeResolver>() {
@Override public IgfsModeResolver apply() {
return ((IgfsImpl)igfs).modeResolver();
}
});
}
catch (IllegalStateException ignored) {
throw new HadoopIgfsCommunicationException("Failed to get mode resolver because Grid is stopping");
}
}
}