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 int getNumberOfControlledRoutes() {
109        return controller.getControlledRoutes().size();
110    }
111
112    @Override
113    public int getNumberOfRestartingRoutes() {
114        return controller.getRestartingRoutes().size();
115    }
116
117    @Override
118    public int getNumberOfExhaustedRoutes() {
119        return controller.getExhaustedRoutes().size();
120    }
121
122    @Override
123    public Collection<String> getControlledRoutes() {
124        if (controller != null) {
125            return controller.getControlledRoutes().stream()
126                    .map(Route::getId)
127                    .toList();
128        }
129
130        return Collections.emptyList();
131    }
132
133    @Override
134    public String getRouteStartupLoggingLevel() {
135        if (controller != null) {
136            return controller.getLoggingLevel().name();
137        } else {
138            return null;
139        }
140    }
141
142    @Override
143    public Collection<String> getRestartingRoutes() {
144        if (controller != null) {
145            return controller.getRestartingRoutes().stream()
146                    .map(Route::getId)
147                    .toList();
148        }
149
150        return Collections.emptyList();
151    }
152
153    @Override
154    public Collection<String> getExhaustedRoutes() {
155        if (controller != null) {
156            return controller.getExhaustedRoutes().stream()
157                    .map(Route::getId)
158                    .toList();
159        }
160
161        return Collections.emptyList();
162    }
163
164    @Override
165    public TabularData routeStatus(boolean exhausted, boolean restarting, boolean includeStacktrace) {
166        try {
167            TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.supervisingRouteControllerRouteStatusTabularType());
168
169            int index = 0;
170            Set<Route> routes = new TreeSet<>(Comparator.comparing(Route::getId));
171            routes.addAll(controller.getControlledRoutes());
172            if (exhausted) {
173                routes.addAll(controller.getExhaustedRoutes());
174            }
175            if (restarting) {
176                routes.addAll(controller.getRestartingRoutes());
177            }
178
179            for (Route route : routes) {
180                CompositeType ct = CamelOpenMBeanTypes.supervisingRouteControllerRouteStatusCompositeType();
181
182                String routeId = route.getRouteId();
183                String status = controller.getRouteStatus(routeId).name();
184                BackOffTimer.Task state = controller.getRestartingRouteState(routeId);
185                String supervising = state != null ? state.getStatus().name() : "";
186                long attempts = state != null ? state.getCurrentAttempts() : 0;
187                String elapsed = "";
188                String last = "";
189                // we can only track elapsed/time for active supervised routes
190                long time = state != null && BackOffTimer.Task.Status.Active == state.getStatus()
191                        ? state.getFirstAttemptTime() : 0;
192                if (time > 0) {
193                    long delta = System.currentTimeMillis() - time;
194                    elapsed = TimeUtils.printDuration(delta, true);
195                }
196                time = state != null && BackOffTimer.Task.Status.Active == state.getStatus() ? state.getLastAttemptTime() : 0;
197                if (time > 0) {
198                    long delta = System.currentTimeMillis() - time;
199                    last = TimeUtils.printDuration(delta, true);
200                }
201                String error = "";
202                String stacktrace = "";
203                Throwable cause = controller.getRestartException(routeId);
204                if (cause != null) {
205                    error = cause.getMessage();
206                    if (includeStacktrace) {
207                        StringWriter writer = new StringWriter();
208                        cause.printStackTrace(new PrintWriter(writer));
209                        writer.flush();
210                        stacktrace = writer.toString();
211                    }
212                }
213
214                CompositeData data = new CompositeDataSupport(
215                        ct,
216                        new String[] {
217                                "index", "routeId", "status", "supervising", "attempts", "elapsed", "last", "error",
218                                "stacktrace" },
219                        new Object[] { index, routeId, status, supervising, attempts, elapsed, last, error, stacktrace });
220                answer.put(data);
221
222                // use a counter as the single index in the TabularData as we do not want a multi-value index
223                index++;
224            }
225            return answer;
226        } catch (Exception e) {
227            throw RuntimeCamelException.wrapRuntimeCamelException(e);
228        }
229    }
230}