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, Message-Listener,
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 derwebtoolkit-spring-boot-startermit. Sie aktiviert den Component-Scan über das Packagecom.levigo.jadice.webund sorgt im Hintergrund für ein Autowiring von DocumentDataProvidern, Message-Listenern 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 Message-Listenern
Auch Message-Listener lassen sich als @Component injizieren. Der
Nachrichtenname wird der Annotation @MessageName entnommen, eine
manuelle Registrierung über JWTServerContext#registerMessageListeners(List)
lt. Message-Listener entfällt dann.
@Component
@MessageName("CUSTOM")
public class CustomMessageListener implements MessageListener<CustomRequestDTO> {
...
}
Zu beachten ist, dass pro Nachrichtenname nur ein Listener registriert
werden kann. Soll ein vom jadice web toolkit mitgelieferter Listener
angepasst werden — etwa der
ExportOperationMessageListener — muss dessen Bean ersetzt und nicht
zusätzlich registriert werden, da die zweite Registrierung desselben
Nachrichtennamens mit einer IllegalArgumentException abgelehnt wird.
Automatische Registrierung von ContextualFactories
Genauso können auch ContextualFactories als @Component injiziert
werden, ohne dass diese programmatisch registriert werden müssen. Sie
werden für die kontextabhängige Erzeugung von DocumentDataProvidern
verwendet:
@Component
public class MyContextualDocumentDataProviderFactory
implements ContextualFactory<DocumentDataProvider<ClassPathSource, ClassPathHandle>> {
@Override
public DocumentDataProvider<ClassPathSource, ClassPathHandle> create(InvocationContext context) {
return new ClassPathDocumentDataProvider();
}
}
@Component
public class MyHttpSessionAwareFactory
implements ContextualFactory<DocumentDataProvider<MySource, MyHandle>> {
@Override
public DocumentDataProvider<MySource, MyHandle> create(InvocationContext context) {
ServletInvocationContext servletInvocationContext = (ServletInvocationContext) context;
return new MyDocumentDataProvider(servletInvocationContext.getSession().getId());
}
}
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
@ComponentScanerfasst 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="https://jakarta.ee/xml/ns/jakartaee"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.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.
Dieser ist inzwischen als @Deprecated markiert. Eine lauffähige Referenz existiert dennoch
weiterhin: Der Showcase, online erreichbar unter
https://webtoolkit.jadice.com/showcase/, wird
bewusst ohne Spring Boot betrieben und als klassisches WAR auf einem eigenständigen Tomcat
ausgeliefert. Zu beachten ist, dass auch in diesem Fall die Spring-Bibliotheken im Classpath
verbleiben müssen, da der JWTServerContext selbst auf Spring-Typen aufsetzt. Verzichtet wird
lediglich auf Spring Boot und die Dependency Injection.
Da ohne Dependency Injection 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 imServletContextdes Application Servers abgelegt werden, damit auf jadice Services, wie zum Beispiel denTileRenderService, 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 Message-Listener über
JWTServerContext#registerMessageListeners(List)(siehe Message-Listener); alternativ direkte Registrierung einzelner Listener überServerMessageManager.get().registerMessage(name, descriptor), ohne den Umweg über die@MessageName-Auswertung - Registrierung der mitgelieferten Standardnachrichten über
ServerMessageManager.get().registerDefaultMessages()— hierüber wird u.a. das Abbrechen laufender Konversationen registriert - Registrierung der Mappings für eigene
Source- undPageSegmentHandle-Typen anSourceMapperundPageSegmentHandleMapper - Erweitern des
TileServletsfür URL-Mapping - Registrierung der
DocumentDataProvider - Registrierung des Annotationsprofils
Beispiel
Das folgende Beispiel zeigt eine solche manuelle Initialisierung in vereinfachter Form. Es dient als Orientierung und ist an die jeweilige Integration anzupassen.
WebtoolkitServletContextListener
@Override
protected void contextInitialized(ServletContextEvent sce, WebtoolkitServerContext context) {
JWTServerContext jwtServerContext = new JWTServerContext();
JadicePreferenceHolder.getInstance();
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();
ThreadFactory threadFactory = new ThreadFactory() {
final ThreadGroup threadGroup = new ThreadGroup("JadiceWebToolkit General Threadpool");
final AtomicInteger counter = new AtomicInteger();
public Thread newThread(final Runnable r) {
Thread t = new Thread(this.threadGroup, r, this.createThreadName(this.counter.incrementAndGet()));
t.setPriority(5);
return t;
}
private String createThreadName(final int threadNumber) {
return "JadiceWebToolkit General Worker-" + threadNumber;
}
};
final JWTThreadPoolExecutor threadPoolExecutor = new JWTThreadPoolExecutor(
serverConfiguration.getGeneralPoolCoreSize(),
serverConfiguration.getGeneralPoolMaxSize(),
15,
TimeUnit.SECONDS,
threadFactory
);
jwtServerContext.setExecutorService(threadPoolExecutor);
AnnotationService annotationService = new AnnotationService();
jwtServerContext.setAnnotationService(annotationService);
TileRenderPriorityTaskExecutor tileRenderPriorityTaskExecutor = new TileRenderPriorityTaskExecutor(threadFactory);
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);
DocumentSnapshotConverter documentSnapshotConverter = new DocumentSnapshotConverter(documentService);
TextService textService = new TextService(threadPoolExecutor, documentService, documentSnapshotConverter);
jwtServerContext.setTextService(textService);
// Initialize the TransportManager
final TransportManager transportManager = TransportManagerProvider.getFromServletContext(sce.getServletContext());
final TransportManagerImpl transportManagerImpl = (TransportManagerImpl) transportManager;
transportManagerImpl.init(ConfigurationManager.getServerConfiguration().getNetworkConfiguration());
// Optional: Export
ExportRepository exportRepository = ExportRepository.get(sce.getServletContext());
ExportOperationMessageListener exportOperationMessageListener = new ExportOperationMessageListener();
exportOperationMessageListener.setRepository(exportRepository);
exportOperationMessageListener.setExecutorService(threadPoolExecutor);
exportOperationMessageListener.setSnapshotConverter(documentSnapshotConverter);
// Register MessageListeners
List<MessageListener<?>> messageListeners = new ArrayList<>();
CancelTileServletMessageListener cancelTileServletMessageListener = new CancelTileServletMessageListener();
cancelTileServletMessageListener.setTileRenderService(tileRenderService);
messageListeners.add(cancelTileServletMessageListener);
CreateDocumentMessageListener createDocumentMessageListener = new CreateDocumentMessageListener();
createDocumentMessageListener.setDocumentService(documentService);
messageListeners.add(createDocumentMessageListener);
GetAnnotationProfileMessageListener getAnnotationProfileMessageListener = new GetAnnotationProfileMessageListener();
getAnnotationProfileMessageListener.setAnnotationService(annotationService);
messageListeners.add(getAnnotationProfileMessageListener);
GetInstructionsMessageListener getInstructionsMessageListener = new GetInstructionsMessageListener();
getInstructionsMessageListener.setExecutorService(threadPoolExecutor);
getInstructionsMessageListener.setSnapshotConverter(documentSnapshotConverter);
messageListeners.add(getInstructionsMessageListener);
GetTextContentMessageListener getTextContentMessageListener = new GetTextContentMessageListener();
getTextContentMessageListener.setTextService(textService);
getTextContentMessageListener.setExecutorService(threadPoolExecutor);
messageListeners.add(getTextContentMessageListener);
RemoteLoggingMessageListener remoteLoggingMessageListener = new RemoteLoggingMessageListener();
remoteLoggingMessageListener.setExecutorService(threadPoolExecutor);
messageListeners.add(remoteLoggingMessageListener);
TextSearchRequestMessageListener textSearchRequestMessageListener = new TextSearchRequestMessageListener();
textSearchRequestMessageListener.setTextService(textService);
messageListeners.add(textSearchRequestMessageListener);
messageListeners.add(exportOperationMessageListener);
// Attention: registerMessageListeners() resets the ServerMessageManager. Therefore it has
// to be called before registerDefaultMessages() and before any registerMessage() call.
jwtServerContext.registerMessageListeners(messageListeners);
// Register the default messages shipped with the jadice web toolkit
ServerMessageManager.get().registerDefaultMessages();
// Register Source-/PageSegmentHandle-Mappings for own types
final ObjectMapper objectMapper = new ObjectMapper();
SourceMapper.get().registerDirectMapping(ClassPathWithAnnoSource.TYPE, ClassPathWithAnnoSource.class);
PageSegmentHandleMapper.get().registerMapping(ClassPathWithAnnoSource.TYPE, ClassPathWithAnnoHandle.class,
(handle) -> objectMapper.valueToTree(ClassPathWithAnnoParamsDTO.from((ClassPathWithAnnoHandle) handle)),
(json) -> ClassPathWithAnnoParamsDTO.toHandle(objectMapper.treeToValue(json, ClassPathWithAnnoParamsDTO.class)));
// Register DataProvider
documentDataProviderRegistry.registerProvider(ClassPathWithAnnoSource.class, //
ClassPathWithAnnoHandle.class, //
new ClassPathWithAnnoDocumentDataProvider() //
);
// Register Anno Profiles
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);
// Place JWTServerContext in ServletContext so it can be retrieved from Servlets, e.g. a custom TileServlet
sce.getServletContext().setAttribute("JWT_SERVER_CONTEXT", jwtServerContext);
}
Die Liste der Message-Listener muss alle Funktionen abdecken, die in der Anwendung
genutzt werden. Bei einer Spring-Boot-Integration werden dagegen alle
MessageListener-Beans automatisch registriert — darunter auch Listener, die im
Beispiel oben nicht enthalten sind, etwa der AuthenticationInfoUpdateHandler
(Nachricht AUTHENTICATION_INFO) und der AdditionalHTTPHeaderHandler
(Nachricht ADDITIONAL_HTTP_HEADERS).
Ohne Spring werden die Nachrichten nicht automatisch registriert. Zu beachten ist dabei
die Reihenfolge: registerMessageListeners(List) ruft intern ServerMessageManager#reset()
auf und verwirft damit alle bereits registrierten Nachrichten. Zuerst also
registerMessageListeners(List) aufrufen, danach registerDefaultMessages() sowie
eigene Registrierungen über ServerMessageManager.get().registerMessage(name, descriptor).
Eine zweite Registrierung desselben Nachrichtennamens wird mit einer
IllegalArgumentException abgelehnt.
Die DocumentDataProvider werden — wie im Beispiel oben — an der
DocumentDataProviderRegistry registriert; die Registrierung selbst unterscheidet sich
nicht zwischen den Umgebungen (siehe
DocumentDataProviderRegistry).
Zusätzlich sind für jeden eigenen Source- bzw. PageSegmentHandle-Typ die Mappings an
SourceMapper und PageSegmentHandleMapper zu registrieren, da diese nicht automatisch
ermittelt werden (siehe
Laden von Dokumenten, Abschnitt „Laden über Source/Handle").
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;
}
}