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; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.Date; 023import java.util.LinkedList; 024import java.util.List; 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.concurrent.ConcurrentMap; 027import java.util.concurrent.atomic.AtomicInteger; 028import java.util.stream.Collectors; 029import java.util.stream.Stream; 030 031import org.apache.camel.Endpoint; 032import org.apache.camel.Exchange; 033import org.apache.camel.MessageHistory; 034import org.apache.camel.spi.InflightRepository; 035import org.apache.camel.support.ServiceSupport; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * Default {@link org.apache.camel.spi.InflightRepository}. 041 * 042 * @version 043 */ 044public class DefaultInflightRepository extends ServiceSupport implements InflightRepository { 045 046 private static final Logger LOG = LoggerFactory.getLogger(DefaultInflightRepository.class); 047 private final ConcurrentMap<String, Exchange> inflight = new ConcurrentHashMap<String, Exchange>(); 048 private final ConcurrentMap<String, AtomicInteger> routeCount = new ConcurrentHashMap<String, AtomicInteger>(); 049 050 public void add(Exchange exchange) { 051 inflight.put(exchange.getExchangeId(), exchange); 052 } 053 054 public void remove(Exchange exchange) { 055 inflight.remove(exchange.getExchangeId()); 056 } 057 058 public void add(Exchange exchange, String routeId) { 059 AtomicInteger existing = routeCount.get(routeId); 060 if (existing != null) { 061 existing.incrementAndGet(); 062 } 063 } 064 065 public void remove(Exchange exchange, String routeId) { 066 AtomicInteger existing = routeCount.get(routeId); 067 if (existing != null) { 068 existing.decrementAndGet(); 069 } 070 } 071 072 public int size() { 073 return inflight.size(); 074 } 075 076 @Deprecated 077 public int size(Endpoint endpoint) { 078 return 0; 079 } 080 081 @Override 082 public void addRoute(String routeId) { 083 routeCount.putIfAbsent(routeId, new AtomicInteger(0)); 084 } 085 086 @Override 087 public void removeRoute(String routeId) { 088 routeCount.remove(routeId); 089 } 090 091 @Override 092 public int size(String routeId) { 093 AtomicInteger existing = routeCount.get(routeId); 094 return existing != null ? existing.get() : 0; 095 } 096 097 @Override 098 public Collection<InflightExchange> browse() { 099 return browse(null, -1, false); 100 } 101 102 @Override 103 public Collection<InflightExchange> browse(String fromRouteId) { 104 return browse(fromRouteId, -1, false); 105 } 106 107 @Override 108 public Collection<InflightExchange> browse(int limit, boolean sortByLongestDuration) { 109 return browse(null, limit, sortByLongestDuration); 110 } 111 112 @Override 113 public Collection<InflightExchange> browse(String fromRouteId, int limit, boolean sortByLongestDuration) { 114 Stream<Exchange> values; 115 if (fromRouteId == null) { 116 // all values 117 values = inflight.values().stream(); 118 } else { 119 // only if route match 120 values = inflight.values().stream() 121 .filter(e -> fromRouteId.equals(e.getFromRouteId())); 122 } 123 124 if (sortByLongestDuration) { 125 // sort by duration and grab the first 126 values = values.sorted((e1, e2) -> { 127 long d1 = getExchangeDuration(e1); 128 long d2 = getExchangeDuration(e2); 129 // need the biggest number first 130 return -1 * Long.compare(d1, d2); 131 }); 132 } else { 133 // else sort by exchange id 134 values = values.sorted(Comparator.comparing(Exchange::getExchangeId)); 135 } 136 137 if (limit > 0) { 138 values = values.limit(limit); 139 } 140 141 List<InflightExchange> answer = values.map(InflightExchangeEntry::new).collect(Collectors.toList()); 142 return Collections.unmodifiableCollection(answer); 143 } 144 145 @Override 146 public InflightExchange oldest(String fromRouteId) { 147 Stream<Exchange> values; 148 149 if (fromRouteId == null) { 150 // all values 151 values = inflight.values().stream(); 152 } else { 153 // only if route match 154 values = inflight.values().stream() 155 .filter(e -> fromRouteId.equals(e.getFromRouteId())); 156 } 157 158 // sort by duration and grab the first 159 Exchange first = values.sorted((e1, e2) -> { 160 long d1 = getExchangeDuration(e1); 161 long d2 = getExchangeDuration(e2); 162 // need the biggest number first 163 return -1 * Long.compare(d1, d2); 164 }).findFirst().orElse(null); 165 166 if (first != null) { 167 return new InflightExchangeEntry(first); 168 } else { 169 return null; 170 } 171 } 172 173 @Override 174 protected void doStart() throws Exception { 175 } 176 177 @Override 178 protected void doStop() throws Exception { 179 int count = size(); 180 if (count > 0) { 181 LOG.warn("Shutting down while there are still {} inflight exchanges.", count); 182 } else { 183 LOG.debug("Shutting down with no inflight exchanges."); 184 } 185 routeCount.clear(); 186 } 187 188 private static long getExchangeDuration(Exchange exchange) { 189 long duration = 0; 190 Date created = exchange.getCreated(); 191 if (created != null) { 192 duration = System.currentTimeMillis() - created.getTime(); 193 } 194 return duration; 195 } 196 197 private static final class InflightExchangeEntry implements InflightExchange { 198 199 private final Exchange exchange; 200 201 private InflightExchangeEntry(Exchange exchange) { 202 this.exchange = exchange; 203 } 204 205 @Override 206 public Exchange getExchange() { 207 return exchange; 208 } 209 210 @Override 211 public long getDuration() { 212 return DefaultInflightRepository.getExchangeDuration(exchange); 213 } 214 215 @Override 216 @SuppressWarnings("unchecked") 217 public long getElapsed() { 218 LinkedList<MessageHistory> list = exchange.getProperty(Exchange.MESSAGE_HISTORY, LinkedList.class); 219 if (list == null || list.isEmpty()) { 220 return 0; 221 } 222 223 // get latest entry 224 MessageHistory history = list.getLast(); 225 if (history != null) { 226 return history.getElapsed(); 227 } else { 228 return 0; 229 } 230 } 231 232 @Override 233 @SuppressWarnings("unchecked") 234 public String getNodeId() { 235 LinkedList<MessageHistory> list = exchange.getProperty(Exchange.MESSAGE_HISTORY, LinkedList.class); 236 if (list == null || list.isEmpty()) { 237 return null; 238 } 239 240 // get latest entry 241 MessageHistory history = list.getLast(); 242 if (history != null) { 243 return history.getNode().getId(); 244 } else { 245 return null; 246 } 247 } 248 249 @Override 250 public String getFromRouteId() { 251 return exchange.getFromRouteId(); 252 } 253 254 @Override 255 public String getRouteId() { 256 return getAtRouteId(); 257 } 258 259 @Override 260 @SuppressWarnings("unchecked") 261 public String getAtRouteId() { 262 LinkedList<MessageHistory> list = exchange.getProperty(Exchange.MESSAGE_HISTORY, LinkedList.class); 263 if (list == null || list.isEmpty()) { 264 return null; 265 } 266 267 // get latest entry 268 MessageHistory history = list.getLast(); 269 if (history != null) { 270 return history.getRouteId(); 271 } else { 272 return null; 273 } 274 } 275 276 @Override 277 public String toString() { 278 return "InflightExchangeEntry[exchangeId=" + exchange.getExchangeId() + "]"; 279 } 280 } 281 282}