/*
* 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.platform.utils;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.binary.BinaryRawWriterEx;
import org.apache.ignite.internal.processors.platform.PlatformContext;
import org.apache.ignite.internal.processors.platform.PlatformTarget;
import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
import org.apache.ignite.internal.processors.platform.memory.PlatformMemory;
import org.apache.ignite.internal.processors.platform.memory.PlatformOutputStream;
import org.apache.ignite.lang.IgniteBiInClosure;
import org.apache.ignite.lang.IgniteFuture;
import org.apache.ignite.lang.IgniteInClosure;
import org.jetbrains.annotations.Nullable;
/**
* Interop future utils.
*/
public class PlatformFutureUtils {
/** Future type: byte. */
public static final int TYP_BYTE = 1;
/** Future type: boolean. */
public static final int TYP_BOOL = 2;
/** Future type: short. */
public static final int TYP_SHORT = 3;
/** Future type: char. */
public static final int TYP_CHAR = 4;
/** Future type: int. */
public static final int TYP_INT = 5;
/** Future type: float. */
public static final int TYP_FLOAT = 6;
/** Future type: long. */
public static final int TYP_LONG = 7;
/** Future type: double. */
public static final int TYP_DOUBLE = 8;
/** Future type: object. */
public static final int TYP_OBJ = 9;
/**
* Listen future.
*
* @param ctx Context.
* @param fut Java future.
* @param futPtr Native future pointer.
* @param typ Expected return type.
* @return Resulting listenable.
*/
public static PlatformListenable listen(final PlatformContext ctx, IgniteInternalFuture fut, final long futPtr,
final int typ, PlatformTarget target) {
PlatformListenable listenable = getListenable(fut);
listen(ctx, listenable, futPtr, typ, null, target);
return listenable;
}
/**
* Listen future.
*
* @param ctx Context.
* @param fut Java future.
* @param futPtr Native future pointer.
* @param typ Expected return type.
* @return Resulting listenable.
*/
public static PlatformListenable listen(final PlatformContext ctx, IgniteFuture fut, final long futPtr,
final int typ, PlatformTarget target) {
PlatformListenable listenable = getListenable(fut);
listen(ctx, listenable, futPtr, typ, null, target);
return listenable;
}
/**
* Listen future.
*
* @param ctx Context.
* @param fut Java future.
* @param futPtr Native future pointer.
* @param typ Expected return type.
* @param writer Writer.
* @return Resulting listenable.
*/
public static PlatformListenable listen(final PlatformContext ctx, IgniteInternalFuture fut, final long futPtr,
final int typ, Writer writer, PlatformTarget target) {
PlatformListenable listenable = getListenable(fut);
listen(ctx, listenable, futPtr, typ, writer, target);
return listenable;
}
/**
* Listen future.
*
* @param ctx Context.
* @param fut Java future.
* @param futPtr Native future pointer.
* @param typ Expected return type.
* @param writer Writer.
* @return Resulting listenable.
*/
public static PlatformListenable listen(final PlatformContext ctx, IgniteFuture fut, final long futPtr,
final int typ, Writer writer, PlatformTarget target) {
PlatformListenable listenable = getListenable(fut);
listen(ctx, listenable, futPtr, typ, writer, target);
return listenable;
}
/**
* Listen future.
*
* @param ctx Context.
* @param fut Java future.
* @param futPtr Native future pointer.
* @param writer Writer.
* @return Resulting listenable.
*/
public static PlatformListenable listen(final PlatformContext ctx, IgniteInternalFuture fut, final long futPtr,
Writer writer, PlatformTarget target) {
PlatformListenable listenable = getListenable(fut);
listen(ctx, listenable, futPtr, TYP_OBJ, writer, target);
return listenable;
}
/**
* Gets the listenable.
*
* @param fut Future.
* @return Platform listenable.
*/
public static PlatformListenable getListenable(IgniteInternalFuture fut) {
return new InternalFutureListenable(fut);
}
/**
* Gets the listenable.
*
* @param fut Future.
* @return Platform listenable.
*/
public static PlatformListenable getListenable(IgniteFuture fut) {
return new FutureListenable(fut);
}
/**
* Listen future.
*
* @param ctx Context.
* @param listenable Listenable entry.
* @param futPtr Native future pointer.
* @param typ Expected return type.
* @param writer Optional writer.
*/
@SuppressWarnings("unchecked")
public static void listen(final PlatformContext ctx, PlatformListenable listenable, final long futPtr, final
int typ, @Nullable final Writer writer, final PlatformTarget target) {
final PlatformCallbackGateway gate = ctx.gateway();
listenable.listen(new IgniteBiInClosure<Object, Throwable>() {
private static final long serialVersionUID = 0L;
@Override public void apply(Object res, Throwable err) {
if (err instanceof Exception)
err = target.convertException((Exception)err);
if (writer != null && writeToWriter(res, err, ctx, writer, futPtr))
return;
if (err != null) {
writeFutureError(ctx, futPtr, err);
return;
}
try {
if (typ == TYP_OBJ) {
if (res == null)
gate.futureNullResult(futPtr);
else {
try (PlatformMemory mem = ctx.memory().allocate()) {
PlatformOutputStream out = mem.output();
BinaryRawWriterEx outWriter = ctx.writer(out);
outWriter.writeObjectDetached(res);
out.synchronize();
gate.futureObjectResult(futPtr, mem.pointer());
}
}
}
else if (res == null)
gate.futureNullResult(futPtr);
else {
switch (typ) {
case TYP_BYTE:
gate.futureByteResult(futPtr, (byte) res);
break;
case TYP_BOOL:
gate.futureBoolResult(futPtr, (boolean) res ? 1 : 0);
break;
case TYP_SHORT:
gate.futureShortResult(futPtr, (short) res);
break;
case TYP_CHAR:
gate.futureCharResult(futPtr, (char) res);
break;
case TYP_INT:
gate.futureIntResult(futPtr, (int) res);
break;
case TYP_FLOAT:
gate.futureFloatResult(futPtr, Float.floatToIntBits((float) res));
break;
case TYP_LONG:
gate.futureLongResult(futPtr, (long) res);
break;
case TYP_DOUBLE:
gate.futureDoubleResult(futPtr, Double.doubleToLongBits((double)res));
break;
default:
assert false : "Should not reach this: " + typ;
}
}
}
catch (Throwable t) {
writeFutureError(ctx, futPtr, t);
if (t instanceof Error)
throw t;
}
}
});
}
/**
* Write future error.
*
* @param ctx Context.
* @param futPtr Future pointer.
* @param err Error.
*/
private static void writeFutureError(final PlatformContext ctx, long futPtr, Throwable err) {
try (PlatformMemory mem = ctx.memory().allocate()) {
PlatformOutputStream out = mem.output();
BinaryRawWriterEx outWriter = ctx.writer(out);
PlatformUtils.writeError(err, outWriter);
PlatformUtils.writeErrorData(err, outWriter);
out.synchronize();
ctx.gateway().futureError(futPtr, mem.pointer());
}
}
/**
* Write result to a custom writer
*
* @param obj Object to write.
* @param err Error to write.
* @param ctx Context.
* @param writer Writer.
* @param futPtr Future pointer.
* @return Value indicating whether custom write was performed. When false, default write will be used.
*/
private static boolean writeToWriter(Object obj, Throwable err, PlatformContext ctx, Writer writer, long futPtr) {
boolean canWrite = writer.canWrite(obj, err);
if (!canWrite)
return false;
try (PlatformMemory mem = ctx.memory().allocate()) {
PlatformOutputStream out = mem.output();
BinaryRawWriterEx outWriter = ctx.writer(out);
writer.write(outWriter, obj, err);
out.synchronize();
ctx.gateway().futureObjectResult(futPtr, mem.pointer());
}
return true;
}
/**
* Writer allowing special future result handling.
*/
public static interface Writer {
/**
* Write object.
*
* @param writer Writer.
* @param obj Object.
* @param err Error.
*/
public void write(BinaryRawWriterEx writer, Object obj, Throwable err);
/**
* Determines whether this writer can write given data.
*
* @param obj Object.
* @param err Error.
* @return Value indicating whether this writer can write given data.
*/
public boolean canWrite(Object obj, Throwable err);
}
/**
* Listenable around Ignite future.
*/
private static class FutureListenable implements PlatformListenable {
/** Future. */
private final IgniteFuture fut;
/**
* Constructor.
*
* @param fut Future.
*/
public FutureListenable(IgniteFuture fut) {
this.fut = fut;
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public void listen(final IgniteBiInClosure<Object, Throwable> lsnr) {
fut.listen(new IgniteInClosure<IgniteFuture>() {
private static final long serialVersionUID = 0L;
@Override public void apply(IgniteFuture fut0) {
try {
lsnr.apply(fut0.get(), null);
}
catch (Throwable err) {
lsnr.apply(null, err);
if (err instanceof Error)
throw err;
}
}
});
}
/** {@inheritDoc} */
@Override public boolean cancel() {
return fut.cancel();
}
/** {@inheritDoc} */
@Override public boolean isCancelled() {
return fut.isCancelled();
}
}
/**
* Listenable around Ignite future.
*/
private static class InternalFutureListenable implements PlatformListenable {
/** Future. */
private final IgniteInternalFuture fut;
/**
* Constructor.
*
* @param fut Future.
*/
public InternalFutureListenable(IgniteInternalFuture fut) {
this.fut = fut;
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public void listen(final IgniteBiInClosure<Object, Throwable> lsnr) {
fut.listen(new IgniteInClosure<IgniteInternalFuture>() {
private static final long serialVersionUID = 0L;
@Override public void apply(IgniteInternalFuture fut0) {
try {
lsnr.apply(fut0.get(), null);
}
catch (Throwable err) {
lsnr.apply(null, err);
}
}
});
}
/** {@inheritDoc} */
@Override public boolean cancel() throws IgniteCheckedException {
return fut.cancel();
}
/** {@inheritDoc} */
@Override public boolean isCancelled() {
return fut.isCancelled();
}
}
}