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