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