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}