/* * Copyright 2017 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.kie.camel; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collector; import java.util.stream.Stream; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.impl.DefaultProducer; import org.kie.api.KieServices; import org.kie.api.command.Command; import org.kie.server.api.model.ServiceResponse; import org.kie.server.client.DMNServicesClient; import org.kie.server.client.KieServicesClient; import org.kie.server.client.KieServicesFactory; import org.kie.server.client.ProcessServicesClient; import org.kie.server.client.RuleServicesClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.stream.Collectors.groupingBy; import static org.kie.camel.KieCamelConstants.*; import static org.kie.camel.KieCamelUtils.asCamelKieName; import static org.kie.camel.KieCamelUtils.getResultMessage; import static org.kie.camel.KieCamelUtils.ucFirst; public class KieProducer extends DefaultProducer { private static final transient Logger log = LoggerFactory.getLogger( KieProducer.class ); private static final String DEFAULT_CLIENT = "KieServices"; private final KieEndpoint endpoint; private final KieServicesClient client; private final Map<String, InternalProducer> producers = new HashMap<>(); public KieProducer( KieEndpoint endpoint ) { super(endpoint); this.endpoint = endpoint; client = KieServicesFactory.newKieServicesClient( endpoint.getKieServicesConf() ); } @Override public void process( Exchange exchange ) throws Exception { getProducer(exchange).execute(exchange); } private InternalProducer getProducer(Exchange exchange) { String clientName = endpoint.getClient() != null ? endpoint.getClient() : exchange.getIn().getHeader( KIE_CLIENT, DEFAULT_CLIENT, String.class ); return producers.computeIfAbsent( clientName, name -> { String producerName = KieProducer.class.getName() + "$" + ucFirst( name ) + "Producer"; try { Class<?> producerClass = Class.forName( producerName ); return (InternalProducer) producerClass.getConstructor( KieServicesClient.class, String.class, KieEndpoint.class ) .newInstance( client, clientName, endpoint ); } catch (Exception e) { log.error( "Unknown client name: " + clientName ); return new DummyProducer(); } } ); } interface InternalProducer { void execute(Exchange exchange); } abstract static class AbstractInternalProducer<C> implements InternalProducer { protected final C client; private final Optional<Method> operationLookupMethod; protected AbstractInternalProducer(C client) { this.client = client; operationLookupMethod = getLookupMethod(); } protected Optional<Operation<C>> getOperation( String operationName ) { return operationLookupMethod.flatMap( m -> { try { return Optional.of( (Operation<C>) m.invoke( null, operationName ) ); } catch (IllegalAccessException | InvocationTargetException e) { return Optional.empty(); } } ); } private Optional<Method> getLookupMethod() { try { Class<?> enumClass = Class.forName( getClass().getName() + "$Operations" ); return Optional.of(enumClass.getMethod( "valueOf", String.class )); } catch (Exception e) { return Optional.empty(); } } } abstract static class AbstractReflectiveProducer<C> extends AbstractInternalProducer<C> { private final Map<String, Collection<Method>> methodsMap; private final String clientName; private final KieEndpoint endpoint; protected AbstractReflectiveProducer(C client, String clientName, KieEndpoint endpoint) { super(client); this.clientName = clientName; this.endpoint = endpoint; this.methodsMap = indexClientMethod( (Class) ( (ParameterizedType) getClass().getGenericSuperclass() ).getActualTypeArguments()[0] ); } @Override public final void execute(Exchange exchange) { String operationName = endpoint.getOperation() != null ? endpoint.getOperation() : exchange.getIn().getHeader( KIE_OPERATION, String.class ); Object response = getOperation(operationName).map(op -> op.execute( client, exchange ) ) .orElseGet( () -> executeViaReflection( operationName, exchange ) ); writeResponse( exchange, response ); } private void writeResponse( Exchange exchange, Object response ) { if (response instanceof ServiceResponse ) { ServiceResponse serviceResponse = (ServiceResponse) response; Message message = getResultMessage(exchange); message.setBody( serviceResponse.getResult() ); message.setHeader( RESPONSE_TYPE, serviceResponse.getType() ); message.setHeader( RESPONSE_MESSAGE, serviceResponse.getMsg() ); } else { getResultMessage(exchange).setBody( response ); } } private Object executeViaReflection( String operationName, Exchange exchange ) { Collection<Method> methods = methodsMap.get( operationName ); if (methods == null) { log.error( "Unknown operation name: " + operationName ); return null; } String bodyParam = endpoint.getConfiguration().getBodyParam( clientName, operationName ); Method method = methods.stream() .filter( m -> invokable( exchange, m, bodyParam ) ) .findFirst() .orElseGet( () -> { log.error( "Unknown operation name: " + operationName ); return null; } ); return method != null ? invoke( exchange, method, bodyParam ) : null; } private boolean invokable(Exchange exchange, Method method, String bodyParam) { Set<String> headers = exchange.getIn().getHeaders().keySet(); return Stream.of(method.getParameters()).allMatch( p -> p.getName().equals( bodyParam ) || headers.contains( asCamelKieName( p.getName() ) ) ); } private Object invoke(Exchange exchange, Method method, String bodyParam) { try { Object[] args = Stream.of(method.getParameters()) .map(p -> p.getName().equals( bodyParam ) ? exchange.getIn().getBody( p.getType() ) : exchange.getIn().getHeader( asCamelKieName( p.getName() ), p.getType() ) ) .toArray(); return method.invoke(client, args); } catch (Exception e) { log.error( "Error executed operation: " + method.getName() + " caused by: " + e.getMessage(), e ); return null; } } private Map<String, Collection<Method>> indexClientMethod(Class<?> cls) { return Stream.of(cls.getMethods()).collect( groupingBy(Method::getName, Collector.of(() -> new TreeSet<Method>( (m1,m2) -> m2.getParameterCount() - m1.getParameterCount() ), Collection::add, (left, right) -> { left.addAll(right); return left; })) ); } } interface Operation<C> { Object execute(C client, Exchange exchange); } static class DummyProducer implements InternalProducer { @Override public void execute( Exchange exchange ) { } } static class KieServicesProducer extends AbstractReflectiveProducer<KieServicesClient> { public KieServicesProducer(KieServicesClient client, String clientName, KieEndpoint endpoint) { super(client, clientName, endpoint); } enum Operations implements Operation<KieServicesClient> { myCustomOperation { @Override public Object execute(KieServicesClient client, Exchange exchange) { return client.getServerInfo(); } } } } static class RuleProducer extends AbstractReflectiveProducer<RuleServicesClient> { public RuleProducer(KieServicesClient client, String clientName, KieEndpoint endpoint) { super( client.getServicesClient(RuleServicesClient.class), clientName, endpoint ); } enum Operations implements Operation<RuleServicesClient> { fireAllRules { @Override public Object execute(RuleServicesClient client, Exchange exchange) { String containerId = exchange.getIn().getHeader( "containerId", String.class ); Command fireCommand = KieServices.get().getCommands().newFireAllRules(); return client.executeCommandsWithResults(containerId, fireCommand); } } } } static class ProcessProducer extends AbstractReflectiveProducer<ProcessServicesClient> { public ProcessProducer(KieServicesClient client, String clientName, KieEndpoint endpoint) { super( client.getServicesClient(ProcessServicesClient.class), clientName, endpoint ); } } static class DmnProducer extends AbstractReflectiveProducer<DMNServicesClient> { public DmnProducer(KieServicesClient client, String clientName, KieEndpoint endpoint) { super( client.getServicesClient(DMNServicesClient.class), clientName, endpoint ); } } }