diff --git a/CHANGELOG.md b/CHANGELOG.md index e447c50049..01b2aca20d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com) ### Changed +- #3401 - Move SyslogAppender into separate bundle for onprem only. SyslogAppender does not work in Cloud Service. - #3390 - Remove usage of commons collections 3 ### Fixed diff --git a/all/pom.xml b/all/pom.xml index 02e4bcee20..acbf884398 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -64,6 +64,13 @@ true true + + ${project.groupId} + acs-aem-commons-bundle-onprem + /apps/acs-commons/install + true + true + ${project.groupId} acs-aem-commons-ui.apps diff --git a/bundle-onprem/.gitignore b/bundle-onprem/.gitignore new file mode 100644 index 0000000000..421eeaa8c5 --- /dev/null +++ b/bundle-onprem/.gitignore @@ -0,0 +1,4 @@ +/nb-configuration.xml +*.factorypath +/dependency-reduced-pom.xml +/nbactions-autoInstallBundle.xml diff --git a/bundle-onprem/pom.xml b/bundle-onprem/pom.xml new file mode 100644 index 0000000000..f64bbcab2a --- /dev/null +++ b/bundle-onprem/pom.xml @@ -0,0 +1,165 @@ + + + + + 4.0.0 + + + + + com.adobe.acs + acs-aem-commons + 6.6.3-SNAPSHOT + + + + + + + acs-aem-commons-bundle-onprem + ACS AEM Commons Bundle - On Prem + Core ACS AEM Commons OSGi Bundle for AEM on prem only + + + + biz.aQute.bnd + bnd-maven-plugin + + + biz.aQute.bnd + bnd-baseline-maven-plugin + + true + + + + org.apache.sling + sling-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + + + + org.osgi + org.osgi.annotation.versioning + provided + + + org.osgi + org.osgi.annotation.bundle + provided + + + org.osgi + org.osgi.service.component.annotations + provided + + + org.osgi + org.osgi.service.metatype.annotations + provided + + + org.apache.sling + org.apache.sling.servlets.annotations + provided + + + org.jetbrains + annotations + provided + + + + + + org.osgi + org.osgi.framework + provided + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + + + + com.adobe.aem + uber-jar + provided + + + + + + release + + + performRelease + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + javadoc-jar + package + + jar + + + ${project.build.directory}/invalid + javadoc + + + + + + + + + diff --git a/bundle-onprem/src/main/java/com/adobe/acs/commons/logging/impl/SyslogAppender.java b/bundle-onprem/src/main/java/com/adobe/acs/commons/logging/impl/SyslogAppender.java new file mode 100644 index 0000000000..e11351ce4b --- /dev/null +++ b/bundle-onprem/src/main/java/com/adobe/acs/commons/logging/impl/SyslogAppender.java @@ -0,0 +1,136 @@ +/* + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2013 - 2023 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.adobe.acs.commons.logging.impl; + +import java.util.Dictionary; +import java.util.Hashtable; + +import ch.qos.logback.core.net.SyslogAppenderBase; +import org.apache.commons.lang3.StringUtils; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import ch.qos.logback.core.Appender; + +@Component(configurationPolicy = ConfigurationPolicy.REQUIRE, + property = { + "webconsole.configurationFactory.nameHint=Host: {host}, for loggers [{loggers}]" + }) +@Designate(ocd = SyslogAppender.Config.class, factory = true) +public final class SyslogAppender { + + @ObjectClassDefinition(name = "ACS AEM Commons - Syslog Appender", + description = "Logback appender to send messages using Syslog") + public @interface Config { + + @AttributeDefinition(name = "Host", description = "Host of Syslog server") + String host(); + + @AttributeDefinition(name = "Logger Names", description = "List of logger categories (ROOT for all)") + String[] loggers() default {"ROOT"}; + + @AttributeDefinition(name = "Port", description = "Port of Syslog server") + int port() default -1; + + @AttributeDefinition(name = "Suffix Pattern", description = "Logback Pattern defining the message format.") + String suffix_pattern() default "[%thread] %-5level %logger{36} - %msg%n"; + + @AttributeDefinition(name = "Syslog Facility", description = "The Syslog Facility is meant to identify the source of a message, " + + "separately from any context included in the Suffix Pattern. The facility option must be set to one of the strings " + + "KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT, ALERT, CLOCK, LOCAL0, LOCAL1, " + + "LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. Case is not important.") + String facility() default "USER"; + + @AttributeDefinition(name = "Stack Trace Pattern", description = "Logback Pattern for customizing the string appearing just before each stack " + + "trace line. The default value for this property is a single tab character.") + String stack_trace_pattern() default ""; + + @AttributeDefinition(name = "Exclude Throwables", description = "Set to true to cause stack trace data associated with a Throwable to be omitted. " + + "By default, this is set to false so that stack trace data is sent to the syslog server.") + boolean throwable_excluded() default false; + } + + private ch.qos.logback.classic.net.SyslogAppender appender; + + private ServiceRegistration appenderRegistration; + + @Activate + @SuppressWarnings("squid:S1149") + protected void activate(final BundleContext ctx, final Config config) { + this.appender = constructAppender(config); + + final Dictionary props = new Hashtable<>(); + props.put("loggers", config.loggers()); + appenderRegistration = ctx.registerService(Appender.class, appender, props); + } + + static ch.qos.logback.classic.net.SyslogAppender constructAppender(final Config config) { + if (StringUtils.isEmpty(config.host()) || config.port() == -1) { + throw new IllegalArgumentException( + "Syslog Appender not configured correctly. Both host and port need to be provided."); + } + + // throws a descriptive IllegalArgumentException if facility is not valid. + SyslogAppenderBase.facilityStringToint(config.facility()); + + final ch.qos.logback.classic.net.SyslogAppender appender = new ch.qos.logback.classic.net.SyslogAppender(); + + appender.setSyslogHost(config.host()); + appender.setPort(config.port()); + + appender.setFacility(config.facility()); + appender.setSuffixPattern(config.suffix_pattern()); + + if (StringUtils.isNotEmpty(config.stack_trace_pattern())) { + appender.setStackTracePattern(config.stack_trace_pattern()); + } + + appender.setThrowableExcluded(config.throwable_excluded()); + return appender; + } + + + @Deactivate + protected void deactivate() { + if (appender != null) { + if (appender.isStarted()) { + appender.stop(); + } + appender = null; + } + + if (appenderRegistration != null) { + try { + appenderRegistration.unregister(); + } catch (IllegalStateException e) { + // ignore + } + appenderRegistration = null; + } + } + +} diff --git a/bundle-onprem/src/main/resources/META-INF/COPYRIGHT b/bundle-onprem/src/main/resources/META-INF/COPYRIGHT new file mode 100644 index 0000000000..817fe2246d --- /dev/null +++ b/bundle-onprem/src/main/resources/META-INF/COPYRIGHT @@ -0,0 +1,15 @@ +Copyright 2013 Adobe + +Adobe holds the copyright for all the files found in this repository. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/bundle-onprem/src/main/resources/META-INF/LICENSE b/bundle-onprem/src/main/resources/META-INF/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/bundle-onprem/src/main/resources/META-INF/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/bundle-onprem/src/test/java/com/adobe/acs/commons/logging/impl/SyslogAppenderTest.java b/bundle-onprem/src/test/java/com/adobe/acs/commons/logging/impl/SyslogAppenderTest.java new file mode 100644 index 0000000000..dd8764732b --- /dev/null +++ b/bundle-onprem/src/test/java/com/adobe/acs/commons/logging/impl/SyslogAppenderTest.java @@ -0,0 +1,123 @@ +/* +* #%L +* ACS AEM Commons Bundle +* %% +* Copyright (C) 2013 - 2023 Adobe +* %% +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* #L% +*/ +package com.adobe.acs.commons.logging.impl; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Dictionary; + +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import ch.qos.logback.core.Appender; + +public class SyslogAppenderTest { + + public static SyslogAppender.Config createConfig(final String host, + final int port, + final String suffixPattern, + final String facility, + final String stackTracePattern, + final boolean throwableExcluded, final String... loggers) { + final SyslogAppender.Config config = Mockito.mock(SyslogAppender.Config.class); + Mockito.when(config.host()).thenReturn(host); + Mockito.when(config.port()).thenReturn(port); + Mockito.when(config.suffix_pattern()).thenReturn(suffixPattern); + Mockito.when(config.facility()).thenReturn(facility); + Mockito.when(config.stack_trace_pattern()).thenReturn(stackTracePattern); + Mockito.when(config.throwable_excluded()).thenReturn(throwableExcluded); + Mockito.when(config.loggers()).thenReturn(loggers); + return config; + } + + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testActivate() { + BundleContext ctx = Mockito.mock(BundleContext.class); + ServiceRegistration reg = Mockito.mock(ServiceRegistration.class); + Mockito.when(ctx.registerService(Mockito.eq(Appender.class), Mockito.any(ch.qos.logback.classic.net.SyslogAppender.class), Mockito.any())) + .thenReturn(reg); + + SyslogAppender appender = new SyslogAppender(); + appender.activate(ctx, createConfig("localhost", 42, "test", + "USER", null, false, "my.logger")); + + ArgumentCaptor> propCaptor = ArgumentCaptor.forClass(Dictionary.class); + Mockito.verify(ctx, Mockito.times(1)) + .registerService( + Mockito.eq(Appender.class), + Mockito.any(ch.qos.logback.classic.net.SyslogAppender.class), + propCaptor.capture()); + Mockito.verifyNoMoreInteractions(ctx); + assertArrayEquals(new String[] {"my.logger"}, (String[])propCaptor.getValue().get("loggers")); + + appender.deactivate(); + Mockito.verify(reg, Mockito.times(1)).unregister(); + Mockito.verifyNoMoreInteractions(ctx); + } + + @Test + public void testConstructSyslogAppender() { + boolean nullHostThrown = false; + try { + ch.qos.logback.classic.net.SyslogAppender logAppender = + SyslogAppender.constructAppender(createConfig(null, 465, "test", + "USER", null, false, "my.logger")); + } catch (final IllegalArgumentException e) { + nullHostThrown = true; + } + + assertTrue(nullHostThrown, "a null host should throw an IllegalArgumentException"); + boolean emptyHostThrown = false; + try { + ch.qos.logback.classic.net.SyslogAppender logAppender = + SyslogAppender.constructAppender(createConfig("", 465, "test", + "USER", null, false, "my.logger")); + } catch (final IllegalArgumentException e) { + emptyHostThrown = true; + } + + assertTrue(emptyHostThrown, "an empty host should throw an IllegalArgumentException"); + + boolean negativePortThrown = false; + try { + ch.qos.logback.classic.net.SyslogAppender logAppender = + SyslogAppender.constructAppender(createConfig("localhost", -1, "test", + "USER", null, false, "my.logger")); + } catch (final IllegalArgumentException e) { + negativePortThrown = true; + } + + assertTrue(negativePortThrown, "a negative port should throw an IllegalArgumentException"); + + ch.qos.logback.classic.net.SyslogAppender logAppender = + SyslogAppender.constructAppender(createConfig("localhost", 465, "test", + "USER", "fff", false, "my.logger")); + assertEquals("fff", logAppender.getStackTracePattern(), "stack trace pattern should match"); + + + } +} diff --git a/bundle/pom.xml b/bundle/pom.xml index 5384596e40..675923dc8c 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -408,7 +408,6 @@ javax.mail-api provided - ch.qos.logback logback-classic diff --git a/bundle/src/main/java/com/adobe/acs/commons/logging/impl/SyslogAppender.java b/bundle/src/main/java/com/adobe/acs/commons/logging/impl/SyslogAppender.java deleted file mode 100644 index 0d38419850..0000000000 --- a/bundle/src/main/java/com/adobe/acs/commons/logging/impl/SyslogAppender.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * ACS AEM Commons - * - * Copyright (C) 2013 - 2023 Adobe - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.adobe.acs.commons.logging.impl; - -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.Map; - -import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.ConfigurationPolicy; -import org.apache.felix.scr.annotations.Deactivate; -import org.apache.felix.scr.annotations.Properties; -import org.apache.felix.scr.annotations.Property; -import org.apache.felix.scr.annotations.PropertyUnbounded; -import ch.qos.logback.core.net.SyslogAppenderBase; -import org.apache.commons.lang.StringUtils; -import org.apache.sling.commons.osgi.PropertiesUtil; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.component.ComponentContext; - -import ch.qos.logback.core.Appender; - -@Component(metatype = true, configurationFactory = true, policy = ConfigurationPolicy.REQUIRE, - label = "ACS AEM Commons - Syslog Appender", - description = "Logback appender to send messages using Syslog") -@Properties({ - @Property( - name = "webconsole.configurationFactory.nameHint", - value = "Host: {host}, for loggers [{loggers}]") -}) -public final class SyslogAppender { - - private static final String ROOT = "ROOT"; - - private static final int DEFAULT_PORT = -1; - - private static final String DEFAULT_SUFFIX_PATTERN = "[%thread] %-5level %logger{36} - %msg%n"; - - private static final String DEFAULT_FACILITY = "USER"; - - private static final boolean DEFAULT_THROWABLE_EXCLUDED = false; - - @Property(label = "Host", description = "Host of Syslog server") - static final String PROP_HOST = "host"; - - @Property(label = "Logger Names", description = "List of logger categories (ROOT for all)", - unbounded = PropertyUnbounded.ARRAY, value = ROOT) - static final String PROP_LOGGERS = "loggers"; - - @Property(label = "Port", description = "Port of Syslog server", intValue = -1) - static final String PROP_PORT = "port"; - - @Property(label = "Suffix Pattern", description = "Logback Pattern defining the message format.", - value = DEFAULT_SUFFIX_PATTERN) - static final String PROP_SUFFIX_PATTERN = "suffix.pattern"; - - @Property(label = "Syslog Facility", value = DEFAULT_FACILITY, propertyPrivate = true, - description = "The Syslog Facility is meant to identify the source of a message, separately from any context " - + "included in the Suffix Pattern. The facility option must be set to one of the strings KERN, USER, MAIL, DAEMON, " - + "AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT, ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, " - + "LOCAL5, LOCAL6, LOCAL7. Case is not important.") - static final String PROP_FACILITY = "facility"; - - @Property(label = "Stack Trace Pattern", description = "Logback Pattern for customizing the string appearing just before each stack " - + "trace line. The default value for this property is a single tab character.") - static final String PROP_STACK_TRACE_PATTERN = "stack.trace.pattern"; - - @Property(label = "Exclude Throwables", description = "Set to true to cause stack trace data associated with a Throwable to be omitted. " - + "By default, this is set to false so that stack trace data is sent to the syslog server.", boolValue = DEFAULT_THROWABLE_EXCLUDED) - static final String PROP_THROWABLE_EXCLUDED = "throwable.excluded"; - - private ch.qos.logback.classic.net.SyslogAppender appender; - - private ServiceRegistration appenderRegistration; - - @Activate - @SuppressWarnings("squid:S1149") - protected void activate(final BundleContext ctx, final Map properties) { - this.appender = constructAppender(properties); - - final String[] loggers = PropertiesUtil.toStringArray(properties.get(PROP_LOGGERS), new String[] {ROOT}); - Dictionary props = new Hashtable<>(); - props.put(PROP_LOGGERS, loggers); - appenderRegistration = ctx.registerService(Appender.class.getName(), appender, props); - } - - static ch.qos.logback.classic.net.SyslogAppender constructAppender(final Map properties) { - final String suffixPattern = PropertiesUtil - .toString(properties.get(PROP_SUFFIX_PATTERN), DEFAULT_SUFFIX_PATTERN); - final int port = PropertiesUtil.toInteger(properties.get(PROP_PORT), DEFAULT_PORT); - final String host = PropertiesUtil.toString(properties.get(PROP_HOST), null); - final String facility = PropertiesUtil.toString(properties.get(PROP_FACILITY), DEFAULT_FACILITY); - final String stackTracePattern = PropertiesUtil.toString(properties.get(PROP_STACK_TRACE_PATTERN), null); - final boolean throwableExcluded = PropertiesUtil.toBoolean(properties.get(PROP_THROWABLE_EXCLUDED), DEFAULT_THROWABLE_EXCLUDED); - - - if (StringUtils.isEmpty(host) || port == -1) { - throw new IllegalArgumentException( - "Syslog Appender not configured correctly. Both host and port need to be provided."); - } - - // throws a descriptive IllegalArgumentException if facility is not valid. - SyslogAppenderBase.facilityStringToint(facility); - - final ch.qos.logback.classic.net.SyslogAppender appender = new ch.qos.logback.classic.net.SyslogAppender(); - - appender.setSyslogHost(host); - appender.setPort(port); - - appender.setFacility(facility); - appender.setSuffixPattern(suffixPattern); - - if (StringUtils.isNotEmpty(stackTracePattern)) { - appender.setStackTracePattern(stackTracePattern); - } - - appender.setThrowableExcluded(throwableExcluded); - return appender; - } - - - @Deactivate - protected void deactivate() { - if (appender != null) { - if (appender.isStarted()) { - appender.stop(); - } - appender = null; - } - - if (appenderRegistration != null) { - appenderRegistration.unregister(); - appenderRegistration = null; - } - } - -} diff --git a/bundle/src/test/java/com/adobe/acs/commons/logging/impl/SyslogAppenderTest.java b/bundle/src/test/java/com/adobe/acs/commons/logging/impl/SyslogAppenderTest.java deleted file mode 100644 index b9be4b2c0c..0000000000 --- a/bundle/src/test/java/com/adobe/acs/commons/logging/impl/SyslogAppenderTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * ACS AEM Commons - * - * Copyright (C) 2013 - 2023 Adobe - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.adobe.acs.commons.logging.impl; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Dictionary; -import java.util.HashMap; -import java.util.Map; - -import com.google.common.collect.ImmutableMap; -import org.apache.sling.commons.testing.osgi.MockBundle; -import org.apache.sling.commons.testing.osgi.MockBundleContext; -import org.junit.Test; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.ServiceRegistration; - -public class SyslogAppenderTest { - - class UnregMockServiceRegistration implements ServiceRegistration { - final ServiceRegistration wrapped; - boolean unregistered = false; - - public UnregMockServiceRegistration(final ServiceRegistration wrapped) { - this.wrapped = wrapped; - } - - @Override - public ServiceReference getReference() { - return wrapped.getReference(); - } - - @Override - public void setProperties(final Dictionary dictionary) { - this.wrapped.setProperties(dictionary); - } - - @Override - public void unregister() { - if (this.unregistered) { - throw new IllegalStateException("already unregistered"); - } - this.unregistered = true; - } - } - - class UnregMockBundleContext extends MockBundleContext { - public UnregMockBundleContext(final MockBundle bundle) { - super(bundle); - } - - @Override - public ServiceRegistration registerService(final String s, final Object o, final Dictionary dictionary) { - return new UnregMockServiceRegistration(super.registerService(s, o, dictionary)); - } - } - - public static Map createConfig(final String host, - final int port, - final String suffixPattern, - final String facility, - final String stackTracePattern, - final boolean throwableExcluded, final String... loggers) { - Map map = new HashMap<>(); - map.put(SyslogAppender.PROP_HOST, host); - map.put(SyslogAppender.PROP_PORT, port); - map.put(SyslogAppender.PROP_SUFFIX_PATTERN, suffixPattern); - map.put(SyslogAppender.PROP_FACILITY, facility); - map.put(SyslogAppender.PROP_STACK_TRACE_PATTERN, stackTracePattern); - map.put(SyslogAppender.PROP_THROWABLE_EXCLUDED, throwableExcluded); - map.put(SyslogAppender.PROP_LOGGERS, loggers); - return map; - } - - - @Test - public void testActivate() { - SyslogAppender appender = new SyslogAppender(); - MockBundleContext ctx = new UnregMockBundleContext(null); - appender.activate(ctx, createConfig("localhost", 42, "test", - "USER", null, false, "my.logger")); - appender.deactivate(); - } - - @Test - public void testConstructSyslogAppender() { - boolean nullHostThrown = false; - try { - ch.qos.logback.classic.net.SyslogAppender logAppender = - SyslogAppender.constructAppender(createConfig(null, 465, "test", - "USER", null, false, "my.logger")); - } catch (final IllegalArgumentException e) { - nullHostThrown = true; - } - - assertTrue("a null host should throw an IllegalArgumentException", nullHostThrown); - boolean emptyHostThrown = false; - try { - ch.qos.logback.classic.net.SyslogAppender logAppender = - SyslogAppender.constructAppender(createConfig("", 465, "test", - "USER", null, false, "my.logger")); - } catch (final IllegalArgumentException e) { - emptyHostThrown = true; - } - - assertTrue("an empty host should throw an IllegalArgumentException", emptyHostThrown); - - boolean negativePortThrown = false; - try { - ch.qos.logback.classic.net.SyslogAppender logAppender = - SyslogAppender.constructAppender(createConfig("localhost", -1, "test", - "USER", null, false, "my.logger")); - } catch (final IllegalArgumentException e) { - negativePortThrown = true; - } - - assertTrue("a negative port should throw an IllegalArgumentException", negativePortThrown); - - ch.qos.logback.classic.net.SyslogAppender logAppender = - SyslogAppender.constructAppender(createConfig("localhost", 465, "test", - "USER", "fff", false, "my.logger")); - assertEquals("stack trace pattern should match", "fff", logAppender.getStackTracePattern()); - - - } -} diff --git a/pom.xml b/pom.xml index 663c11e93a..f9e4790eae 100644 --- a/pom.xml +++ b/pom.xml @@ -939,6 +939,7 @@ Bundle-DocURL: https://adobe-consulting-services.github.io/acs-aem-commons/ bundle + bundle-onprem oakpal-checks ui.apps ui.content