Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ protected void doStop() throws Exception {
PlatformHttpComponent platformHttpComponent
= (PlatformHttpComponent) camelContext.hasComponent("platform-http");
if (platformHttpComponent != null && info != null) {
platformHttpComponent.removeHttpEndpoint(info.path());
platformHttpComponent.removeHttpEndpoint(info.path(), null);
}
transport = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
package org.apache.camel.component.platform.http.main;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.camel.CamelContext;
import org.apache.camel.StartupListener;
Expand All @@ -35,14 +37,15 @@ protected static void setupStartupSummary(
CamelContext camelContext, Set<HttpEndpointModel> endpoints, int serverPort, boolean ssl, String header)
throws Exception {
camelContext.addStartupListener(new StartupListener() {
private volatile Set<HttpEndpointModel> last;
private volatile Set<String> lastEndpointSignatures;

private void logSummary() {
if (endpoints.isEmpty()) {
return;
}
// log only if changed
if (last == null || last.size() != endpoints.size() || !last.containsAll(endpoints)) {
// log only if changed (ignore consumer identity on route reload)
Set<String> currentSignatures = endpointSignatures(endpoints);
if (lastEndpointSignatures == null || !lastEndpointSignatures.equals(currentSignatures)) {
LOG.info(header);
int longestEndpoint = 0;
int longestVerbs = 0;
Expand Down Expand Up @@ -78,8 +81,19 @@ private void logSummary() {
}
}

// use a defensive copy of last known endpoints
last = new HashSet<>(endpoints);
lastEndpointSignatures = currentSignatures;
}

private Set<String> endpointSignatures(Set<HttpEndpointModel> endpointModels) {
return endpointModels.stream()
.map(this::endpointSignature)
.collect(Collectors.toCollection(HashSet::new));
}

private String endpointSignature(HttpEndpointModel model) {
return model.getUri() + "|" + Objects.toString(model.getVerbs(), "") + "|"
+ Objects.toString(model.getConsumes(), "") + "|"
+ Objects.toString(model.getProduces(), "");
}

private String getEndpoint(HttpEndpointModel httpEndpointModel, boolean ssl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ protected void configurePlatformHttpConsumer(PlatformHttpConsumer platformHttpCo
protected void doStart() throws Exception {
super.doStart();
ServiceHelper.startService(platformHttpConsumer);
if (register) {
if (register && platformHttpConsumer != null) {
getComponent().addHttpEndpoint(getEndpoint().getPath(), getEndpoint().getHttpMethodRestrict(),
getEndpoint().getConsumes(), getEndpoint().getProduces(), platformHttpConsumer);
}
Expand All @@ -116,8 +116,8 @@ protected void doStart() throws Exception {
@Override
protected void doStop() throws Exception {
super.doStop();
if (register) {
getComponent().removeHttpEndpoint(getEndpoint().getPath());
if (register && platformHttpConsumer != null) {
Comment thread
davsclaus marked this conversation as resolved.
getComponent().removeHttpEndpoint(platformHttpConsumer);
}
ServiceHelper.stopAndShutdownServices(platformHttpConsumer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,29 @@ public boolean equals(Object o) {
return false;
}
HttpEndpointModel that = (HttpEndpointModel) o;
return uri.equals(that.uri);
return uri.equals(that.uri) && consumer == that.consumer;
}

@Override
public int hashCode() {
return Objects.hash(uri);
return Objects.hash(uri, consumer);
}

@Override
public int compareTo(HttpEndpointModel o) {
return uri.compareTo(o.uri);
int cmp = uri.compareTo(o.uri);
if (cmp != 0) {
return cmp;
}
if (consumer == o.consumer) {
return 0;
}
if (consumer == null) {
return -1;
}
if (o.consumer == null) {
return 1;
}
return Integer.compare(System.identityHashCode(consumer), System.identityHashCode(o.consumer));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;

import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
Expand Down Expand Up @@ -72,8 +73,8 @@ public class PlatformHttpComponent extends HeaderFilterStrategyComponent
+ " or all requests must be handled by Camel.")
private boolean serverRequestValidation = true;

private final Set<HttpEndpointModel> httpEndpoints = new TreeSet<>();
private final Set<HttpEndpointModel> httpManagementEndpoints = new TreeSet<>();
private final Set<HttpEndpointModel> httpEndpoints = new LinkedHashSet<>();
Comment thread
davsclaus marked this conversation as resolved.
private final Set<HttpEndpointModel> httpManagementEndpoints = new LinkedHashSet<>();
private final List<PlatformHttpListener> listeners = new ArrayList<>();
private volatile boolean localEngine;

Expand Down Expand Up @@ -164,19 +165,54 @@ private void addHttpEndpoint(
* Removes a known http endpoint managed by this component.
*/
public void removeHttpEndpoint(String uri) {
this.removeHttpEndpoint(this.httpEndpoints, uri);
removeHttpEndpoints(this.httpEndpoints, e -> e.getUri().equals(uri));
}

/**
* Removes the http endpoint registered for the given consumer.
*/
public void removeHttpEndpoint(Consumer consumer) {
Comment thread
davsclaus marked this conversation as resolved.
if (consumer == null) {
return;
}
removeHttpEndpoints(this.httpEndpoints, e -> e.getConsumer() == consumer);
}

/**
* Removes the http endpoint registered for the given uri and consumer reference.
* <p>
* Use this when multiple registrations share the same uri but have different consumers, or when the registration
* used a {@code null} consumer (for example MCP server metadata).
* </p>
*/
public void removeHttpEndpoint(String uri, Consumer consumer) {
removeHttpEndpoints(this.httpEndpoints, e -> e.getUri().equals(uri) && e.getConsumer() == consumer);
}

/**
* Removes a known http endpoint managed by this component.
*/
public void removeHttpManagementEndpoint(String uri) {
this.removeHttpEndpoint(this.httpManagementEndpoints, uri);
removeHttpEndpoints(this.httpManagementEndpoints, e -> e.getUri().equals(uri));
}

/**
* Removes the http management endpoint registered for the given consumer.
* <p>
* Provided for symmetry with {@link #removeHttpManagementEndpoint(String)} for callers that track a management
* consumer reference.
* </p>
*/
public void removeHttpManagementEndpoint(Consumer consumer) {
Comment thread
davsclaus marked this conversation as resolved.
if (consumer == null) {
return;
}
removeHttpEndpoints(this.httpManagementEndpoints, e -> e.getConsumer() == consumer);
}

private void removeHttpEndpoint(Set<HttpEndpointModel> endpoints, String uri) {
private void removeHttpEndpoints(Set<HttpEndpointModel> endpoints, Predicate<HttpEndpointModel> filter) {
List<HttpEndpointModel> toRemove = new ArrayList<>();
endpoints.stream().filter(e -> e.getUri().equals(uri)).forEach(model -> {
endpoints.stream().filter(filter).forEach(model -> {
toRemove.add(model);
for (PlatformHttpListener listener : listeners) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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.platform.http;

import java.util.HashSet;
import java.util.Set;

import org.apache.camel.Consumer;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;

class HttpEndpointModelTest {

@Test
void setRetainsMultipleConsumersOnSamePath() {
Consumer getConsumer = mock(Consumer.class);
Consumer postConsumer = mock(Consumer.class);

HttpEndpointModel getModel = new HttpEndpointModel("/shared", "GET", null, null, getConsumer);
HttpEndpointModel postModel = new HttpEndpointModel("/shared", "POST", null, null, postConsumer);

Set<HttpEndpointModel> endpoints = new HashSet<>();
assertTrue(endpoints.add(getModel));
assertTrue(endpoints.add(postModel));
assertEquals(2, endpoints.size());
}

@Test
void equalsAndHashCodeUseConsumerIdentity() {
Consumer first = mock(Consumer.class);
Consumer second = mock(Consumer.class);

HttpEndpointModel firstModel = new HttpEndpointModel("/shared", "GET", null, null, first);
HttpEndpointModel secondModel = new HttpEndpointModel("/shared", "POST", null, null, second);
HttpEndpointModel sameConsumerModel = new HttpEndpointModel("/shared", "GET", null, null, first);

assertNotEquals(firstModel, secondModel);
assertEquals(firstModel, sameConsumerModel);
assertEquals(firstModel.hashCode(), sameConsumerModel.hashCode());
}

@Test
void compareToIsConsistentWithEquals() {
Consumer first = mock(Consumer.class);
Consumer second = mock(Consumer.class);

HttpEndpointModel firstModel = new HttpEndpointModel("/shared", "GET", null, null, first);
HttpEndpointModel secondModel = new HttpEndpointModel("/shared", "POST", null, null, second);
HttpEndpointModel sameConsumerModel = new HttpEndpointModel("/shared", "GET", null, null, first);

assertEquals(0, firstModel.compareTo(sameConsumerModel));
assertNotEquals(0, firstModel.compareTo(secondModel));
}
}
Loading