Skip to content

Commit

Permalink
TIKA-2389 -- allow users to configure warnings for problems during in…
Browse files Browse the repository at this point in the history
…itialization
  • Loading branch information
tballison committed Jul 3, 2017
1 parent 93f941e commit 4161f22
Show file tree
Hide file tree
Showing 20 changed files with 636 additions and 96 deletions.
28 changes: 25 additions & 3 deletions tika-core/src/main/java/org/apache/tika/config/Initializable.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,40 @@
*/
package org.apache.tika.config;

import org.apache.tika.exception.TikaConfigException;

import java.util.Map;

import org.apache.tika.exception.TikaConfigException;

/**
* Components that must do special processing across multiple fields
* at initialization time should implement this interface.
* <p>
* TikaConfig will call initialize on Initializable classes after
* setting the parameters.
* setting the parameters for non-statically service loaded classes.
* <p>
* TikaConfig will call checkInitialization on all Initializables,
* whether loaded statically
*/
public interface Initializable {

/**
* @param params params to use for initialization
* @throws TikaConfigException
*/
void initialize(Map<String, Param> params) throws TikaConfigException;


/**
*
*
* @param problemHandler if there is a problem and no
* custom initializableProblemHandler has been configured
* via Initializable parameters,
* this is called to respond.
* @throws TikaConfigException
*/
void checkInitialization(InitializableProblemHandler problemHandler)
throws TikaConfigException;


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.tika.config;


import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.tika.exception.TikaConfigException;


/**
* This is to be used to handle potential recoverable problems that
* might arise during initialization.
*/
public interface InitializableProblemHandler {


void handleInitializableProblem(String className, String message) throws TikaConfigException;

/**
* Strategy that simply ignores all problems.
*/
InitializableProblemHandler IGNORE = new InitializableProblemHandler() {
public void handleInitializableProblem(String className, String message) {
}
@Override
public String toString() {
return "IGNORE";
}
};

/**
* Strategy that logs warnings of all problems using a {@link Logger}
* created using the given class name.
*/
InitializableProblemHandler INFO = new InitializableProblemHandler() {
public void handleInitializableProblem(String classname, String message) {
Logger.getLogger(classname).log(
Level.INFO, message);
}
@Override
public String toString() {
return "INFO";
}
};


/**
* Strategy that logs warnings of all problems using a {@link Logger}
* created using the given class name.
*/
InitializableProblemHandler WARN = new InitializableProblemHandler() {
public void handleInitializableProblem(String classname, String message) {
Logger.getLogger(classname).log(
Level.WARNING, message);
}
@Override
public String toString() {
return "WARN";
}
};

InitializableProblemHandler THROW = new InitializableProblemHandler() {
public void handleInitializableProblem(String classname, String message) throws TikaConfigException {
throw new TikaConfigException(message);
}
@Override
public String toString() {
return "THROW";
}
};

InitializableProblemHandler DEFAULT = WARN;

}
38 changes: 35 additions & 3 deletions tika-core/src/main/java/org/apache/tika/config/ServiceLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,22 @@ static Object removeService(Object reference) {
private final ClassLoader loader;

private final LoadErrorHandler handler;
private final InitializableProblemHandler initializableProblemHandler;

private final boolean dynamic;

public ServiceLoader(
ClassLoader loader, LoadErrorHandler handler, boolean dynamic) {
ClassLoader loader, LoadErrorHandler handler,
InitializableProblemHandler initializableProblemHandler, boolean dynamic) {
this.loader = loader;
this.handler = handler;
this.initializableProblemHandler = initializableProblemHandler;
this.dynamic = dynamic;

}
public ServiceLoader(
ClassLoader loader, LoadErrorHandler handler, boolean dynamic) {
this(loader, handler, InitializableProblemHandler.WARN, dynamic);
}

public ServiceLoader(ClassLoader loader, LoadErrorHandler handler) {
Expand Down Expand Up @@ -164,6 +172,16 @@ public LoadErrorHandler getLoadErrorHandler() {
return handler;
}

/**
* Returns the handler for problems with initializables
*
* @return handler for problems with initializables
* @since Apache Tika 1.15.1
*/
public InitializableProblemHandler getInitializableProblemHandler() {
return initializableProblemHandler;
}

/**
* Returns an input stream for reading the specified resource from the
* configured class loader.
Expand All @@ -181,6 +199,16 @@ public InputStream getResourceAsStream(String name) {
}
}

/**
*
* @return ClassLoader used by this ServiceLoader
* @see #getContextClassLoader() for the context's ClassLoader
* @since Apache Tika 1.15.1
*/
public ClassLoader getLoader() {
return loader;
}

/**
* Loads and returns the named service class that's expected to implement
* the given interface.
Expand Down Expand Up @@ -327,7 +355,11 @@ public <T> List<T> loadStaticServiceProviders(Class<T> iface) {
try {
Class<?> klass = loader.loadClass(name);
if (iface.isAssignableFrom(klass)) {
providers.add((T) klass.newInstance());
T instance = (T) klass.newInstance();
if (instance instanceof Initializable) {
((Initializable)instance).checkInitialization(InitializableProblemHandler.WARN);
}
providers.add(instance);
}
} catch (Throwable t) {
handler.handleLoadError(name, t);
Expand Down
48 changes: 43 additions & 5 deletions tika-core/src/main/java/org/apache/tika/config/TikaConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
Expand All @@ -47,6 +48,7 @@
import org.apache.tika.detect.DefaultEncodingDetector;
import org.apache.tika.detect.Detector;
import org.apache.tika.detect.EncodingDetector;
import org.apache.tika.exception.TikaConfigException;
import org.apache.tika.exception.TikaException;
import org.apache.tika.language.translate.DefaultTranslator;
import org.apache.tika.language.translate.Translator;
Expand Down Expand Up @@ -231,23 +233,25 @@ public TikaConfig(ClassLoader loader)
* @throws TikaException if problem with MimeTypes or parsing XML config
*/
public TikaConfig() throws TikaException, IOException {
this.serviceLoader = new ServiceLoader();

String config = System.getProperty("tika.config");
if (config == null) {
config = System.getenv("TIKA_CONFIG");
}

if (config == null) {
this.serviceLoader = new ServiceLoader();
this.mimeTypes = getDefaultMimeTypes(ServiceLoader.getContextClassLoader());
this.encodingDetector = getDefaultEncodingDetector(serviceLoader);
this.parser = getDefaultParser(mimeTypes, serviceLoader, encodingDetector);
this.detector = getDefaultDetector(mimeTypes, serviceLoader);
this.translator = getDefaultTranslator(serviceLoader);
this.executorService = getDefaultExecutorService();
} else {
try (InputStream stream = getConfigInputStream(config, serviceLoader)) {
ServiceLoader tmpServiceLoader = new ServiceLoader();
try (InputStream stream = getConfigInputStream(config, tmpServiceLoader)) {
Element element = getBuilder().parse(stream).getDocumentElement();
serviceLoader = serviceLoaderFromDomElement(element, tmpServiceLoader.getLoader());
DetectorXmlLoader detectorLoader = new DetectorXmlLoader();
EncodingDetectorXmlLoader encodingDetectorLoader = new EncodingDetectorXmlLoader();
TranslatorXmlLoader translatorLoader = new TranslatorXmlLoader();
Expand Down Expand Up @@ -474,7 +478,7 @@ private static Set<MediaType> mediaTypesListFromDomElement(
return Collections.emptySet();
}

private static ServiceLoader serviceLoaderFromDomElement(Element element, ClassLoader loader) {
private static ServiceLoader serviceLoaderFromDomElement(Element element, ClassLoader loader) throws TikaConfigException {
Element serviceLoaderElement = getChild(element, "service-loader");
ServiceLoader serviceLoader;
if (serviceLoaderElement != null) {
Expand All @@ -486,8 +490,9 @@ private static ServiceLoader serviceLoaderFromDomElement(Element element, ClassL
} else if(LoadErrorHandler.THROW.toString().equalsIgnoreCase(loadErrorHandleConfig)) {
loadErrorHandler = LoadErrorHandler.THROW;
}

serviceLoader = new ServiceLoader(loader, loadErrorHandler, dynamic);

InitializableProblemHandler initializableProblemHandler = getInitializableProblemHandler(serviceLoaderElement.getAttribute("initializableProblemHandler"));
serviceLoader = new ServiceLoader(loader, loadErrorHandler, initializableProblemHandler, dynamic);
} else if(loader != null) {
serviceLoader = new ServiceLoader(loader);
} else {
Expand All @@ -496,6 +501,26 @@ private static ServiceLoader serviceLoaderFromDomElement(Element element, ClassL
return serviceLoader;
}

private static InitializableProblemHandler getInitializableProblemHandler(String initializableProblemHandler)
throws TikaConfigException {
if (initializableProblemHandler == null || initializableProblemHandler.length() == 0) {
return InitializableProblemHandler.DEFAULT;
}
if (InitializableProblemHandler.IGNORE.toString().equalsIgnoreCase(initializableProblemHandler)) {
return InitializableProblemHandler.IGNORE;
} else if (InitializableProblemHandler.INFO.toString().equalsIgnoreCase(initializableProblemHandler)) {
return InitializableProblemHandler.INFO;
} else if (InitializableProblemHandler.WARN.toString().equalsIgnoreCase(initializableProblemHandler)) {
return InitializableProblemHandler.WARN;
} else if (InitializableProblemHandler.THROW.toString().equalsIgnoreCase(initializableProblemHandler)) {
return InitializableProblemHandler.THROW;
}
throw new TikaConfigException(
String.format(Locale.US, "Couldn't parse non-null '%s'. Must be one of 'ignore', 'info', 'warn' or 'throw'",
initializableProblemHandler));
}


private static abstract class XmlLoader<CT,T> {
protected static final String PARAMS_TAG_NAME = "params";
abstract boolean supportsComposite();
Expand Down Expand Up @@ -547,6 +572,16 @@ CT loadOverall(Element element, MimeTypes mimeTypes,
T loadOne(Element element, MimeTypes mimeTypes, ServiceLoader loader)
throws TikaException, IOException {
String name = element.getAttribute("class");

String initProbHandler = element.getAttribute("initializableProblemHandler");
InitializableProblemHandler initializableProblemHandler;
if (initProbHandler == null || initProbHandler.length() == 0) {
initializableProblemHandler = loader.getInitializableProblemHandler();
} else {
initializableProblemHandler =
getInitializableProblemHandler(initProbHandler);
}

T loaded = null;

try {
Expand Down Expand Up @@ -601,6 +636,7 @@ T loadOne(Element element, MimeTypes mimeTypes, ServiceLoader loader)
AnnotationUtils.assignFieldParams(loaded, params);
if (loaded instanceof Initializable) {
((Initializable) loaded).initialize(params);
((Initializable) loaded).checkInitialization(initializableProblemHandler);
}
// Have any decoration performed, eg explicit mimetypes
loaded = decorate(loaded, element);
Expand Down Expand Up @@ -630,6 +666,7 @@ T loadOne(Element element, MimeTypes mimeTypes, ServiceLoader loader)
}
}


T newInstance(Class<? extends T> loadedClass) throws
IllegalAccessException, InstantiationException,
NoSuchMethodException, InvocationTargetException {
Expand Down Expand Up @@ -1087,4 +1124,5 @@ EncodingDetector decorate(EncodingDetector created, Element element) {
}
}


}
31 changes: 31 additions & 0 deletions tika-core/src/test/java/org/apache/tika/config/TikaConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.tika.config.DummyExecutor;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.config.TikaConfigTest;
import org.apache.tika.exception.TikaConfigException;
import org.apache.tika.exception.TikaException;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.CompositeParser;
Expand Down Expand Up @@ -261,4 +262,34 @@ public void testTikaExecutorServiceFromConfig() throws Exception {
assertEquals("Should have configured Core Threads", 3, executorService.getCorePoolSize());
assertEquals("Should have configured Max Threads", 10, executorService.getMaximumPoolSize());
}

@Test(expected = TikaConfigException.class)
public void testInitializerBadValue() throws Exception {
TikaConfig config = getConfig("TIKA-2389-illegal.xml");
}


@Test(expected = TikaConfigException.class)
public void testInitializerPerParserThrow() throws Exception {
TikaConfig config = getConfig("TIKA-2389-throw-per-parser.xml");
}

@Test(expected = TikaConfigException.class)
public void testInitializerServiceLoaderThrow() throws Exception {
TikaConfig config = getConfig("TIKA-2389-throw-default.xml");
}

@Test
public void testInitializerServiceLoaderThrowButOverridden() throws Exception {
//TODO: test that this was logged at INFO level
TikaConfig config = getConfig("TIKA-2389-throw-default-overridden.xml");
}


@Test
public void testInitializerPerParserWarn() throws Exception {
//TODO: test that this was logged at WARN level
TikaConfig config = getConfig("TIKA-2389-warn-per-parser.xml");
}

}
Loading

0 comments on commit 4161f22

Please sign in to comment.