001/**
002 * Copyright (C) 2006-2020 Talend Inc. - www.talend.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.talend.sdk.component.server.configuration;
017
018import java.util.List;
019import java.util.Optional;
020
021import javax.annotation.PostConstruct;
022import javax.enterprise.context.ApplicationScoped;
023import javax.enterprise.inject.spi.CDI;
024import javax.inject.Inject;
025
026import org.eclipse.microprofile.config.inject.ConfigProperty;
027import org.talend.sdk.component.api.meta.Documentation;
028
029import lombok.Getter;
030import lombok.extern.slf4j.Slf4j;
031
032@Slf4j
033@Getter
034@ApplicationScoped
035public class ComponentServerConfiguration {
036
037    @Inject
038    @Documentation("If set it will replace any message for exceptions. Set to `false` to use the actual exception message.")
039    @ConfigProperty(name = "talend.component.server.jaxrs.exceptionhandler.defaultMessage", defaultValue = "false")
040    private String defaultExceptionMessage;
041
042    @Inject
043    @Documentation("The local maven repository used to locate components and their dependencies")
044    @ConfigProperty(name = "talend.component.server.maven.repository")
045    private Optional<String> mavenRepository;
046
047    // property to list plugins directly by gav. This is nice to set it on the cli but not as a maintenance solution.
048    @Inject
049    @Documentation("A comma separated list of gav to locate the components")
050    @ConfigProperty(name = "talend.component.server.component.coordinates")
051    private Optional<String> componentCoordinates;
052
053    // property to list plugins like in a fatjar, ie value = gav. Nice for assemblies, less for demo/cli usage.
054    @Inject
055    @Documentation("A property file (or multiple comma separated) where the value is a gav of a component to register"
056            + "(complementary with `coordinates`). Note that the path can end up with `*` or `*.properties` "
057            + "to take into account all properties in a folder.")
058    @ConfigProperty(name = "talend.component.server.component.registry")
059    private Optional<List<String>> componentRegistry;
060
061    @Inject
062    @Documentation("Should the /documentation endpoint be activated. "
063            + "Note that when called on localhost the doc is always available.")
064    @ConfigProperty(name = "talend.component.server.documentation.active", defaultValue = "true")
065    private Boolean supportsDocumentation;
066
067    // sync with org.talend.sdk.component.server.service.security.SecurityExtension.addSecurityHandlers
068    @Inject
069    @Documentation("How to validate a connection. Accepted values: securityNoopHandler.")
070    @ConfigProperty(name = "talend.component.server.security.connection.handler", defaultValue = "securityNoopHandler")
071    private String securityConnectionHandler;
072
073    // sync with org.talend.sdk.component.server.service.security.SecurityExtension.addSecurityHandlers
074    @Inject
075    @Documentation("How to validate a command/request. Accepted values: securityNoopHandler.")
076    @ConfigProperty(name = "talend.component.server.security.command.handler", defaultValue = "securityNoopHandler")
077    private String securityCommandHandler;
078
079    @Inject
080    @Documentation("Should the component extensions add required dependencies.")
081    @ConfigProperty(name = "talend.component.server.component.extend.dependencies", defaultValue = "true")
082    private Boolean addExtensionDependencies;
083
084    @Inject
085    @Documentation("A component translation repository. This is where you put your documentation translations. "
086            + "Their name must follow the pattern `documentation_${container-id}_language.adoc` where `${container-id}` "
087            + "is the component jar name (without the extension and version, generally the artifactId).")
088    @ConfigProperty(name = "talend.component.server.component.documentation.translations",
089            defaultValue = "${home}/documentations")
090    private String documentationI18nTranslations;
091
092    @Inject
093    @Documentation("Should the /api/v1/environment endpoint be activated. "
094            + "It shows some internal versions and git commit which are not always desirable over the wire.")
095    @ConfigProperty(name = "talend.component.server.environment.active", defaultValue = "true")
096    private Boolean supportsEnvironment;
097
098    @Inject
099    @Documentation("Accepted tokens in Authorization header for remote calls to secured endpoints "
100            + "(/api/v1/environment or /documentation).")
101    @ConfigProperty(name = "talend.component.server.filter.secured.tokens", defaultValue = "-")
102    private String securedEndpointsTokens;
103
104    @Inject
105    @Documentation("A folder available for the server - don't forget to mount it in docker if you are using the "
106            + "image - which accepts subfolders named as component plugin id "
107            + "(generally the artifactId or jar name without the version, ex: jdbc). Each family folder can contain:\n\n"
108            + "- a `user-configuration.properties` file which will be merged with component configuration system "
109            + "(see services). This properties file enables the function `userJar(xxxx)` to replace the jar named `xxxx` "
110            + "by its virtual gav (`groupId:artifactId:version`),\n"
111            + "- a list of jars which will be merged with component family classpath\n")
112    @ConfigProperty(name = "talend.component.server.user.extensions.location")
113    private Optional<String> userExtensions;
114
115    @Inject
116    @Documentation("Should the implicit artifacts be provisionned to a m2. If set to `auto` it tries to detect "
117            + "if there is a m2 to provision - recommended, if set to `skip` it is ignored, else it uses the value as a "
118            + "m2 path.")
119    @ConfigProperty(name = "talend.component.server.user.extensions.provisioning.location", defaultValue = "auto")
120    private String userExtensionsAutoM2Provisioning;
121
122    @Inject
123    @Documentation("Timeout for extension initialization at startup, since it ensures the startup wait extensions "
124            + "are ready and loaded it allows to control the latency it implies.")
125    @ConfigProperty(name = "talend.component.server.component.extension.startup.timeout", defaultValue = "180000")
126    private Long extensionsStartupTimeout;
127
128    @Inject
129    @Documentation("If you deploy some extension, where they can create their dependencies if needed.")
130    @ConfigProperty(name = "talend.component.server.component.extension.maven.repository")
131    private Optional<String> extensionMavenRepository;
132
133    @Inject
134    @Documentation("Should the components using a `@GridLayout` support tab translation. "
135            + "Studio does not suppot that feature yet so this is not enabled by default.")
136    @ConfigProperty(name = "talend.component.server.gridlayout.translation.support", defaultValue = "false")
137    private Boolean translateGridLayoutTabNames;
138
139    @Inject
140    @Documentation("Should the all requests/responses be logged (debug purposes - only work when running with CXF).")
141    @ConfigProperty(name = "talend.component.server.request.log", defaultValue = "false")
142    private Boolean logRequests;
143
144    @Inject
145    @Documentation("Maximum items a cache can store, used for index endpoints.")
146    @ConfigProperty(name = "talend.component.server.cache.maxSize", defaultValue = "1000")
147    private Integer maxCacheSize;
148
149    @Inject
150    @Documentation("Should the lastUpdated timestamp value of `/environment` "
151            + "endpoint be updated with server start time.")
152    @ConfigProperty(name = "talend.component.server.lastUpdated.useStartTime", defaultValue = "false")
153    private Boolean changeLastUpdatedAtStartup;
154
155    @Inject
156    @Documentation("These patterns are used to find the icons in the classpath(s).")
157    @ConfigProperty(name = "talend.component.server.icon.paths",
158            defaultValue = "icons/%s.svg,icons/svg/%s.svg,icons/%s_icon32.png,icons/png/%s_icon32.png")
159    private List<String> iconExtensions;
160
161    @Inject
162    @Documentation("For caching reasons the goal is to reduce the locales to the minimum required numbers. "
163            + "For instance we avoid `fr` and `fr_FR` which would lead to the same entries but x2 in terms of memory. "
164            + "This mapping enables that by whitelisting allowed locales, default being `en`. "
165            + "If the key ends with `*` it means all string starting with the prefix will match. "
166            + "For instance `fr*` will match `fr_FR` but also `fr_CA`.")
167    @ConfigProperty(name = "talend.component.server.locale.mapping", defaultValue = "en*=en\nfr*=fr\nzh*=zh_CN\nja*=ja")
168    private String localeMapping;
169
170    @PostConstruct
171    private void init() {
172        if (logRequests != null && logRequests) {
173            final ClassLoader loader = Thread.currentThread().getContextClassLoader();
174            try {
175                doActivateDebugMode(loader, loader.loadClass("org.apache.cxf.ext.logging.LoggingFeature"));
176            } catch (final Exception | NoClassDefFoundError e) {
177                try {
178                    doActivateDebugMode(loader, loader.loadClass("org.apache.cxf.feature.LoggingFeature"));
179                } catch (final Exception | NoClassDefFoundError ex) {
180                    log.warn("Can't honor log request configuration, skipping ({})", e.getMessage());
181                }
182            }
183        }
184    }
185
186    private void doActivateDebugMode(final ClassLoader loader, final Class<?> feature)
187            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
188            java.lang.reflect.InvocationTargetException, NoSuchMethodException {
189        final Class<?> bus = loader.loadClass("org.apache.cxf.Bus");
190        final Object instance = feature.getConstructor().newInstance();
191        final Object busInstance = CDI.current().select(bus).get();
192        feature.getMethod("initialize", bus).invoke(instance, busInstance);
193        log.info("Activated debug mode - will log requests/responses");
194    }
195}