1
0

[LOGGING-186] Replace custom code with ServiceLoader call (#171)

* Replace custom code with `ServiceLoader` call

* Files should end with an empty line

* Files should end with an empty line

* Comment empty block

* Comment empty block

---------

Co-authored-by: Gary Gregory <garydgregory@users.noreply.github.com>
This commit is contained in:
Piotr P. Karwasz
2023-10-18 22:47:48 +02:00
committed by GitHub
parent 2ec80207a5
commit ca2cdfee81
8 changed files with 187 additions and 32 deletions

21
pom.xml
View File

@@ -391,6 +391,9 @@ under the License.
</goals> </goals>
<configuration> <configuration>
<runOrder>${failsafe.runorder}</runOrder> <runOrder>${failsafe.runorder}</runOrder>
<excludes>
<exclude>org/apache/commons/logging/serviceloader/**</exclude>
</excludes>
<includes> <includes>
<include>**/*TestCase.java</include> <include>**/*TestCase.java</include>
</includes> </includes>
@@ -408,6 +411,24 @@ under the License.
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
</execution> </execution>
<!--
- The ServiceLoaderTestCase uses an alternative implementation of LogFactory,
- therefore it requires a separate execution.
-->
<execution>
<id>serviceLoader-test</id>
<goals>
<goal>integration-test</goal>
</goals>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${project.build.testOutputDirectory}/serviceloader</additionalClasspathElement>
</additionalClasspathElements>
<includes>
<include>org/apache/commons/logging/serviceloader/*TestCase.java</include>
</includes>
</configuration>
</execution>
</executions> </executions>
</plugin> </plugin>

View File

@@ -66,6 +66,9 @@ The <action> type attribute can be add,update,fix,remove.
<action type="fix" dev="ggregory" due-to="step-security-bot, Gary Gregory"> <action type="fix" dev="ggregory" due-to="step-security-bot, Gary Gregory">
[StepSecurity] ci: Harden GitHub Actions #145. [StepSecurity] ci: Harden GitHub Actions #145.
</action> </action>
<action issue="LOGGING-185" type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz">
Replace custom code with `ServiceLoader` call.
</action>
<!-- UPDATES --> <!-- UPDATES -->
<action dev="ggregory" type="update" due-to="Gary Gregory"> <action dev="ggregory" type="update" due-to="Gary Gregory">
Bump Java from 6 to 8. Bump Java from 6 to 8.

View File

@@ -30,7 +30,10 @@ import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
/** /**
* Factory for creating {@link Log} instances, with discovery and * Factory for creating {@link Log} instances, with discovery and
@@ -189,6 +192,12 @@ public abstract class LogFactory {
*/ */
private static final WeakReference<ClassLoader> thisClassLoaderRef; private static final WeakReference<ClassLoader> thisClassLoaderRef;
/**
* Maximum number of {@link ServiceLoader} errors to ignore, while
* looking for an implementation.
*/
private static final int MAX_BROKEN_SERVICES = 3;
// ----------------------------------------------------------- Constructors // ----------------------------------------------------------- Constructors
/** /**
@@ -507,42 +516,25 @@ public abstract class LogFactory {
if (factory == null) { if (factory == null) {
if (isDiagnosticsEnabled()) { if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID + logDiagnostic("[LOOKUP] Using ServiceLoader to define the LogFactory subclass to use...");
"] to define the LogFactory subclass to use...");
} }
try { try {
final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID); final ServiceLoader<LogFactory> serviceLoader = ServiceLoader.load(LogFactory.class);
final Iterator<LogFactory> iterator = serviceLoader.iterator();
if ( is != null ) { int i = MAX_BROKEN_SERVICES;
// This code is needed by EBCDIC and other strange systems. while (factory == null && i-- > 0) {
// It's a fix for bugs reported in xerces
BufferedReader rd;
try { try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); if (iterator.hasNext()) {
} catch (final java.io.UnsupportedEncodingException e) { factory = iterator.next();
rd = new BufferedReader(new InputStreamReader(is)); }
} } catch (final ServiceConfigurationError | LinkageError ex) {
if (isDiagnosticsEnabled()) {
String factoryClassName; logDiagnostic("[LOOKUP] An exception occurred while trying to find an" +
try { " instance of LogFactory" +
factoryClassName = rd.readLine(); ": [" + trim(ex.getMessage()) +
} finally { "]. Trying alternative implementations...");
rd.close();
}
if (factoryClassName != null && ! factoryClassName.isEmpty()) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Creating an instance of LogFactory class " +
factoryClassName +
" as specified by file '" + SERVICE_ID +
"' which was present in the path of the context classloader.");
} }
factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
}
} else {
// is == null
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");
} }
} }
} catch (final Exception ex) { } catch (final Exception ex) {

View File

@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.logging.serviceloader;
import junit.framework.TestCase;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.serviceloader.internal.DummyLogFactory;
public class ServiceLoaderTestCase extends TestCase {
public void testServiceLoader() {
final LogFactory factory = LogFactory.getFactory();
assertTrue("Wrong factory retrieved through ServiceLoader: " + factory.getClass().getName(),
factory instanceof DummyLogFactory);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.logging.serviceloader.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogConfigurationException;
import org.apache.commons.logging.LogFactory;
public class DummyLogFactory extends LogFactory {
@Override
public Object getAttribute(String name) {
return null;
}
@Override
public String[] getAttributeNames() {
return new String[0];
}
@Override
public Log getInstance(Class clazz) throws LogConfigurationException {
return null;
}
@Override
public Log getInstance(String name) throws LogConfigurationException {
return null;
}
@Override
public void release() {
// empty
}
@Override
public void removeAttribute(String name) {
// empty
}
@Override
public void setAttribute(String name, Object value) {
// empty
}
}

View File

@@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.logging.serviceloader.internal;
/**
* A common ServiceLoader error is finding a class that implements LogFactory from a different classloader.
* This class should emulate that behavior.
*/
public class ThrowingLogFactory {
// empty
}

View File

@@ -0,0 +1,25 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
##
# Version 1.2 of Apache Commons Logging did not support comments in the service file.
#
# A class that does not implement LogFactory to emulate services the implement
# LogFactory for another classloader
org.apache.commons.logging.serviceloader.internal.ThrowingLogFactory
##
# A proper LogFactory
org.apache.commons.logging.serviceloader.internal.DummyLogFactory

View File

@@ -0,0 +1 @@
Contains resources used exclusively by `ServiceLoaderTestCase`