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.IOException;
020import java.util.Collections;
021import java.util.List;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.Optional;
025import java.util.Set;
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.CatalogCamelContext;
035import org.apache.camel.Component;
036import org.apache.camel.RuntimeCamelException;
037import org.apache.camel.ServiceStatus;
038import org.apache.camel.StatefulService;
039import org.apache.camel.api.management.ManagedInstance;
040import org.apache.camel.api.management.ManagedResource;
041import org.apache.camel.api.management.mbean.CamelOpenMBeanTypes;
042import org.apache.camel.api.management.mbean.ComponentVerifierExtension;
043import org.apache.camel.api.management.mbean.ComponentVerifierExtension.Result;
044import org.apache.camel.api.management.mbean.ComponentVerifierExtension.Result.Status;
045import org.apache.camel.api.management.mbean.ComponentVerifierExtension.Scope;
046import org.apache.camel.api.management.mbean.ComponentVerifierExtension.VerificationError;
047import org.apache.camel.api.management.mbean.ComponentVerifierExtension.VerificationError.ExceptionAttribute;
048import org.apache.camel.api.management.mbean.ComponentVerifierExtension.VerificationError.GroupAttribute;
049import org.apache.camel.api.management.mbean.ComponentVerifierExtension.VerificationError.HttpAttribute;
050import org.apache.camel.api.management.mbean.ComponentVerifierExtension.VerificationError.StandardCode;
051import org.apache.camel.api.management.mbean.ManagedComponentMBean;
052import org.apache.camel.spi.ManagementStrategy;
053import org.apache.camel.support.JSonSchemaHelper;
054import org.apache.camel.util.CastUtils;
055
056@ManagedResource(description = "Managed Component")
057public class ManagedComponent implements ManagedInstance, ManagedComponentMBean {
058    private final Component component;
059    private final String name;
060
061    public ManagedComponent(String name, Component component) {
062        this.name = name;
063        this.component = component;
064    }
065
066    public void init(ManagementStrategy strategy) {
067        // do nothing
068    }
069
070    public Component getComponent() {
071        return component;
072    }
073
074    public String getComponentName() {
075        return name;
076    }
077
078    public String getState() {
079        // must use String type to be sure remote JMX can read the attribute without requiring Camel classes.
080        if (component instanceof StatefulService) {
081            ServiceStatus status = ((StatefulService) component).getStatus();
082            return status.name();
083        }
084
085        // assume started if not a ServiceSupport instance
086        return ServiceStatus.Started.name();
087    }
088
089    public String getCamelId() {
090        return component.getCamelContext().getName();
091    }
092
093    public String getCamelManagementName() {
094        return component.getCamelContext().getManagementName();
095    }
096
097    public Object getInstance() {
098        return component;
099    }
100
101    public String informationJson() {
102        try {
103            // a component may have been given a different name, so resolve its default name by its java type
104            // as we can find the component json information from the default component name
105            String defaultName = component.getCamelContext().adapt(CatalogCamelContext.class).resolveComponentDefaultName(component.getClass().getName());
106            String target = defaultName != null ? defaultName : name;
107            return component.getCamelContext().adapt(CatalogCamelContext.class).getComponentParameterJsonSchema(target);
108        } catch (IOException e) {
109            throw RuntimeCamelException.wrapRuntimeCamelException(e);
110        }
111    }
112
113    public TabularData explain(boolean allOptions) {
114        try {
115            // a component may have been given a different name, so resolve its default name by its java type
116            // as we can find the component json information from the default component name
117            String defaultName = component.getCamelContext().adapt(CatalogCamelContext.class).resolveComponentDefaultName(component.getClass().getName());
118            String target = defaultName != null ? defaultName : name;
119            String json = component.getCamelContext().adapt(CatalogCamelContext.class).explainComponentJson(target, allOptions);
120
121            List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("componentProperties", json, true);
122
123            TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.explainComponentTabularType());
124
125            for (Map<String, String> row : rows) {
126                String name = row.get("name");
127                String kind = row.get("kind");
128                String group = row.get("group") != null ? row.get("group") : "";
129                String label = row.get("label") != null ? row.get("label") : "";
130                String type = row.get("type");
131                String javaType = row.get("javaType");
132                String deprecated = row.get("deprecated") != null ? row.get("deprecated") : "";
133                String secret = row.get("secret") != null ? row.get("secret") : "";
134                String value = row.get("value") != null ? row.get("value") : "";
135                String defaultValue = row.get("defaultValue") != null ? row.get("defaultValue") : "";
136                String description = row.get("description") != null ? row.get("description") : "";
137
138                CompositeType ct = CamelOpenMBeanTypes.explainComponentCompositeType();
139                CompositeData data = new CompositeDataSupport(ct,
140                        new String[]{"option", "kind", "group", "label", "type", "java type", "deprecated", "secret", "value", "default value", "description"},
141                        new Object[]{name, kind, group, label, type, javaType, deprecated, secret, value, defaultValue, description});
142                answer.put(data);
143            }
144
145            return answer;
146        } catch (Exception e) {
147            throw RuntimeCamelException.wrapRuntimeCamelException(e);
148        }
149    }
150
151    @Override
152    public boolean isVerifySupported() {
153        return component.getExtension(org.apache.camel.component.extension.ComponentVerifierExtension.class).isPresent();
154    }
155
156    @Override
157    public ComponentVerifierExtension.Result verify(String scope, Map<String, String> options) {
158        try {
159            org.apache.camel.component.extension.ComponentVerifierExtension.Scope scopeEnum = org.apache.camel.component.extension.ComponentVerifierExtension.Scope.fromString(scope);
160            Optional<org.apache.camel.component.extension.ComponentVerifierExtension> verifier = component.getExtension(org.apache.camel.component.extension.ComponentVerifierExtension.class);
161            if (verifier.isPresent()) {
162                org.apache.camel.component.extension.ComponentVerifierExtension.Result result = verifier.get().verify(scopeEnum, CastUtils.cast(options));
163                String rstatus = result.getStatus().toString();
164                String rscope = result.getScope().toString();
165                return new ResultImpl(Scope.valueOf(rscope), Status.valueOf(rstatus),
166                        result.getErrors().stream().map(this::translate).collect(Collectors.toList()));
167
168            } else {
169                return new ResultImpl(Scope.PARAMETERS, Status.UNSUPPORTED, Collections.emptyList());
170            }
171        } catch (IllegalArgumentException e) {
172            return new ResultImpl(Scope.PARAMETERS, Status.UNSUPPORTED, Collections.singletonList(
173                    new VerificationErrorImpl(StandardCode.UNSUPPORTED_SCOPE, "Unsupported scope: " + scope)));
174        }
175    }
176
177    private VerificationError translate(org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError error) {
178        return new VerificationErrorImpl(translate(error.getCode()), error.getDescription(),
179                error.getParameterKeys(), translate(error.getDetails()));
180    }
181
182    private Map<VerificationError.Attribute, Object> translate(Map<org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.Attribute, Object> details) {
183        return details.entrySet().stream().collect(Collectors.toMap(e -> translate(e.getKey()), Entry::getValue));
184    }
185
186    private VerificationError.Attribute translate(org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.Attribute attribute) {
187        if (attribute == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.GroupAttribute.GROUP_NAME) {
188            return GroupAttribute.GROUP_NAME;
189        } else if (attribute == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.GroupAttribute.GROUP_OPTIONS) {
190            return GroupAttribute.GROUP_OPTIONS;
191        } else if (attribute == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.HttpAttribute.HTTP_CODE) {
192            return HttpAttribute.HTTP_CODE;
193        } else if (attribute == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.HttpAttribute.HTTP_REDIRECT) {
194            return HttpAttribute.HTTP_REDIRECT;
195        } else if (attribute == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.HttpAttribute.HTTP_TEXT) {
196            return HttpAttribute.HTTP_TEXT;
197        } else if (attribute == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.ExceptionAttribute.EXCEPTION_CLASS) {
198            return ExceptionAttribute.EXCEPTION_CLASS;
199        } else if (attribute == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.ExceptionAttribute.EXCEPTION_INSTANCE) {
200            return ExceptionAttribute.EXCEPTION_INSTANCE;
201        }  else if (attribute != null) {
202            return VerificationError.asAttribute(attribute.getName());
203        } else {
204            return null;
205        }
206    }
207
208    private VerificationError.Code translate(org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.Code code) {
209        if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.AUTHENTICATION) {
210            return StandardCode.AUTHENTICATION;
211        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.EXCEPTION) {
212            return StandardCode.EXCEPTION;
213        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.INTERNAL) {
214            return StandardCode.INTERNAL;
215        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.MISSING_PARAMETER) {
216            return StandardCode.MISSING_PARAMETER;
217        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.UNKNOWN_PARAMETER) {
218            return StandardCode.UNKNOWN_PARAMETER;
219        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.ILLEGAL_PARAMETER) {
220            return StandardCode.ILLEGAL_PARAMETER;
221        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION) {
222            return StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION;
223        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.ILLEGAL_PARAMETER_VALUE) {
224            return StandardCode.ILLEGAL_PARAMETER_VALUE;
225        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.INCOMPLETE_PARAMETER_GROUP) {
226            return StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION;
227        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.UNSUPPORTED) {
228            return StandardCode.UNSUPPORTED;
229        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.UNSUPPORTED_SCOPE) {
230            return StandardCode.UNSUPPORTED_SCOPE;
231        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.UNSUPPORTED_COMPONENT) {
232            return StandardCode.UNSUPPORTED_COMPONENT;
233        } else if (code == org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError.StandardCode.GENERIC) {
234            return StandardCode.GENERIC;
235        } else if (code != null) {
236            return VerificationError.asCode(code.getName());
237        } else {
238            return null;
239        }
240    }
241
242    public static class VerificationErrorImpl implements VerificationError {
243        private final Code code;
244        private final String description;
245        private final Set<String> parameterKeys;
246        private final Map<Attribute, Object> details;
247
248        public VerificationErrorImpl(Code code, String description) {
249            this.code = code;
250            this.description = description;
251            this.parameterKeys = Collections.emptySet();
252            this.details = Collections.emptyMap();
253        }
254
255        public VerificationErrorImpl(Code code, String description, Set<String> parameterKeys, Map<Attribute, Object> details) {
256            this.code = code;
257            this.description = description;
258            this.parameterKeys = parameterKeys;
259            this.details = details;
260        }
261
262        @Override
263        public Code getCode() {
264            return code;
265        }
266
267        @Override
268        public String getDescription() {
269            return description;
270        }
271
272        @Override
273        public Set<String> getParameterKeys() {
274            return parameterKeys;
275        }
276
277        @Override
278        public Map<Attribute, Object> getDetails() {
279            return details;
280        }
281    }
282
283    public static class ResultImpl implements Result {
284        private final Scope scope;
285        private final Status status;
286        private final List<VerificationError> errors;
287
288        public ResultImpl(Scope scope, Status status, List<VerificationError> errors) {
289            this.scope = scope;
290            this.status = status;
291            this.errors = errors;
292        }
293
294        @Override
295        public Scope getScope() {
296            return scope;
297        }
298
299        @Override
300        public Status getStatus() {
301            return status;
302        }
303
304        @Override
305        public List<VerificationError> getErrors() {
306            return errors;
307        }
308    }
309}