/* * 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.util.future; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteIllegalStateException; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.util.lang.GridClosureException; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiClosure; import org.apache.ignite.lang.IgniteInClosure; import org.apache.ignite.lang.IgniteOutClosure; /** * Future which waits for embedded future to complete and then asynchronously executes * provided closure with embedded future result. */ @SuppressWarnings({"NullableProblems"}) public class GridEmbeddedFuture<A, B> extends GridFutureAdapter<A> { /** */ private static final long serialVersionUID = 0L; /** Embedded future to wait for. */ private IgniteInternalFuture<B> embedded; /** * @param c Closure to execute upon completion of embedded future. * @param embedded Embedded future. */ public GridEmbeddedFuture( final IgniteBiClosure<B, Exception, A> c, IgniteInternalFuture<B> embedded ) { assert embedded != null; assert c != null; this.embedded = embedded; embedded.listen(new AL1() { @SuppressWarnings({"ErrorNotRethrown", "CatchGenericClass"}) @Override public void applyx(IgniteInternalFuture<B> embedded) { try { onDone(c.apply(embedded.get(), null)); } catch (IgniteCheckedException | RuntimeException e) { onDone(c.apply(null, e)); } catch (Error e) { onDone(e); throw e; } } }); } /** * Embeds futures. Specific change order of arguments to avoid conflicts. * * @param embedded Embedded future. * @param c Closure which runs upon completion of embedded closure and which returns another future. */ public GridEmbeddedFuture( IgniteInternalFuture<B> embedded, final IgniteBiClosure<B, Exception, IgniteInternalFuture<A>> c ) { assert embedded != null; assert c != null; this.embedded = embedded; embedded.listen(new AL1() { @Override public void applyx(IgniteInternalFuture<B> embedded) { try { IgniteInternalFuture<A> next = c.apply(embedded.get(), null); if (next == null) { onDone(); return; } next.listen(new AL2() { @Override public void applyx(IgniteInternalFuture<A> next) { try { onDone(next.get()); } catch (GridClosureException e) { onDone(e.unwrap()); } catch (IgniteCheckedException | RuntimeException e) { onDone(e); } catch (Error e) { onDone(e); throw e; } } }); } catch (GridClosureException e) { c.apply(null, e); onDone(e.unwrap()); } catch (IgniteCheckedException | RuntimeException e) { c.apply(null, e); onDone(e); } catch (Error e) { onDone(e); throw e; } } }); } /** * Embeds futures. * * @param embedded Future. * @param c1 Closure which runs upon completion of embedded future and which returns another future. * @param c2 Closure will runs upon completion of future returned by {@code c1} closure. */ public GridEmbeddedFuture( IgniteInternalFuture<B> embedded, final IgniteBiClosure<B, Exception, IgniteInternalFuture<A>> c1, final IgniteBiClosure<A, Exception, A> c2 ) { assert embedded != null; assert c1 != null; assert c2 != null; this.embedded = embedded; embedded.listen(new AL1() { @Override public void applyx(IgniteInternalFuture<B> embedded) { try { IgniteInternalFuture<A> next = c1.apply(embedded.get(), null); if (next == null) { onDone(); return; } next.listen(new AL2() { @Override public void applyx(IgniteInternalFuture<A> next) { try { onDone(c2.apply(next.get(), null)); } catch (GridClosureException e) { c2.apply(null, e); onDone(e.unwrap()); } catch (IgniteCheckedException | RuntimeException e) { c2.apply(null, e); onDone(e); } catch (Error e) { onDone(e); throw e; } } }); } catch (GridClosureException e) { c1.apply(null, e); onDone(e.unwrap()); } catch (IgniteCheckedException | RuntimeException e) { c1.apply(null, e); onDone(e); } catch (Error e) { onDone(e); throw e; } } }); } /** * @param embedded Embedded future. * @param c Closure to create next future. */ public GridEmbeddedFuture( IgniteInternalFuture<B> embedded, final IgniteOutClosure<IgniteInternalFuture<A>> c ) { assert embedded != null; assert c != null; this.embedded = embedded; embedded.listen(new AL1() { @Override public void applyx(IgniteInternalFuture<B> embedded) { try { IgniteInternalFuture<A> next = c.apply(); if (next == null) { onDone(); return; } next.listen(new AL2() { @Override public void applyx(IgniteInternalFuture<A> next) { try { onDone(next.get()); } catch (GridClosureException e) { onDone(e.unwrap()); } catch (IgniteCheckedException | RuntimeException e) { onDone(e); } catch (Error e) { onDone(e); throw e; } } }); } catch (Error e) { onDone(e); throw e; } } }); } /** {@inheritDoc} */ @Override public boolean cancel() throws IgniteCheckedException { return embedded.cancel(); } /** {@inheritDoc} */ @Override public boolean isCancelled() { return embedded.isCancelled(); } /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridEmbeddedFuture.class, this); } /** Typedef. */ private abstract class AL1 extends AsyncListener1 { /** */ private static final long serialVersionUID = 0L; } /** Typedef. */ private abstract class AL2 extends AsyncListener2 { /** */ private static final long serialVersionUID = 0L; } /** * Make sure that listener does not throw exceptions. */ private abstract class AsyncListener1 implements IgniteInClosure<IgniteInternalFuture<B>> { /** */ private static final long serialVersionUID = 0L; /** {@inheritDoc} */ @Override public final void apply(IgniteInternalFuture<B> f) { try { applyx(f); } catch (IgniteIllegalStateException ignore) { U.warn(null, "Will not execute future listener (grid is stopping): " + this); } catch (Exception e) { onDone(e); } catch (Error e) { onDone(e); throw e; } } /** * @param f Future. * @throws Exception In case of error. */ protected abstract void applyx(IgniteInternalFuture<B> f) throws Exception; } /** * Make sure that listener does not throw exceptions. */ private abstract class AsyncListener2 implements IgniteInClosure<IgniteInternalFuture<A>> { /** */ private static final long serialVersionUID = 0L; /** {@inheritDoc} */ @Override public final void apply(IgniteInternalFuture<A> f) { try { applyx(f); } catch (IgniteIllegalStateException ignore) { U.warn(null, "Will not execute future listener (grid is stopping): " + this); } catch (Exception e) { onDone(e); } catch (Error e) { onDone(e); throw e; } } /** * @param f Future. * @throws Exception In case of error. */ protected abstract void applyx(IgniteInternalFuture<A> f) throws Exception; } }