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}