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 java.util.HashMap;
020import java.util.Map;
021import java.util.Optional;
022
023import org.apache.camel.CamelContext;
024import org.apache.camel.CamelContextAware;
025import org.apache.camel.Endpoint;
026import org.apache.camel.Route;
027import org.apache.camel.api.management.ManagedResource;
028import org.apache.camel.cloud.DiscoverableService;
029import org.apache.camel.cloud.ServiceDefinition;
030import org.apache.camel.cloud.ServiceRegistry;
031import org.apache.camel.support.RoutePolicySupport;
032import org.apache.camel.util.ObjectHelper;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036@ManagedResource(description = "Service Registration Route policy")
037public class ServiceRegistrationRoutePolicy extends RoutePolicySupport implements CamelContextAware {
038    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistrationRoutePolicy.class);
039
040    private final ServiceRegistry.Selector serviceRegistrySelector;
041
042    private ServiceRegistry serviceRegistry;
043    private CamelContext camelContext;
044
045    public ServiceRegistrationRoutePolicy() {
046        this(null, ServiceRegistrySelectors.DEFAULT_SELECTOR);
047    }
048
049    public ServiceRegistrationRoutePolicy(ServiceRegistry.Selector serviceRegistrySelector) {
050        this(null, serviceRegistrySelector);
051    }
052
053    public ServiceRegistrationRoutePolicy(ServiceRegistry serviceRegistry, ServiceRegistry.Selector serviceRegistrySelector) {
054        this.serviceRegistry = serviceRegistry;
055        this.serviceRegistrySelector = serviceRegistrySelector;
056    }
057
058    @Override
059    public CamelContext getCamelContext() {
060        return camelContext;
061    }
062
063    @Override
064    public void setCamelContext(CamelContext camelContext) {
065        this.camelContext = camelContext;
066    }
067
068    // ***********************
069    // policy life-cycle
070    // ***********************
071
072    @Override
073    public void doStart() throws Exception {
074        if (serviceRegistry == null) {
075            serviceRegistry = ServiceRegistryHelper.lookupService(camelContext, serviceRegistrySelector).orElseThrow(
076                () -> new IllegalStateException("ServiceRegistry service not found")
077            );
078        }
079
080        LOGGER.debug("ServiceRegistrationRoutePolicy {} is using ServiceRegistry instance {} (id={}, type={})",
081            this,
082            serviceRegistry,
083            serviceRegistry.getId(),
084            serviceRegistry.getClass().getName()
085        );
086    }
087
088    // ***********************
089    // route life-cycle
090    // ***********************
091
092    @Override
093    public void onStart(Route route) {
094        register(route);
095    }
096
097    @Override
098    public void onStop(Route route) {
099        deregister(route);
100    }
101
102    @Override
103    public void onSuspend(Route route) {
104        deregister(route);
105    }
106
107    @Override
108    public void onResume(Route route) {
109        register(route);
110    }
111
112    // ***********************
113    // registration helpers
114    // ***********************
115
116    private void register(Route route) {
117        computeServiceDefinition(route).ifPresent(serviceRegistry::register);
118    }
119
120    private void deregister(Route route) {
121        computeServiceDefinition(route).ifPresent(serviceRegistry::deregister);
122    }
123
124    private Optional<ServiceDefinition> computeServiceDefinition(Route route) {
125        final Endpoint endpoint = route.getConsumer().getEndpoint();
126        final Map<String, String> properties = new HashMap<>();
127
128        if (endpoint instanceof DiscoverableService) {
129            final DiscoverableService service = (DiscoverableService) endpoint;
130
131            // first load all the properties from the endpoint
132            properties.putAll(service.getServiceProperties());
133        }
134
135        // then add additional properties from route with ServiceDefinition.SERVICE_META_PREFIX,
136        // note that route defined properties may override DiscoverableService
137        // provided ones
138        for (Map.Entry<String, Object> entry: route.getProperties().entrySet()) {
139            if (!entry.getKey().startsWith(ServiceDefinition.SERVICE_META_PREFIX)) {
140                continue;
141            }
142
143            final String key = entry.getKey();
144            final String val = camelContext.getTypeConverter().convertTo(String.class, entry.getValue());
145
146            properties.put(key, val);
147        }
148
149        // try to get the service name from route properties
150        String serviceName = properties.get(ServiceDefinition.SERVICE_META_NAME);
151        if (serviceName == null) {
152            // if not check if the route group is defined use the route group
153            serviceName = route.getGroup();
154
155            if (serviceName != null) {
156                properties.put(ServiceDefinition.SERVICE_META_NAME, serviceName);
157            }
158        }
159
160        if (ObjectHelper.isEmpty(serviceName)) {
161            LOGGER.debug("Route {} has not enough information for service registration", route);
162            return Optional.empty();
163        }
164
165        // try to get the service id from route properties
166        String serviceId = properties.get(ServiceDefinition.SERVICE_META_ID);
167        if (serviceId == null) {
168            // if not check if the route id is custom and use it
169            if (route.getRouteContext().getRoute().hasCustomIdAssigned()) {
170                serviceId = route.getId();
171            }
172
173            if (serviceId != null) {
174                properties.put(ServiceDefinition.SERVICE_META_ID, serviceId);
175            }
176        }
177        if (serviceId == null) {
178            // finally auto generate the service id
179            serviceId = getCamelContext().getUuidGenerator().generateUuid();
180        }
181
182        final String serviceHost = properties.get(ServiceDefinition.SERVICE_META_HOST);
183        final String servicePort = properties.getOrDefault(ServiceDefinition.SERVICE_META_PORT, "-1");
184
185        // Build the final resource definition from bits collected from the
186        // endpoint and the route.
187        return Optional.of(
188            new DefaultServiceDefinition(
189                serviceId,
190                serviceName,
191                serviceHost,
192                Integer.parseInt(servicePort),
193                properties
194            )
195        );
196    }
197}
198