Skip to main content

Spring Boot / Spring Framework

Spring Framework

Das Spring Framework hat sich in den letzten Jahren bei der schnellen Entwicklung von Java Backend Anwendungen etabliert. Spring macht es den Entwicklern leicht, produktionsreife Anwendungen in kurzer Zeit zu erstellen. Das Spring Framework ergänzt dabei die bekannten JakartaEE Spezifikationen wie zum Beispiel Servlet API (JSR 340), WebSocket API (JSR 356), JSON Binding API (JSR 367) oder JPA (JSR 338).

Das Spring Framework basiert auf einem objektorientierten Entwurfsmuster, das als Dependency Injection bekannt geworden ist. Hierbei geht es darum, Objekte möglichst lose miteinander zu koppeln. Dies wird dadurch erreicht, dass ein Objekt andere Objekte, deren Hilfe es für seine Aufgabe benötigt, von außen "injiziert" bekommt, anstatt diese selbst zu erzeugen oder sich auf andere Art und Weise eigenverantwortlich zu beschaffen.

Spring Boot

Spring Boot hilft bei der Entwicklung von Spring basierten stand-alone Anwendungen, was sehr gut in die Welt von Microservices passt. Das jadice web toolkit und Spring Boot geben dabei einen Weg vor, der unserer Ansicht nach am besten funktioniert, um möglichst schnell und einfach eine Anwendung zu entwickeln. Dies wird unter anderem dadurch erzielt, dass es sinnvolle Defaults gibt und möglichst wenig Konfiguration erforderlich ist, um eine Anwendung zu entwickeln.

Integratoren, die das jadice web toolkit in eine Spring- oder Spring-Boot-Applikation integrieren möchten, können das ausgelieferte Integrationsmodul nutzen.

Spring Boot-Integrationsmodul

Das jadice web toolkit folgt dem Spring-Boot-Starter-Konzept, so, dass das Aufsetzen einer Spring-Boot-Anwendung mit dem jadice web toolkit sich sehr einfach gestaltet. Das webtoolkit-spring-boot-starter als Starter für das jadice web toolkit ist Bestandteil der Auslieferung und kann als Maven-Dependency eingebunden werden. Er bringt die erforderlichen Dependencies auf die einzubindenden jadice-web-toolkit- und Spring-Boot-Module mit:

    <dependency>
<groupId>com.levigo.jadice.webtoolkit</groupId>
<artifactId>webtoolkit-spring-boot-starter</artifactId>
</dependency>

Ein einführendes Tutorial, basierend auf Spring Boot, existiert unter Getting Started - jadice web toolkit mit Spring Boot.

Der Grundgedanke des webtoolkit-spring-boot-starter liegt darin, die Spring-Paradigmen für das jadice web toolkit zu unterstützen, so, dass die erforderlichen Komponenten deklarativ injiziert bzw. konfiguriert werden können. Dies umfasst DocumentDataProvider, ServerOperations, ContextualFactories sowie Annotationsprofile und Konfigurationswerte - wie nachfolgend ausführlich beschrieben. Die programmatische Registrierung dieser Komponenten, die früher typischerweise über einen WebtoolkitServletContextListener vorgenommen wurde, kann dadurch vollständig entfallen.

Spring Boot Dependencies

Das jadice web toolkit erwartet, dass die Spring Boot Dependencies von der Integration bereitgestellt werden. Dadurch haben Sie die Kontrolle, über die konkret eingebundene Version. Wir empfehlen, die BOM (Bill of Materials) zu nutzen, um die Versionen und Abhängigkeiten zu setzen. Weitere Informationen hierzu finden Sie in unserer Knowledge Base.

Threadmanagement

Das Spring-Modul sorgt dafür, dass bei asynchron ausgeführten Operationen der zuständige Thread mit dem Spring-SecurityContext assoziiert wird. Dies passiert allerdings nur, sofern Spring Security im Klassenpfad gefunden wird. Andernfalls wird ein gewöhnlicher ThreadPoolExecutor aufgerufen.

Spring Boot Application

Eine jadice web toolkit-Spring-Boot-Anwendung wird durch Hinzufügen der Annotationen @SpringBootApplication und @EnableJWTSpringBootApplication(...) definiert:

  • @SpringBootApplication: zur Aktivierung einer Spring Boot Application, entsprechend Using the @SpringBootApplication Annotation

  • @EnableJWTSpringBootApplication(...): Diese Annotation bringt der webtoolkit-spring-boot-starter mit. Sie sorgt im Hintergrund für ein Autowiring von DocumentDataProvidern, ServerOperations und Annotationsprofilen.

    @SpringBootApplication
// Activate jadice web toolkit support specifying the GWT module name for the entry point
@EnableJWTSpringBootApplication("com.levigo.jadice.web.demo.springboot.Application")
public class DemoApplication extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

Automatische Registrierung von DocumentDataProvidern

DocumentDataProvider-Implementierungen lassen sich mittels Dependency Injection automatisch registrieren. Eine programmatische Registrierung an der DocumentDataProviderRegistry (siehe DocumentDataProviderRegistry) ist dann nicht mehr erforderlich. Hierzu ist die entsprechende Klasse lediglich mit der Spring-Annotation @Component zu versehen:

    @Component
public class MyDocumentDataProvider implements DocumentDataProvider<MySource, MyPageSegmentHandle> {
...
}

Automatische Registrierung von ServerOperations

Auch ServerOperations lassen sich als @Component injizieren. Die manuelle Registrierung bei der ServerOperationRegistry im ServletContextListener lt. Server Operations entfällt dann.

    @Component
public class SampleServerOperation implements ServerOperation<SampleServerOperationParameters, SampleServerOperationMessage> {
...
}

Automatische Registrierung von ContextualFactories

Genauso können auch ContextualFactories als @Component injiziert werden, ohne dass diese programmatisch registriert werden müssen:

    @Component
public class MyContextualServerOperationFactory implements ContextualFactory<ServerOperation<MyContextualServerOperationParameters, MyContextualServerOperationMessage>> {
@Override
public MyContextualServerOperation create(InvocationContext context) {
ServletInvocationContext servletInvocationContext = (ServletInvocationContext) context;
return new MyContextualServerOperation(servletInvocationContext.getSession().getId());
}
}

@Component
public class MyContextualDocumentDataProviderFactory implements ContextualFactory<DocumentDataProvider<ClassPathSource, ClassPathHandle>> {
@Override
public DocumentDataProvider<ClassPathSource, ClassPathHandle> create(InvocationContext context) {
return new ClassPathDocumentDataProvider();
}
}

Konfiguration serverseitiger Einstellungen

In einer Spring-Boot-Umgebung lassen sich Einstellungen des jadice web toolkit, die für gewöhnlich programmatisch über die ServerConfiguration vorgenommen werden, deklarativ über eine application.yml bzw. application.properties konfigurieren.

Dabei werden die für das jadice web toolkit spezifischen Einstellungen mit dem Prefix webtoolkit angesprochen und die Werte über entsprechende Selektoren konfiguriert. Die Namen der Selektoren entsprechen dabei den Namen der Setter der ServerConfiguration.

Im folgenden Beispiel wird eine programmatische Konfiguration ihrem deklarativen Pendant gegenübergestellt:

ConfigurationManager.getServerConfiguration().getNetworkConfiguration().setSessionTimeout(Duration.ofSeconds(30));
ConfigurationManager.getServerConfiguration().getNetworkConfiguration().setResponseAggregationWindow(Duration.ofMillis(20));
ConfigurationManager.getServerConfiguration().setTileCompressionType(TileCompressionType.PNGJ_BEST_COMPRESSION);
ConfigurationManager.getServerConfiguration().getNetworkConfiguration().setKeepAliveInterval(Duration.ofSeconds(30));

Konfiguration per application.properties:

webtoolkit.tileCompressionType: PNGJ_BEST_COMPRESSION
webtoolkit.networkConfiguration.sessionTimeout: 30s
webtoolkit.networkConfiguration.responseAggregationWindow: 20ms
webtoolkit.networkConfiguration.keepAliveInterval: 30s

Konfiguration per application.yml:

webtoolkit:
tileCompressionType: PNGJ_BEST_COMPRESSION

networkConfiguration:
sessionTimeout: 30s
responseAggregationWindow: 20ms
keepAliveInterval: 30s

Deklaration von Annotationsprofilen

Neben den gerade erwähnten Konfigurationswerten lassen sich auch Annotationsprofile über die Spring-Boot-Konfigurationsdateien spezifizieren. Nachfolgendes Beispiel zeigt die Konfiguration zweier Annotationsprofile in einer application.yml.

#JWT prefix
webtoolkit:

# Specify annotation profiles
annotationProfiles: /jwt-minimal-profile.xml, /jwt-second-profile.xml

# Specify default annotation profile (The value must match annotation-profile name in jwt-minimal-profile.xml)
defaultAnnotationProfile: JWT-Minimal

WAR-Deployment (Traditional Deployment)

Spring Boot Anwendungen sind dafür vorgesehen, als "Runnable-JAR" erstellt zu werden, das heißt, nach dem Build der Anwendung entsteht eine JAR-Datei, die direkt mit java -jar MySpringBootApp.jar gestartet werden kann. Spring Boot liefert hierfür einen eingebetteten Tomcat mit. Durch den Import eines anderen "Starter"-Moduls können beispielsweise auch Wildfly oder weitere App-Server verwendet werden.

Es ist jedoch auch weiterhin möglich, die Anwendung als WAR-Datei erstellen zu lassen, um sie so auf einem App-Server zu deployen. Die dafür benötigten Anpassungen an den Dependencies finden Sie im Kapitel Artefakte.

Möglicherweise sind darüber hinaus noch Anpassungen am Code notwendig. Beim Traditional Deployment wird der App-Server nicht von Spring Boot gestartet und verwaltet. Das hat zur Folge, dass manche Dinge anders funktionieren als erwartet. Insbesondere das Verhalten von @ServletComponentScan ist in diesem Szenario anders. Da der App-Server die Javax-Annotationen wie @WebServlet, @WebFilter etc. erfasst und die Klassen entsprechend registriert, kann Spring Boot hier nicht eingreifen und Felder die per @Autowired annotiert sind folglich nicht automatisch injecten. Wenn Sie also beispielsweise in Servlets Komponenten per Dependency Injection (@Autowired) injizieren möchten, funktioniert dies beim Traditional Deployment nicht.

Die folgenden Schritte sind nötig, um das Autowiring manuell durchzuführen:

  • @WebServlet-Annotation im Servlet entfernen
  • An einer Klasse, welche vom @ComponentScan erfasst wird (also in einem entsprechendem Java-Package liegt), das folgende Feld injecten: @Autowired AutowireCapableBeanFactory beanFactory;
  • Ein Snippet entsprechend dem Beispiel in dieser Klasse einfügen
@Bean
public ServletRegistrationBean<MyTestServlet> fileUploadServiceServletRegistrationBean(){
ServletRegistrationBean<MyTestServlet> servletRegistrationBean = new ServletRegistrationBean<>();
MyTestServlet servlet = new MyTestServlet();
beanFactory.autowireBean(servlet);
servletRegistrationBean.setServlet(servlet);
servletRegistrationBean.setUrlMappings(Collections.singletonList("/testEndpoint"));
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}

Eine weitere Besonderheit in diesem Szenario ist, dass die web.xml-Datei nicht entfallen darf.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>MyApp</display-name>
</web-app>

Beim Traditional Deployment kann außerdem die Verwendung des spring-boot-maven-plugin entfallen. Dieses Plugin sorgt dafür, dass durch das Repacking zwei Ordner entstehen, WEB-INF/lib und WEB-INF/lib-provided. Beim Deployment auf einem App-Server, werden die Libraries unter WEB-INF/lib-provided ignoriert. Wird die Anwendung jedoch über die Command-Line gestartet (java -jar), werden die Libraries verwendet, da in diesem Fall kein App-Server zur Verfügung steht. Wenn Sie sich für das Traditional Deployment entschieden haben, ist der Ordner WEB-INF/lib-provided in jedem Fall überflüssig und kann somit entfallen, da die Dateigröße des WAR-Archivs nur unnötig ansteigt. Weitere Informationen hierzu finden Sie in der offiziellen Dokumentation.

Nutzung des jadice web toolkit ohne Spring Framework

Das jadice web toolkit kann grundsätzlich auch ohne das Spring Framework und den damit verbundenen Mechanismen wie Dependency Injection genutzt werden. Dies wird jedoch ausdrücklich nicht empfohlen.

Hierfür empfiehlt es sich, den Weg zu wählen, der in der Vergangenheit vom jadice web toolkit verwendet wurde: eine Konfiguration über den WebtoolkitServletContextListener. Da in einer Umgebung ohne Spring die benötigten Services nicht automatisch erstellt werden, ist dies von Hand zu erledigen. Die Schritte hierfür sind nicht trivial:

  • Erstellen eines JWTServerContext. Dieser enthält die Services, die vom jadice web toolkit benötigt werden. Der Context kann dann im ServletContext des Application Servers abgelegt werden, damit auf jadice Services, wie zum Beispiel die ServerOperationRegistry, an anderen Stellen (Servlets etc.) zugegriffen werden kann.
  • Konfiguration der jadice document platform über JadicePreferenceHolder
  • Initialisierung der Caches über CacheManager
  • Initialisierung der Fonts über FontEnvironments
  • Initialisierung der Thread-Pools
  • Initialisierung des TransportManagers
  • Registrierung der MessageHandler
  • Registrierung der Services für RPC
  • Erweitern des TileServlets für URL-Mapping
  • Registrierung der DocumentDataProvider
  • Registrierung der ServerOperations
  • Registrierung des Annotationsprofils

Beispiel

Das folgende Beispiel wurde aus der Showcase-Anwendung, welche Teil der Auslieferung ist, extrahiert.

WebtoolkitServletContextListener

@Override
protected void contextInitialized(ServletContextEvent sce, WebtoolkitServerContext context) {
// We do not use Spring here, so we have to wire that stuff by hand
JWTServerContext jwtServerContext = new JWTServerContext();
// calling getInstance() ensures that the JadicePreferenceStore is actually initialized instead
// of the default one
JadicePreferenceHolder.getInstance();
// after calling JadicePreferenceHolder.getInstance(), the jadice configuration preference store
// will be available.
final PreferenceStore ps = PreferenceStoreHolder.getPreferenceStoreByName(
JadicePreferenceHolder.JADICE_CONFIGURATION);
JadicePropertiesConfiguration.configure(ps);
CacheManager.setDefault(DefaultCacheProvider.getDefaultServerCache());
GitInfo.logCommitIdOnce();
FontEnvironments.initialize();

DocumentDataProviderRegistry documentDataProviderRegistry = new DocumentDataProviderRegistryImpl();
jwtServerContext.setDocumentDataProviderRegistry(documentDataProviderRegistry);
ServerConfiguration serverConfiguration = ConfigurationManager.getServerConfiguration();
final JWTThreadPoolExecutor threadPoolExecutor = new JWTThreadPoolExecutor(
serverConfiguration.getGeneralPoolCoreSize(), serverConfiguration.getGeneralPoolMaxSize(), 15);
jwtServerContext.setExecutorService(threadPoolExecutor);
AnnotationService annotationService = new AnnotationService();
jwtServerContext.setAnnotationService(annotationService);
TileRenderPriorityTaskExecutor tileRenderPriorityTaskExecutor = new TileRenderPriorityTaskExecutor();
TileRenderer tileRenderer = new TileRenderer(tileRenderPriorityTaskExecutor);
DocumentService documentService = new DocumentService(tileRenderer, threadPoolExecutor,
documentDataProviderRegistry, annotationService);
jwtServerContext.setDocumentService(documentService);
TileRenderService tileRenderService = new TileRenderService(documentService, tileRenderPriorityTaskExecutor, tileRenderer);
jwtServerContext.setTileRenderService(tileRenderService);
SynchronizeObjectDeserializer synchronizeObjectDeserializer = new SynchronizeObjectDeserializer(documentService);
TextService textService = new TextService(documentService, synchronizeObjectDeserializer);
jwtServerContext.setTextService(textService);
ServerOperationRegistry serverOperationRegistry = new ServerOperationRegistryImpl();
jwtServerContext.setServerOperationRegistry(serverOperationRegistry);
ServerOperationService serverOperationService = new ServerOperationService(synchronizeObjectDeserializer, serverOperationRegistry, threadPoolExecutor);
jwtServerContext.setServerOperationService(serverOperationService);

// Initialize the TransportManager
final TransportManager transportManager = TransportManagerProvider.getFromServletContext(sce.getServletContext());
final TransportManagerImpl transportManagerImpl = (TransportManagerImpl) transportManager;
transportManagerImpl.init(ConfigurationManager.getServerConfiguration().getNetworkConfiguration());

final ServiceCallRequestHandler serviceCallHandler = new ServiceCallRequestHandler(() -> threadPoolExecutor);

// register basic MessageHandler for Services
transportManagerImpl.getMessageHandlerManager().registerMessageHandler(ServiceCallRequest.class,
serviceCallHandler);
transportManagerImpl.getMessageHandlerManager().registerMessageHandler(CancelRequest.class,
new CancelHandler(serviceCallHandler));
transportManagerImpl.getMessageHandlerManager().registerMessageHandler(AuthenticationInfoUpdate.class,
new AuthenticationInfoUpdateHandler());

// register Services
serviceCallHandler.register("DocumentService", new DocumentServiceInvoker(documentService));
serviceCallHandler.register("AnnotationService", new AnnotationServiceInvoker(annotationService));
serviceCallHandler.register("TextService", new TextServiceInvoker(textService));
serviceCallHandler.register("ServerOperationService",
new ServerOperationServiceInvoker(serverOperationService));
serviceCallHandler.register("TileRenderService", new GenericInvoker(tileRenderService));

// Register DataProvider
documentDataProviderRegistry.registerProvider( //
ClassPathWithAnnoSource.class, //
ClassPathWithAnnoHandle.class, //
new ClassPathWithAnnoDocumentDataProvider() //
);

// Register Anno Profiles
serverOperationRegistry.register(SimpleServerOperationParameters.class, new SimpleServerOperation());

AnnotationProfile annotationProfile = AnnotationProfile.load(getClass().getResource("/annotationConfigurations/jwt-annotation-profile.xml"));
annotationService.registerProfile(annotationProfile);

// as we're dealing with a single profile only, it will be our default profile.
AnnotationProfile.setDefaultProfile(annotationProfile);

demoSystem = ShowcaseDemoSystem.create(sce.getServletContext(), jwtServerContext);
// Place JWTServerContext in ServletContext so it can be retrieved from Servlets, e.g. ShowcaseTileServlet
sce.getServletContext().setAttribute("JWT_SERVER_CONTEXT", jwtServerContext);
}
Anmerkung zu vorigen Methoden, einen DocumentDataProvider zu registrieren

In vorigen Versionen war es möglich, die DocumentDataProvider im Verzeichnis META-INF/services zu registrieren. Diese Möglichkeit entfällt. Stattdessen wird empfohlen, über den Mechanismus, wie im folgenden Beispiel ShowcaseDemosSystem aufgezeigt, zu gehen. Dort werden einige zueinander passende Trios Source, PageSegmentHandle und DocumentDataProvider registriert.

ShowcaseDemoSystem

/**
* <pre>
* Copyright (c), levigo holding gmbh.
*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
* </pre>
*/
package com.levigo.jadice.web.demo.showcase.server;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;

import com.levigo.jadice.annotation.profiles.AnnotationProfile;
import com.levigo.jadice.document.io.IOUtils;
import com.levigo.jadice.document.io.MemoryInputStream;
import com.levigo.jadice.document.io.SeekableInputStream;
import com.levigo.jadice.web.demo.common.server.DemoSystem;
import com.levigo.jadice.web.demo.common.server.dataprovider.ClassPathDocumentDataProvider;
import com.levigo.jadice.web.demo.common.server.dataprovider.ClassPathWithAnnoDocumentDataProvider;
import com.levigo.jadice.web.demo.common.server.dataprovider.ClassPathWithRenderControlsDocumentDataProvider;
import com.levigo.jadice.web.demo.common.server.dataprovider.SplitFileUploadDocumentDataProvider;
import com.levigo.jadice.web.demo.common.shared.service.sources.ClassPathHandle;
import com.levigo.jadice.web.demo.common.shared.service.sources.ClassPathSource;
import com.levigo.jadice.web.demo.common.shared.service.sources.ClassPathWithAnnoHandle;
import com.levigo.jadice.web.demo.common.shared.service.sources.ClassPathWithAnnoSource;
import com.levigo.jadice.web.demo.common.shared.service.sources.ClassPathWithRenderControlsHandle;
import com.levigo.jadice.web.demo.common.shared.service.sources.ClassPathWithRenderControlsSource;
import com.levigo.jadice.web.demo.showcase.shared.*;
import com.levigo.jadice.web.server.AnnotationProfileRegistry;
import com.levigo.jadice.web.server.DocumentDataProviderRegistry;
import com.levigo.jadice.web.server.WebtoolkitServerContext;
import com.levigo.jadice.web.server.annotation.AnnotationImageProvider;
import com.levigo.jadice.web.server.export.ExportRepository;

public class ShowcaseDemoSystem extends DemoSystem {

/**
* Retrieve the a {@link ShowcaseDemoSystem} instance associated with the given
* {@link ServletContext}. If no {@link ShowcaseDemoSystem} has been associated with the
* {@link ServletContext} this method will return {@code null}. If a {@link DemoSystem} instance
* other than a {@link ShowcaseDemoSystem} instance has been associated with the given
* {@link ServletContext}, an {@link IllegalStateException} will be thrown.
*
* @param context
* @return the ShowcaseDemoSystem
* @throws IllegalStateException if the {@link ServletContext} contains another type of
* {@link DemoSystem} or if no {@link DemoSystem} instance has been associated with the
* given {@link ServletContext}
* @throws IllegalArgumentException if context is null
*/
public static ShowcaseDemoSystem get(ServletContext context) {
if (context == null)
throw new IllegalArgumentException("context must not be null");

DemoSystem demoSystem = DemoSystem.get(context);

if (demoSystem instanceof ShowcaseDemoSystem)
return (ShowcaseDemoSystem) demoSystem;

throw new IllegalStateException(
"Incorrect type of " + DemoSystem.class.getSimpleName() + " has been started. Expected: "
+ ShowcaseDemoSystem.class.getName() + " received: " + demoSystem.getClass().getName());
}

/**
* Create a new {@link ShowcaseDemoSystem} instance and associate it with the given
* {@link ServletContext} . This method must be called within a
* {@link ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)} to ensure
* that the instance will be available before the {@link Servlet} implementations will be
* initialized.
*
* @param context
* @param webtoolkitServerContext
* @return a new {@link ShowcaseDemoSystem} instance associated with the given
* {@link ServletContext} and {@link WebtoolkitServerContext}
*/
public static ShowcaseDemoSystem create(ServletContext context, WebtoolkitServerContext webtoolkitServerContext) {
if (context == null)
throw new IllegalArgumentException("context must not be null");

ShowcaseDemoSystem system = new ShowcaseDemoSystem(webtoolkitServerContext, context);
DemoSystem.attach(context, system);
return system;
}


public ShowcaseDemoSystem(WebtoolkitServerContext webtoolkitServerContext, ServletContext servletContext) {
registerDocumentDataProvider(webtoolkitServerContext.getDocumentDataProviderRegistry());
registerServerOperation(webtoolkitServerContext.getServerOperationRegistry(), servletContext);
registerAnnotationProfiles(webtoolkitServerContext.getAnnotationProfileRegistry());
registerPrintingServerOperation(webtoolkitServerContext, servletContext);
}

private void registerPrintingServerOperation(WebtoolkitServerContext jwtServerContext,
ServletContext servletContext) {
// register stream printing server operation
jwtServerContext.getServerOperationRegistry().register(ExportParameters.class,
new ExportServerOperation(ExportRepository.get(servletContext)));
}

private void registerServerOperation(ServerOperationRegistry serverOperationRegistry, ServletContext servletContext) {
// register the ServerOperation instance using the corresponding parameters class as key
// the parameters class will be used to find the registered instance when invoking
serverOperationRegistry.register(SimpleServerOperationParameters.class, new SimpleServerOperation());

// register a ContextualFactory that creates the ServerOperation and injects the current
// HttpSession
serverOperationRegistry.register(HttpSessionServerOperationParameters.class,
new HttpSessionServerOperationFactory());

// register further ServerOperation instances
serverOperationRegistry.register(ConfirmedServerOperationParameters.class, new ConfirmedServerOperation());
serverOperationRegistry.register(PropertySerializationServerOperationParameters.class,
new PropertySerializationServerOperation());
serverOperationRegistry.register(ProgressiveServerOperationParameters.class, new ProgressiveServerOperation());
serverOperationRegistry.register(ComplexServerOperationParameters.class, new ComplexServerOperation());
serverOperationRegistry.register(BookmarkPersistencyServerOperationParameters.class,
new BookmarkPersistencyServerOperation());

serverOperationRegistry.register(DecorationPrintingServerOperationParameters.class,
new DecorationPrintingServerOperation(ExportRepository.get(servletContext)));
serverOperationRegistry.register(FilteredPrintingServerOperationParameters.class,
new FilteredPrintingServerOperation(ExportRepository.get(servletContext)));
}

protected void registerDocumentDataProvider(DocumentDataProviderRegistry documentDataProviderRegistry) {
documentDataProviderRegistry.registerProvider( //
ClassPathWithAnnoSource.class, //
ClassPathWithAnnoHandle.class, //
new ClassPathWithAnnoDocumentDataProvider() //
);

documentDataProviderRegistry.registerProvider( //
FilteredClassPathWithAnnoSource.class, //
FilteredClassPathWithAnnoHandle.class, //
new FilteredClassPathWithAnnoDocumentDataProvider() //
);

documentDataProviderRegistry.registerProvider( //
ClassPathWithRenderControlsSource.class, //
ClassPathWithRenderControlsHandle.class, //
new ClassPathWithRenderControlsDocumentDataProvider() //
);

documentDataProviderRegistry.registerProvider( //
SplitFileUploadSource.class, //
SplitFileUploadHandle.class, //
new SplitFileUploadDocumentDataProvider(getFileUploadservice()) //
);

documentDataProviderRegistry.registerProvider( //
DocumentWithPermissionSource.class, //
DocumentWithPermissionHandle.class, //
new DocumentWithPermissionsDataProvider() //
);

// register the simple data provider that reads a specific file from disk
documentDataProviderRegistry.registerProvider( //
MyDataProviderSource.class, //
MyDataProviderHandle.class, //
new MyDataProvider() //
);

documentDataProviderRegistry.registerProvider(//
HocrDataProviderSource.class, //
HocrDataProviderHandle.class, //
new DataProviderWithHocr() //
);

documentDataProviderRegistry.registerProvider(//
AuthenticationSource.class, //
AuthenticationHandle.class, //
new DataProviderFactoryWithAuthentication() //
);

documentDataProviderRegistry.registerProvider( //
LazyClassPathSource.class, //
LazyClassPathHandle.class, //
new LazyClassPathDocumentDataProvider() //
);

documentDataProviderRegistry.registerProvider( //
CompositePageSource.class, //
CompositePageHandle.class, //
new CompositePageDataProvider() //
);

documentDataProviderRegistry.registerProvider( //
ClassPathWithBookmarksSource.class, //
ClassPathWithBookmarksHandle.class, //
new ClassPathWithBookmarksDataProvider() //
);

documentDataProviderRegistry.registerProvider( //
FontSubstitutionSource.class, //
FontSubstitutionHandle.class, //
new FontSubstitutionDocumentDataProvider() //
);

documentDataProviderRegistry.registerProvider( //
ClassPathSource.class, //
ClassPathHandle.class, //
new ClassPathDocumentDataProvider() //
);

// create example file if not existing (save resource at server location)
try {
new File(MyDataProvider.BASEPATH).mkdirs();
File file = new File(MyDataProvider.BASEPATH + "testimage_full.jpg");
file.deleteOnExit();

IOUtils.copyAndClose(getClass().getClassLoader().getResourceAsStream("example-docs/testimage_full.jpg"),
new FileOutputStream(file));

} catch (Exception e) {
throw new RuntimeException("Failed to initialize showcase system", e);
}

}

protected void registerAnnotationProfiles(AnnotationProfileRegistry annotationProfileRegistry) {
// we would like to use annotations. In this case we have to load and register the desired
// annotation profile.
AnnotationProfile annotationProfile = registerProfile(annotationProfileRegistry,
"/annotationConfigurations/jwt-annotation-profile.xml");

// as we're dealing with a single profile only, it will be our default profile.
AnnotationProfile.setDefaultProfile(annotationProfile);

// in order to provide different images according to the defined image ids at the client side,
// we register an AnnotationImageProvider for the registered profile
annotationProfileRegistry.registerAnnotationImageProvider(annotationProfile.getName(),
new AnnotationImageProvider() {
@Override
public SeekableInputStream provideAnnotationImage(String annotationImageID) throws IOException {
InputStream is = null;
if (annotationImageID.equals("signatureAnnoImage"))
is = Thread.currentThread().getContextClassLoader().getResourceAsStream("signature_kl_opaque_dotted.png");
else if (annotationImageID.equals("qrJWTAnnoImage"))
is = Thread.currentThread().getContextClassLoader().getResourceAsStream("qr_jwt.png");
else if (annotationImageID.equals("qrJadiceAnnoImage"))
is = Thread.currentThread().getContextClassLoader().getResourceAsStream("qr_jadice.png");

if (null == is)
throw new IOException("Couldn't find/load image for id "" + annotationImageID + """);

return new MemoryInputStream(is);
}
});

registerProfile(annotationProfileRegistry, "/annotationConfigurations/cm7_profile.xml");
registerProfile(annotationProfileRegistry, "/annotationConfigurations/cm8_profile.xml");
registerProfile(annotationProfileRegistry, "/annotationConfigurations/filenet-p8_profile.xml");
registerProfile(annotationProfileRegistry, "/annotationConfigurations/filenetIS_profile.xml");
}

protected AnnotationProfile registerProfile(AnnotationProfileRegistry annotationProfileRegistry, String path) {
AnnotationProfile annotationProfile = AnnotationProfile.load(getClass().getResource(path));
if (annotationProfile == null)
throw new IllegalStateException("Required AnnotationProfile could not be loaded.");
annotationProfileRegistry.registerProfile(annotationProfile);
return annotationProfile;
}

@Override
protected void doShutdown() {
}
}

In einer Integration ohne Spring Boot und das dazugehörende DependencyInjection kann der JWTServerContext über den folgenden Mechanismus zur Verfügung gestellt werden.

MyTileServlet

@WebServlet(
asyncSupported = true,
description = "Servlet handling tile requests",
displayName = "jadice web toolkit tile download",
name = "jwtTileDownloadServlet",
urlPatterns = {
"/jwt/tile/*"
})
public class MyTileServlet extends TileServlet {

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
if (null == tileRenderService) {
final JWTServerContext jwtServerContext = (JWTServerContext) getFromServletContext(
config.getServletContext());
if (null == jwtServerContext)
throw new UnavailableException("No JWTServerContext found in context");

tileRenderService = jwtServerContext.getTileRenderService();
}
}

public WebtoolkitServerContext getFromServletContext(ServletContext servletContext) {
JWTServerContext serverContext = (JWTServerContext) servletContext.getAttribute("JWT_SERVER_CONTEXT");

if (serverContext == null) {
throw new NullPointerException("JWTServerContext was not placed in ServletContext");
}
return serverContext;
}
}