001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.impl.cloud;
018
019import org.apache.camel.AsyncCallback;
020import org.apache.camel.AsyncProcessor;
021import org.apache.camel.CamelContext;
022import org.apache.camel.Exchange;
023import org.apache.camel.ExchangePattern;
024import org.apache.camel.Expression;
025import org.apache.camel.Message;
026import org.apache.camel.cloud.ServiceDefinition;
027import org.apache.camel.cloud.ServiceLoadBalancer;
028import org.apache.camel.processor.SendDynamicProcessor;
029import org.apache.camel.support.ServiceSupport;
030import org.apache.camel.util.AsyncProcessorHelper;
031import org.apache.camel.util.ObjectHelper;
032import org.apache.camel.util.ServiceHelper;
033import org.apache.camel.util.StringHelper;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037public class DefaultServiceCallProcessor extends ServiceSupport implements AsyncProcessor {
038    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultServiceCallProcessor.class);
039
040    private final ExchangePattern exchangePattern;
041    private final String name;
042    private final String scheme;
043    private final String uri;
044    private final String contextPath;
045    private final CamelContext camelContext;
046    private final ServiceLoadBalancer loadBalancer;
047    private final Expression expression;
048    private SendDynamicProcessor processor;
049
050    public DefaultServiceCallProcessor(
051        CamelContext camelContext, String name, String scheme, String uri, ExchangePattern exchangePattern,
052        ServiceLoadBalancer loadBalancer, Expression expression) {
053
054        this.uri = uri;
055        this.exchangePattern = exchangePattern;
056        this.camelContext = camelContext;
057        this.loadBalancer = loadBalancer;
058
059        // setup from the provided name which can contain scheme and context-path information as well
060        String serviceName;
061        if (name.contains("/")) {
062            serviceName = StringHelper.before(name, "/");
063            this.contextPath = StringHelper.after(name, "/");
064        } else if (name.contains("?")) {
065            serviceName = StringHelper.before(name, "?");
066            this.contextPath = StringHelper.after(name, "?");
067        } else {
068            serviceName = name;
069            this.contextPath = null;
070        }
071        if (serviceName.contains(":")) {
072            this.scheme = StringHelper.before(serviceName, ":");
073            this.name = StringHelper.after(serviceName, ":");
074        } else {
075            this.scheme = scheme;
076            this.name = serviceName;
077        }
078
079        this.expression = expression;
080    }
081
082    // *************************************
083    // Properties
084    // *************************************
085
086
087    public ExchangePattern getExchangePattern() {
088        return exchangePattern;
089    }
090
091    public String getName() {
092        return name;
093    }
094
095    public String getScheme() {
096        return scheme;
097    }
098
099    public String getUri() {
100        return uri;
101    }
102
103    public String getContextPath() {
104        return contextPath;
105    }
106
107    public ServiceLoadBalancer getLoadBalancer() {
108        return loadBalancer;
109    }
110
111    public Expression getExpression() {
112        return expression;
113    }
114
115    // *************************************
116    // Lifecycle
117    // *************************************
118
119    @Override
120    protected void doStart() throws Exception {
121        StringHelper.notEmpty(name, "name", "service name");
122        ObjectHelper.notNull(camelContext, "camel context");
123        ObjectHelper.notNull(expression, "expression");
124        ObjectHelper.notNull(loadBalancer, "load balancer");
125
126        processor = new SendDynamicProcessor(uri, expression);
127        processor.setCamelContext(camelContext);
128        if (exchangePattern != null) {
129            processor.setPattern(exchangePattern);
130        }
131
132        // Start services if needed
133        ServiceHelper.startService(processor);
134        ServiceHelper.startService(loadBalancer);
135    }
136
137    @Override
138    protected void doStop() throws Exception {
139        // Stop services if needed
140        ServiceHelper.stopService(loadBalancer);
141        ServiceHelper.stopService(processor);
142    }
143
144    // *************************************
145    // Processor
146    // *************************************
147
148
149    @Override
150    public void process(Exchange exchange) throws Exception {
151        AsyncProcessorHelper.process(this, exchange);
152    }
153
154    @Override
155    public boolean process(final Exchange exchange, final AsyncCallback callback) {
156        Message message = exchange.getIn();
157        message.setHeader(ServiceCallConstants.SERVICE_CALL_URI, uri);
158        message.setHeader(ServiceCallConstants.SERVICE_CALL_CONTEXT_PATH, contextPath);
159        message.setHeader(ServiceCallConstants.SERVICE_CALL_SCHEME, scheme);
160
161        String serviceName = message.getHeader(ServiceCallConstants.SERVICE_NAME, name, String.class);
162
163        try {
164            return loadBalancer.process(serviceName, server -> execute(server, exchange, callback));
165        } catch (Exception e) {
166            exchange.setException(e);
167            return true;
168        }
169    }
170
171    private boolean execute(ServiceDefinition server, Exchange exchange, AsyncCallback callback) throws Exception {
172        String host = server.getHost();
173        int port = server.getPort();
174
175        LOGGER.debug("Service {} active at server: {}:{}", name, host, port);
176
177        // set selected server as header
178        exchange.getIn().setHeader(ServiceCallConstants.SERVICE_HOST, host);
179        exchange.getIn().setHeader(ServiceCallConstants.SERVICE_PORT, port > 0 ? port : null);
180        exchange.getIn().setHeader(ServiceCallConstants.SERVICE_NAME, server.getName());
181        exchange.getIn().setHeader(ServiceCallConstants.SERVICE_META, server.getMetadata());
182
183        // use the dynamic send processor to call the service
184        return processor.process(exchange, callback);
185    }
186}