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.management.mbean; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.Set; 023import java.util.TreeSet; 024 025import javax.management.openmbean.CompositeData; 026import javax.management.openmbean.CompositeDataSupport; 027import javax.management.openmbean.CompositeType; 028import javax.management.openmbean.TabularData; 029import javax.management.openmbean.TabularDataSupport; 030 031import org.apache.camel.CamelContext; 032import org.apache.camel.Route; 033import org.apache.camel.RuntimeCamelException; 034import org.apache.camel.api.management.ManagedResource; 035import org.apache.camel.api.management.mbean.CamelOpenMBeanTypes; 036import org.apache.camel.api.management.mbean.ManagedSupervisingRouteControllerMBean; 037import org.apache.camel.spi.SupervisingRouteController; 038import org.apache.camel.support.ExceptionHelper; 039import org.apache.camel.util.TimeUtils; 040import org.apache.camel.util.backoff.BackOffTimer; 041 042@ManagedResource(description = "Managed SupervisingRouteController") 043public class ManagedSupervisingRouteController extends ManagedService implements ManagedSupervisingRouteControllerMBean { 044 045 private final SupervisingRouteController controller; 046 047 public ManagedSupervisingRouteController(CamelContext context, SupervisingRouteController controller) { 048 super(context, controller); 049 this.controller = controller; 050 } 051 052 public SupervisingRouteController getRouteController() { 053 return controller; 054 } 055 056 @Override 057 public boolean isEnabled() { 058 return true; 059 } 060 061 @Override 062 public int getThreadPoolSize() { 063 return controller.getThreadPoolSize(); 064 } 065 066 @Override 067 public long getInitialDelay() { 068 return controller.getInitialDelay(); 069 } 070 071 @Override 072 public long getBackOffDelay() { 073 return controller.getBackOffDelay(); 074 } 075 076 @Override 077 public long getBackOffMaxDelay() { 078 return controller.getBackOffMaxDelay(); 079 } 080 081 @Override 082 public long getBackOffMaxElapsedTime() { 083 return controller.getBackOffMaxElapsedTime(); 084 } 085 086 @Override 087 public long getBackOffMaxAttempts() { 088 return controller.getBackOffMaxAttempts(); 089 } 090 091 @Override 092 public double getBackOffMultiplier() { 093 return controller.getBackOffMultiplier(); 094 } 095 096 @Override 097 public String getIncludeRoutes() { 098 return controller.getIncludeRoutes(); 099 } 100 101 @Override 102 public String getExcludeRoutes() { 103 return controller.getExcludeRoutes(); 104 } 105 106 @Override 107 public boolean isUnhealthyOnExhausted() { 108 return controller.isUnhealthyOnExhausted(); 109 } 110 111 @Override 112 public boolean isUnhealthyOnRestarting() { 113 return controller.isUnhealthyOnRestarting(); 114 } 115 116 @Override 117 public boolean isStartingRoutes() { 118 return controller.isStartingRoutes(); 119 } 120 121 @Override 122 public boolean isHasUnhealthyRoutes() { 123 return controller.hasUnhealthyRoutes(); 124 } 125 126 @Override 127 public int getNumberOfControlledRoutes() { 128 return controller.getControlledRoutes().size(); 129 } 130 131 @Override 132 public int getNumberOfRestartingRoutes() { 133 return controller.getRestartingRoutes().size(); 134 } 135 136 @Override 137 public int getNumberOfExhaustedRoutes() { 138 return controller.getExhaustedRoutes().size(); 139 } 140 141 @Override 142 public Collection<String> getControlledRoutes() { 143 if (controller != null) { 144 return controller.getControlledRoutes().stream() 145 .map(Route::getId) 146 .toList(); 147 } 148 149 return Collections.emptyList(); 150 } 151 152 @Override 153 public String getRouteStartupLoggingLevel() { 154 if (controller != null) { 155 return controller.getLoggingLevel().name(); 156 } else { 157 return null; 158 } 159 } 160 161 @Override 162 public Collection<String> getRestartingRoutes() { 163 if (controller != null) { 164 return controller.getRestartingRoutes().stream() 165 .map(Route::getId) 166 .toList(); 167 } 168 169 return Collections.emptyList(); 170 } 171 172 @Override 173 public Collection<String> getExhaustedRoutes() { 174 if (controller != null) { 175 return controller.getExhaustedRoutes().stream() 176 .map(Route::getId) 177 .toList(); 178 } 179 180 return Collections.emptyList(); 181 } 182 183 @Override 184 public TabularData routeStatus(boolean exhausted, boolean restarting, boolean includeStacktrace) { 185 try { 186 TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.supervisingRouteControllerRouteStatusTabularType()); 187 188 int index = 0; 189 Set<Route> routes = new TreeSet<>(Comparator.comparing(Route::getId)); 190 routes.addAll(controller.getControlledRoutes()); 191 if (exhausted) { 192 routes.addAll(controller.getExhaustedRoutes()); 193 } 194 if (restarting) { 195 routes.addAll(controller.getRestartingRoutes()); 196 } 197 198 for (Route route : routes) { 199 CompositeType ct = CamelOpenMBeanTypes.supervisingRouteControllerRouteStatusCompositeType(); 200 201 String routeId = route.getRouteId(); 202 String status = controller.getRouteStatus(routeId).name(); 203 BackOffTimer.Task state = controller.getRestartingRouteState(routeId); 204 String supervising = state != null ? state.getStatus().name() : ""; 205 long attempts = state != null ? state.getCurrentAttempts() : 0; 206 String elapsed = ""; 207 String last = ""; 208 // we can only track elapsed/time for active supervised routes 209 long time = state != null && BackOffTimer.Task.Status.Active == state.getStatus() 210 ? state.getFirstAttemptTime() : 0; 211 if (time > 0) { 212 long delta = System.currentTimeMillis() - time; 213 elapsed = TimeUtils.printDuration(delta, true); 214 } 215 time = state != null && BackOffTimer.Task.Status.Active == state.getStatus() ? state.getLastAttemptTime() : 0; 216 if (time > 0) { 217 long delta = System.currentTimeMillis() - time; 218 last = TimeUtils.printDuration(delta, true); 219 } 220 String error = ""; 221 String stacktrace = ""; 222 Throwable cause = controller.getRestartException(routeId); 223 if (cause != null) { 224 error = cause.getMessage(); 225 if (includeStacktrace) { 226 stacktrace = ExceptionHelper.stackTraceToString(cause); 227 } 228 } 229 230 CompositeData data = new CompositeDataSupport( 231 ct, 232 new String[] { 233 "index", "routeId", "status", "supervising", "attempts", "elapsed", "last", "error", 234 "stacktrace" }, 235 new Object[] { index, routeId, status, supervising, attempts, elapsed, last, error, stacktrace }); 236 answer.put(data); 237 238 // use a counter as the single index in the TabularData as we do not want a multi-value index 239 index++; 240 } 241 return answer; 242 } catch (Exception e) { 243 throw RuntimeCamelException.wrapRuntimeCamelException(e); 244 } 245 } 246 247 @Override 248 public void startRoutes() { 249 controller.startRoutes(); 250 } 251}