Регистрация моей собственной реализации ImageSessionContext для Apache XMLGraphics
Мы используем Apache FOP с его библиотекой XMLGraphics для создания документов для наших клиентов на основе разработанных ими шаблонов. В их шаблонах часто есть изображения, взятые с веб-ресурса. Однако стало очевидно, что в некоторых случаях у нас возникают трудности, когда некоторые старые шаблоны были разработаны с внешними изображениями с использованием
http
протокол и ресурс теперь находится за
https
протокол. Это связано с тем, что xmlgraphics не поддерживает перенаправления при попытке предварительно загрузить изображение из веб-службы. Это связано с тем, что он просто выполняет
URL.openStream()
метод.
Что я хотел бы сделать, так это создать свою собственную реализацию для замены реализации по умолчанию. Это должно быть возможно
DefaultImageSessionContext
расширяет
public abstract class AbstractImageSessionContext
который, в свою очередь, реализует
ImageSessionContext
.
Кто-нибудь знает, как я могу зарегистрировать свою реализацию с помощью FOP?
1 ответ
Это был мой первоначальный ответ:
Извините, если я дам вам неверную информацию, прошло много времени без использования FOP.
Как указано в документации фреймворка Apache XML Graphics Image Loading , для предварительной загрузки изображения, возможно, в какой-то части вашего кода у вас есть что-то вроде этого:
ImageSessionContext sessionContext = new DefaultImageSessionContext(
getImageManager().getImageContext(), null);
ImageInfo info = getImageManager().getImageInfo(url.toString(), sessionContext);
Если вы хотите поддерживать перенаправления, как вы предлагаете, вы можете предоставить свою собственную реализацию.
Просмотр исходного кода
<tcode id="4273533"></tcode>, вероятно, лучшим подходом будет создание нового класса, который переопределяет
resolveURI
метод. Рассмотрим, например, следующий код, основанный на
<tcode id="4273535"></tcode>, который должен следовать за перенаправлениями по мере необходимости:
public class HttpRedirectsAwareImageSessionContext extends DefaultImageSessionContext {
@Override
protected Source resolveURI(String uri) {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(uri);
urlConnection = (HttpURLConnection) url.openConnection();
// Just read the InputStream, to a byte[] for instance. As in the example
// you can use the convenient methods of Apache Commons IOUtils for the task
// as well. See: https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/IOUtils.html#toBufferedInputStream-java.io.InputStream-
InputStream in = IOUtils.toBufferedInputStream(urlConnection.getInputStream());
return new StreamSource(in, url.toExternalForm());
} catch (MalformedURLException e) {
// Legacy DefaultImageSessionContext code
File f = new File(baseDir, uri);
if (f.isFile()) {
return new StreamSource(f);
} else {
return null;
}
} catch (IOException ioe) {
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
}
Затем используйте вместо этого новый created:
ImageSessionContext sessionContext = new HttpRedirectsAwareImageSessionContext(
getImageManager().getImageContext(), null);
ImageInfo info = getImageManager().getImageInfo(url.toString(), sessionContext);
Но боюсь, что не до конца понял ваши требования.
Как вы можете видеть, например, в
FOPUserAgent
исходный код, FOP предоставляет собственный базовый
ImageSessionContext
выполнение:
private final ImageSessionContext imageSessionContext;
/**
* Main constructor. <b>This constructor should not be called directly. Please use the
* methods from FopFactory to construct FOUserAgent instances!</b>
* @param factory the factory that provides environment-level information
* @param resourceResolver the resolver used to acquire resources
* @see org.apache.fop.apps.FopFactory
*/
FOUserAgent(final FopFactory factory, InternalResourceResolver resourceResolver) {
this.factory = factory;
this.resourceResolver = resourceResolver;
setTargetResolution(factory.getTargetResolution());
setAccessibility(factory.isAccessibilityEnabled());
setKeepEmptyTags(factory.isKeepEmptyTags());
imageSessionContext = new AbstractImageSessionContext(factory.getFallbackResolver()) {
public ImageContext getParentContext() {
return factory;
}
public float getTargetResolution() {
return FOUserAgent.this.getTargetResolution();
}
public Source resolveURI(String uri) {
return FOUserAgent.this.resolveURI(uri);
}
};
}
/**
* Attempts to resolve the given URI.
* Will use the configured resolver and if not successful fall back
* to the default resolver.
* @param uri URI to access
* @return A {@link javax.xml.transform.Source} object, or null if the URI
* cannot be resolved.
*/
public StreamSource resolveURI(String uri) {
// TODO: What do we want to do when resources aren't found??? We also need to remove this
// method entirely
try {
// Have to do this so we can resolve data URIs
StreamSource src = new StreamSource(resourceResolver.getResource(uri));
src.setSystemId(getResourceResolver().getBaseURI().toASCIIString());
return src;
} catch (URISyntaxException use) {
return null;
} catch (IOException ioe) {
return null;
}
}
Эта реализация полагается на класс
InternalResourceResolver
, которые, в свою очередь, полагаются на абстракция, предоставляемая платформой загрузки графических изображений XML.
Чтобы решить вашу проблему, вы можете предоставить свою индивидуальную реализацию при инициализации FOP через
FopFactoryBuilder
.
Несмотря на то, что документация Apache FOP довольно устарела, она содержит полезное введение здесь, в этой и, в основном, в других статьях. Например, исходя из информации, представленной в последней ссылке:
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = uri.toURL();
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
// Based on the above-mentioned code
HttpURLConnection urlConnection = null;
try {
URL url = uri.toURL();
urlConnection = (HttpURLConnection) url.openConnection();
// Just read the InputStream, to a byte[] for instance. As in the example
// you can use the convenient methods of Apache Commons IOUtils for the task
// as well. See: https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/IOUtils.html#toBufferedInputStream-java.io.InputStream-
InputStream in = IOUtils.toBufferedInputStream(urlConnection.getInputStream());
return new StreamSource(in, url.toExternalForm());
} catch (MalformedURLException e) {
// Legacy DefaultImageSessionContext code
File f = new File(baseDir, uri);
if (f.isFile()) {
return new StreamSource(f);
} else {
return null;
}
} catch (IOException ioe) {
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
};
//Setting up the FOP factory
FopFactoryBuilder builder = new FopFactoryBuilder(new File(".").toURI(), resolver);
fopFactory = builder.build();
Имейте в виду, что это очень простой пример, актуальный
ResourceResolver
, построенный ФОП в
ResourceResolverFactory
довольно сложно, но, возможно, может дать вам несколько идей о том, как реализовать свой собственный:
/*
* 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.
*/
/* $Id$ */
package org.apache.fop.apps.io;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.xmlgraphics.io.Resource;
import org.apache.xmlgraphics.io.ResourceResolver;
import org.apache.xmlgraphics.io.TempResourceResolver;
import org.apache.xmlgraphics.io.TempResourceURIGenerator;
/**
* A factory class for {@link ResourceResolver}s.
*/
public final class ResourceResolverFactory {
private ResourceResolverFactory() {
}
/**
* Returns the default resource resolver, this is most basic resolver which can be used when
* no there are no I/O or file access restrictions.
*
* @return the default resource resolver
*/
public static ResourceResolver createDefaultResourceResolver() {
return DefaultResourceResolver.INSTANCE;
}
/**
* A helper merthod that creates an internal resource resolver using the default resover:
* {@link ResourceResolverFactory#createDefaultResourceResolver()}.
*
* @param baseURI the base URI from which to resolve URIs
* @return the default internal resource resolver
*/
public static InternalResourceResolver createDefaultInternalResourceResolver(URI baseURI) {
return new InternalResourceResolver(baseURI, createDefaultResourceResolver());
}
/**
* Creates an interal resource resolver given a base URI and a resource resolver.
*
* @param baseURI the base URI from which to resolve URIs
* @param resolver the resource resolver
* @return the internal resource resolver
*/
public static InternalResourceResolver createInternalResourceResolver(URI baseURI,
ResourceResolver resolver) {
return new InternalResourceResolver(baseURI, resolver);
}
/**
* Creates a temporary-resource-scheme aware resource resolver. Temporary resource URIs are
* created by {@link TempResourceURIGenerator}.
*
* @param tempResourceResolver the temporary-resource-scheme resolver to use
* @param defaultResourceResolver the default resource resolver to use
* @return the ressource resolver
*/
public static ResourceResolver createTempAwareResourceResolver(
TempResourceResolver tempResourceResolver,
ResourceResolver defaultResourceResolver) {
return new TempAwareResourceResolver(tempResourceResolver, defaultResourceResolver);
}
/**
* This creates the builder class for binding URI schemes to implementations of
* {@link ResourceResolver}. This allows users to define their own URI schemes such that they
* have finer control over the acquisition of resources.
*
* @param defaultResolver the default resource resolver that should be used in the event that
* none of the other registered resolvers match the scheme
* @return the scheme aware {@link ResourceResolver} builder
*/
public static SchemeAwareResourceResolverBuilder createSchemeAwareResourceResolverBuilder(
ResourceResolver defaultResolver) {
return new SchemeAwareResourceResolverBuilderImpl(defaultResolver);
}
private static final class DefaultResourceResolver implements ResourceResolver {
private static final ResourceResolver INSTANCE = new DefaultResourceResolver();
private final TempAwareResourceResolver delegate;
private DefaultResourceResolver() {
delegate = new TempAwareResourceResolver(new DefaultTempResourceResolver(),
new NormalResourceResolver());
}
/** {@inheritDoc} */
public Resource getResource(URI uri) throws IOException {
return delegate.getResource(uri);
}
/** {@inheritDoc} */
public OutputStream getOutputStream(URI uri) throws IOException {
return delegate.getOutputStream(uri);
}
}
private static final class TempAwareResourceResolver implements ResourceResolver {
private final TempResourceResolver tempResourceResolver;
private final ResourceResolver defaultResourceResolver;
public TempAwareResourceResolver(TempResourceResolver tempResourceHandler,
ResourceResolver defaultResourceResolver) {
this.tempResourceResolver = tempResourceHandler;
this.defaultResourceResolver = defaultResourceResolver;
}
private static boolean isTempURI(URI uri) {
return TempResourceURIGenerator.isTempURI(uri);
}
/** {@inheritDoc} */
public Resource getResource(URI uri) throws IOException {
if (isTempURI(uri)) {
return tempResourceResolver.getResource(uri.getPath());
} else {
return defaultResourceResolver.getResource(uri);
}
}
/** {@inheritDoc} */
public OutputStream getOutputStream(URI uri) throws IOException {
if (isTempURI(uri)) {
return tempResourceResolver.getOutputStream(uri.getPath());
} else {
return defaultResourceResolver.getOutputStream(uri);
}
}
}
private static class DefaultTempResourceResolver implements TempResourceResolver {
private final ConcurrentHashMap<String, File> tempFiles = new ConcurrentHashMap<String, File>();
private File getTempFile(String uri) throws IllegalStateException {
File tempFile = tempFiles.remove(uri);
if (tempFile == null) {
throw new IllegalStateException(uri + " was never created or has been deleted");
}
return tempFile;
}
private File createTempFile(String path) throws IOException {
File tempFile = File.createTempFile(path, ".fop.tmp");
File oldFile = tempFiles.put(path, tempFile);
if (oldFile != null) {
String errorMsg = oldFile.getAbsolutePath() + " has been already created for " + path;
boolean newTempDeleted = tempFile.delete();
if (!newTempDeleted) {
errorMsg += ". " + tempFile.getAbsolutePath() + " was not deleted.";
}
throw new IOException(errorMsg);
}
return tempFile;
}
/** {@inheritDoc} */
public Resource getResource(String id) throws IOException {
return new Resource(new FileDeletingInputStream(getTempFile(id)));
}
/** {@inheritDoc} */
public OutputStream getOutputStream(String id) throws IOException {
return new FileOutputStream(createTempFile(id));
}
}
private static class FileDeletingInputStream extends FilterInputStream {
private final File file;
protected FileDeletingInputStream(File file) throws MalformedURLException, IOException {
super(file.toURI().toURL().openStream());
this.file = file;
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
file.delete();
}
}
}
private static class NormalResourceResolver implements ResourceResolver {
public Resource getResource(URI uri) throws IOException {
return new Resource(uri.toURL().openStream());
}
public OutputStream getOutputStream(URI uri) throws IOException {
return new FileOutputStream(new File(uri));
}
}
private static final class SchemeAwareResourceResolver implements ResourceResolver {
private final Map<String, ResourceResolver> schemeHandlingResourceResolvers;
private final ResourceResolver defaultResolver;
private SchemeAwareResourceResolver(
Map<String, ResourceResolver> schemEHandlingResourceResolvers,
ResourceResolver defaultResolver) {
this.schemeHandlingResourceResolvers = schemEHandlingResourceResolvers;
this.defaultResolver = defaultResolver;
}
private ResourceResolver getResourceResolverForScheme(URI uri) {
String scheme = uri.getScheme();
if (schemeHandlingResourceResolvers.containsKey(scheme)) {
return schemeHandlingResourceResolvers.get(scheme);
} else {
return defaultResolver;
}
}
/** {@inheritDoc} */
public Resource getResource(URI uri) throws IOException {
return getResourceResolverForScheme(uri).getResource(uri);
}
/** {@inheritDoc} */
public OutputStream getOutputStream(URI uri) throws IOException {
return getResourceResolverForScheme(uri).getOutputStream(uri);
}
}
/**
* Implementations of this interface will be builders for {@link ResourceResolver}, they bind
* URI schemes to their respective resolver. This gives users more control over the mechanisms
* by which URIs are resolved.
* <p>
* Here is an example of how this could be used:
* </p>
* <p><code>
* SchemeAwareResourceResolverBuilder builder
* = ResourceResolverFactory.createSchemeAwareResourceResolverBuilder(defaultResolver);
* builder.registerResourceResolverForScheme("test", testResolver);
* builder.registerResourceResolverForScheme("anotherTest", test2Resolver);
* ResourceResolver resolver = builder.build();
* </code></p>
* This will result in all URIs for the form "test:///..." will be resolved using the
* <code>testResolver</code> object; URIs of the form "anotherTest:///..." will be resolved
* using <code>test2Resolver</code>; all other URIs will be resolved from the defaultResolver.
*/
public interface SchemeAwareResourceResolverBuilder {
/**
* Register a scheme with its respective {@link ResourceResolver}. This resolver will be
* used as the only resolver for the specified scheme.
*
* @param scheme the scheme to be used with the given resolver
* @param resourceResolver the resource resolver
*/
void registerResourceResolverForScheme(String scheme, ResourceResolver resourceResolver);
/**
* Builds a {@link ResourceResolver} that will delegate to the respective resource resolver
* when a registered URI scheme is given
*
* @return a resolver that delegates to the appropriate scheme resolver
*/
ResourceResolver build();
}
private static final class CompletedSchemeAwareResourceResolverBuilder
implements SchemeAwareResourceResolverBuilder {
private static final SchemeAwareResourceResolverBuilder INSTANCE
= new CompletedSchemeAwareResourceResolverBuilder();
/** {@inheritDoc} */
public ResourceResolver build() {
throw new IllegalStateException("Resource resolver already built");
}
/** {@inheritDoc} */
public void registerResourceResolverForScheme(String scheme,
ResourceResolver resourceResolver) {
throw new IllegalStateException("Resource resolver already built");
}
}
private static final class ActiveSchemeAwareResourceResolverBuilder
implements SchemeAwareResourceResolverBuilder {
private final Map<String, ResourceResolver> schemeHandlingResourceResolvers
= new HashMap<String, ResourceResolver>();
private final ResourceResolver defaultResolver;
private ActiveSchemeAwareResourceResolverBuilder(ResourceResolver defaultResolver) {
this.defaultResolver = defaultResolver;
}
/** {@inheritDoc} */
public void registerResourceResolverForScheme(String scheme,
ResourceResolver resourceResolver) {
schemeHandlingResourceResolvers.put(scheme, resourceResolver);
}
/** {@inheritDoc} */
public ResourceResolver build() {
return new SchemeAwareResourceResolver(
Collections.unmodifiableMap(schemeHandlingResourceResolvers), defaultResolver);
}
}
private static final class SchemeAwareResourceResolverBuilderImpl
implements SchemeAwareResourceResolverBuilder {
private SchemeAwareResourceResolverBuilder delegate;
private SchemeAwareResourceResolverBuilderImpl(ResourceResolver defaultResolver) {
this.delegate = new ActiveSchemeAwareResourceResolverBuilder(defaultResolver);
}
/** {@inheritDoc} */
public void registerResourceResolverForScheme(String scheme,
ResourceResolver resourceResolver) {
delegate.registerResourceResolverForScheme(scheme, resourceResolver);
}
/** {@inheritDoc} */
public ResourceResolver build() {
ResourceResolver resourceResolver = delegate.build();
delegate = CompletedSchemeAwareResourceResolverBuilder.INSTANCE;
return resourceResolver;
}
}
}
Обрати внимание на
NormalResourceResolver
и
getResource
реализация метода.
Весь упомянутый исходный код относится к FOP версии 2.6, которую можно загрузить отсюда .