/** * 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.camel.component.ignite.compute; import java.util.ArrayList; import java.util.Collection; import org.apache.camel.AsyncCallback; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.RuntimeCamelException; import org.apache.camel.TypeConverter; import org.apache.camel.component.ignite.IgniteConstants; import org.apache.camel.impl.DefaultAsyncProducer; import org.apache.camel.util.MessageHelper; import org.apache.ignite.IgniteCompute; import org.apache.ignite.compute.ComputeTask; import org.apache.ignite.lang.IgniteCallable; import org.apache.ignite.lang.IgniteClosure; import org.apache.ignite.lang.IgniteFuture; import org.apache.ignite.lang.IgniteInClosure; import org.apache.ignite.lang.IgniteReducer; import org.apache.ignite.lang.IgniteRunnable; /** * Ignite Compute producer. */ public class IgniteComputeProducer extends DefaultAsyncProducer { private IgniteComputeEndpoint endpoint; public IgniteComputeProducer(IgniteComputeEndpoint endpoint) { super(endpoint); this.endpoint = endpoint; } @Override public boolean process(Exchange exchange, AsyncCallback callback) { IgniteCompute compute = endpoint.createIgniteCompute().withAsync(); try { switch (executionTypeFor(exchange)) { case CALL: doCall(exchange, callback, compute); break; case BROADCAST: doBroadcast(exchange, callback, compute); break; case EXECUTE: doExecute(exchange, callback, compute); break; case RUN: doRun(exchange, callback, compute); break; case APPLY: doApply(exchange, callback, compute); break; case AFFINITY_CALL: doAffinityCall(exchange, callback, compute); break; case AFFINITY_RUN: doAffinityRun(exchange, callback, compute); break; default: exchange.setException(new UnsupportedOperationException("Operation not supported by Ignite Compute producer.")); return true; } compute.future().listen(IgniteInCamelClosure.create(exchange, callback)); } catch (Exception e) { exchange.setException(e); return true; } return false; } @SuppressWarnings({ "unchecked", "rawtypes" }) private void doCall(final Exchange exchange, final AsyncCallback callback, IgniteCompute compute) throws Exception { Object job = exchange.getIn().getBody(); IgniteReducer<Object, Object> reducer = exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_REDUCER, IgniteReducer.class); if (Collection.class.isAssignableFrom(job.getClass())) { Collection<?> col = (Collection<?>) job; TypeConverter tc = exchange.getContext().getTypeConverter(); Collection<IgniteCallable<?>> callables = new ArrayList<>(col.size()); for (Object o : col) { callables.add(tc.mandatoryConvertTo(IgniteCallable.class, o)); } if (reducer != null) { compute.call((Collection) callables, reducer); } else { compute.call((Collection) callables); } } else if (IgniteCallable.class.isAssignableFrom(job.getClass())) { compute.call((IgniteCallable<Object>) job); } else { throw new RuntimeCamelException(String.format( "Ignite Compute endpoint with CALL executionType is only " + "supported for IgniteCallable payloads, or collections of them. The payload type was: %s.", job.getClass().getName())); } } @SuppressWarnings("unchecked") private void doBroadcast(final Exchange exchange, final AsyncCallback callback, IgniteCompute compute) throws Exception { Object job = exchange.getIn().getBody(); if (IgniteCallable.class.isAssignableFrom(job.getClass())) { compute.broadcast((IgniteCallable<?>) job); } else if (IgniteRunnable.class.isAssignableFrom(job.getClass())) { compute.broadcast((IgniteRunnable) job); } else if (IgniteClosure.class.isAssignableFrom(job.getClass())) { compute.broadcast((IgniteClosure<Object, Object>) job, exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_PARAMS)); } else { throw new RuntimeCamelException( String.format("Ignite Compute endpoint with BROADCAST executionType is only " + "supported for IgniteCallable, IgniteRunnable or IgniteClosure payloads. The payload type was: %s.", job.getClass().getName())); } } @SuppressWarnings("unchecked") private void doExecute(final Exchange exchange, final AsyncCallback callback, IgniteCompute compute) throws Exception { Object job = exchange.getIn().getBody(); Object params = exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_PARAMS); if (job instanceof Class && ComputeTask.class.isAssignableFrom((Class<?>) job)) { Class<? extends ComputeTask<Object, Object>> task = (Class<? extends ComputeTask<Object, Object>>) job; compute.execute(task, params); } else if (ComputeTask.class.isAssignableFrom(job.getClass())) { compute.execute((ComputeTask<Object, Object>) job, params); } else if (endpoint.getTaskName() != null) { if (exchange.getIn().getBody() != null) { params = exchange.getIn().getBody(); } compute.execute(endpoint.getTaskName(), params); } else { throw new RuntimeCamelException(String.format("Ignite Compute endpoint with EXECUTE executionType is only " + "supported for ComputeTask payloads, Class<ComputeTask> or any payload in conjunction with the " + "task name option. The payload type was: %s.", job.getClass().getName())); } } private void doRun(final Exchange exchange, final AsyncCallback callback, IgniteCompute compute) throws Exception { Object job = exchange.getIn().getBody(); if (Collection.class.isAssignableFrom(job.getClass())) { Collection<?> col = (Collection<?>) job; TypeConverter tc = exchange.getContext().getTypeConverter(); Collection<IgniteRunnable> runnables = new ArrayList<>(col.size()); for (Object o : col) { runnables.add(tc.mandatoryConvertTo(IgniteRunnable.class, o)); } compute.run(runnables); } else if (IgniteRunnable.class.isAssignableFrom(job.getClass())) { compute.run((IgniteRunnable) job); } else { throw new RuntimeCamelException(String.format( "Ignite Compute endpoint with RUN executionType is only " + "supported for IgniteRunnable payloads, or collections of them. The payload type was: %s.", job.getClass().getName())); } } @SuppressWarnings("unchecked") private <T, R1, R2> void doApply(final Exchange exchange, final AsyncCallback callback, IgniteCompute compute) throws Exception { IgniteClosure<T, R1> job = exchange.getIn().getBody(IgniteClosure.class); T params = (T) exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_PARAMS); if (job == null || params == null) { throw new RuntimeCamelException( String.format("Ignite Compute endpoint with APPLY executionType is only " + "supported for IgniteClosure payloads with parameters. The payload type was: %s.", exchange.getIn().getBody().getClass().getName())); } IgniteReducer<R1, R2> reducer = exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_REDUCER, IgniteReducer.class); if (Collection.class.isAssignableFrom(params.getClass())) { Collection<T> colParams = (Collection<T>) params; if (reducer == null) { compute.apply(job, colParams); } else { compute.apply(job, colParams, reducer); } } else { compute.apply(job, params); } } @SuppressWarnings("unchecked") private void doAffinityCall(final Exchange exchange, final AsyncCallback callback, IgniteCompute compute) throws Exception { IgniteCallable<Object> job = exchange.getIn().getBody(IgniteCallable.class); String affinityCache = exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_AFFINITY_CACHE_NAME, String.class); Object affinityKey = exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_AFFINITY_KEY, Object.class); if (job == null || affinityCache == null || affinityKey == null) { throw new RuntimeCamelException(String.format( "Ignite Compute endpoint with AFFINITY_CALL executionType is only " + "supported for IgniteCallable payloads, along with an affinity cache and key. The payload type was: %s.", exchange.getIn().getBody().getClass().getName())); } compute.affinityCall(affinityCache, affinityKey, job); } private void doAffinityRun(final Exchange exchange, final AsyncCallback callback, IgniteCompute compute) throws Exception { IgniteRunnable job = exchange.getIn().getBody(IgniteRunnable.class); String affinityCache = exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_AFFINITY_CACHE_NAME, String.class); Object affinityKey = exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_AFFINITY_KEY, Object.class); if (job == null || affinityCache == null || affinityKey == null) { throw new RuntimeCamelException(String.format( "Ignite Compute endpoint with AFFINITY_RUN executionType is only " + "supported for IgniteRunnable payloads, along with an affinity cache and key. The payload type was: %s.", exchange.getIn().getBody().getClass().getName())); } compute.affinityRun(affinityCache, affinityKey, job); } private IgniteComputeExecutionType executionTypeFor(Exchange exchange) { return exchange.getIn().getHeader(IgniteConstants.IGNITE_COMPUTE_EXECUTION_TYPE, endpoint.getExecutionType(), IgniteComputeExecutionType.class); } private static class IgniteInCamelClosure implements IgniteInClosure<IgniteFuture<Object>> { private static final long serialVersionUID = 7486030906412223384L; private Exchange exchange; private AsyncCallback callback; private static IgniteInCamelClosure create(Exchange exchange, AsyncCallback callback) { IgniteInCamelClosure answer = new IgniteInCamelClosure(); answer.exchange = exchange; answer.callback = callback; return answer; } @Override public void apply(IgniteFuture<Object> future) { Message in = exchange.getIn(); Message out = exchange.getOut(); MessageHelper.copyHeaders(in, out, true); Object result = null; try { result = future.get(); } catch (Exception e) { exchange.setException(e); callback.done(false); return; } exchange.getOut().setBody(result); callback.done(false); } }; }