refactor(distro-tomcat): rework Tomcat distro for new features and improvements
MODIFY
distro/tomcat/pom.xml
distro/tomcat/pom.xml
<groupId>io.apiman</groupId>
<artifactId>apiman-distro-data</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.apiman</groupId>
+ <artifactId>apiman-distro-tools</artifactId> <!-- Tools for migration, etc. -->
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
<!-- log4j2 bits -->
<dependency>
<groupId>io.apiman</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
- <groupId>io.apiman</groupId>
- <artifactId>apiman-distro-tools</artifactId>
- <version>${project.version}</version>
- <type>zip</type>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
</dependency>
+ <!-- H2 db driver for quickstart -->
</dependencies>
<build>

MODIFY
distro/tomcat/src/main/assembly/overlay.xml
distro/tomcat/src/main/assembly/overlay.xml
</includes>
</unpackOptions>
</dependencySet>
+ <!-- Default H2 DB driver -->
+ <dependencySet>
+ <outputDirectory>lib</outputDirectory>
+ <includes>
+ <include>com.h2database:h2:jar</include>
+ </includes>
+ </dependencySet>
</dependencySets>
<fileSets>
<fileSet>

MODIFY
distro/tomcat/src/main/resources/overlay/conf/apiman.properties
distro/tomcat/src/main/resources/overlay/conf/apiman.properties
@@ -39,9 +39,9 @@ apiman.es.allowAnyHost=
# ---------------------------------------------------------------------
# Some hibernate settings only useful when JPA is the storage.type.
# ---------------------------------------------------------------------
-#apiman.hibernate.connection.datasource=java:/apiman/datasources/apiman-manager
-#apiman.hibernate.dialect=io.apiman.manager.api.jpa.ApimanH2Dialect
-#apiman.hibernate.hbm2ddl.auto=validate
+apiman.hibernate.connection.datasource=java:comp/env/apiman/datasources/apiman-manager
+apiman.hibernate.dialect=io.apiman.manager.api.jpa.ApimanH2Dialect
+apiman.hibernate.hbm2ddl.auto=validate
# How the API Manager UI authenticates to the API Manager REST services
apiman-manager-ui.api.authentication.type=authToken
@@ -61,25 +61,8 @@ apiman-manager-ui.org-create-admin-only=false
apiman-manager.config.features.rest-response-should-contain-stacktraces=false
# API Manager storage settings.
-#apiman-manager.storage.type=jpa
-#apiman-manager.storage.jpa.initialize=true
-
-apiman-manager.storage.type=es
-apiman-manager.storage.es.client.type=${apiman.es.client.type}
-apiman-manager.storage.es.client.protocol=${apiman.es.protocol}
-apiman-manager.storage.es.client.host=${apiman.es.host}
-apiman-manager.storage.es.client.port=${apiman.es.port}
-apiman-manager.storage.es.client.username=${apiman.es.username}
-apiman-manager.storage.es.client.password=${apiman.es.password}
-apiman-manager.storage.es.client.timeout=${apiman.es.timeout}
-apiman-manager.storage.es.client.pollingTime=${apiman.es.pollingTime}
-apiman-manager.storage.es.client.initialize=${apiman.es.initialize}
-apiman-manager.storage.es.client.keystore.path=${apiman.es.keystore.path}
-apiman-manager.storage.es.client.keystore.password=${apiman.es.keystore.password}
-apiman-manager.storage.es.client.truststore.path=${apiman.es.truststore.path}
-apiman-manager.storage.es.client.truststore.password=${apiman.es.truststore.password}
-apiman-manager.storage.es.client.allowSelfSigned=${apiman.es.allowSelfSigned}
-apiman-manager.storage.es.client.allowAnyHost=${apiman.es.allowAnyHost}
+apiman-manager.storage.type=jpa
+apiman-manager.storage.jpa.initialize=true
# API Manager metrics settings.
#apiman-manager.metrics.type=io.apiman.manager.api.jdbc.JdbcMetricsAccessor

ADD
distro/tomcat/src/main/resources/overlay/conf/context.xml
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!-- The contents of this file will be loaded for each web application -->
+<Context>
+
+ <!-- Default set of monitored resources. If one of these changes, the -->
+ <!-- web application will be reloaded. -->
+ <WatchedResource>WEB-INF/web.xml</WatchedResource>
+ <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
+ <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
+
+ <ResourceLink name="apiman/datasources/apiman-manager"
+ global="apiman/datasources/apiman-manager"
+ auth="Container"
+ type="javax.sql.DataSource" />
+
+ <!-- Uncomment this to disable session persistence across Tomcat restarts -->
+ <!--
+ <Manager pathname="" />
+ -->
+
+</Context>

MODIFY
distro/tomcat/src/main/resources/overlay/conf/server.xml
distro/tomcat/src/main/resources/overlay/conf/server.xml
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
+
+ <Resource
+ name="apiman/datasources/apiman-manager"
+ url="jdbc:h2:file:${catalina.base}/data/apiman/apiman-manager-h2"
+ auth="Container"
+ type="javax.sql.DataSource"
+ driverClassName="org.h2.Driver"
+ maxTotal="100"
+ maxIdle="30"
+ maxWaitMillis="10000"/>
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share

MODIFY
manager/api/jpa/src/main/java/io/apiman/manager/api/jpa/AbstractJpaStorage.java
manager/api/jpa/src/main/java/io/apiman/manager/api/jpa/AbstractJpaStorage.java
@@ -23,6 +23,7 @@ import io.apiman.manager.api.beans.search.SearchCriteriaBean;
import io.apiman.manager.api.beans.search.SearchCriteriaFilterBean;
import io.apiman.manager.api.beans.search.SearchCriteriaFilterOperator;
import io.apiman.manager.api.beans.search.SearchResultsBean;
+import io.apiman.manager.api.core.config.ApiManagerConfig;
import io.apiman.manager.api.core.exceptions.StorageException;
import java.util.ArrayList;
@@ -58,6 +59,9 @@ public abstract class AbstractJpaStorage {
@Inject
private EntityManagerFactoryAccessor emf;
+ @Inject
+ private ApiManagerConfig config;
+
/**
* Constructor.
*/
@@ -65,7 +69,7 @@ public abstract class AbstractJpaStorage {
}
protected Jdbi getJdbi() {
- return Jdbi.create(lookupDS("java:/apiman/datasources/apiman-manager"));
+ return Jdbi.create(lookupDS(config.getHibernateDataSource()));
}
/**

MODIFY
manager/api/jpa/src/main/java/io/apiman/manager/api/jpa/EntityManagerFactoryAccessor.java
manager/api/jpa/src/main/java/io/apiman/manager/api/jpa/EntityManagerFactoryAccessor.java
@@ -76,7 +76,7 @@ public class EntityManagerFactoryAccessor implements IEntityManagerFactoryAccess
properties.put("hibernate.hbm2ddl.auto", autoValue); //$NON-NLS-1$
properties.put("hibernate.dialect", dialect); //$NON-NLS-1$
properties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT");
- properties.put("hibernate.transaction.jta.platform", "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform");
+ // properties.put("hibernate.transaction.jta.platform", "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform");
// First try using standard JPA to load the persistence unit. If that fails, then
// try using hibernate directly in a couple ways (depends on hibernate version and

MODIFY
manager/api/jpa/src/main/java/io/apiman/manager/api/jpa/JpaStorageInitializer.java
manager/api/jpa/src/main/java/io/apiman/manager/api/jpa/JpaStorageInitializer.java
@@ -70,7 +70,7 @@ public class JpaStorageInitializer {
DB_TYPE_MAP.put(ApimanOracle12Dialect.class.getName(), "oracle12"); //$NON-NLS-1$
DB_TYPE_MAP.put(Oracle12cDialect.class.getName(), "oracle12"); //$NON-NLS-1$
DB_TYPE_MAP.put(Oracle10gDialect.class.getName(), "oracle12"); //$NON-NLS-1$
-
+ // TODO: update these mappings
DB_TYPE_MAP.put(ApimanPostgreSQLDialect.class.getName(), "postgresql9"); //$NON-NLS-1$
DB_TYPE_MAP.put(PostgreSQL9Dialect.class.getName(), "postgresql9"); //$NON-NLS-1$
DB_TYPE_MAP.put(PostgreSQL82Dialect.class.getName(), "postgresql9"); //$NON-NLS-1$
@@ -91,7 +91,7 @@ public class JpaStorageInitializer {
if (dsJndiLocation == null) {
throw new RuntimeException("Missing datasource JNDI location from JPA storage configuration."); //$NON-NLS-1$
}
- ds = lookupDS("java:/apiman/datasources/apiman-manager");
+ ds = lookupDS(dsJndiLocation);
dbType = DB_TYPE_MAP.get(hibernateDialect);
if (dbType == null) {
throw new RuntimeException("Unknown hibernate dialect configured: " + hibernateDialect); //$NON-NLS-1$

ADD
manager/api/war/tomcat8/src/main/java/io/apiman/manager/api/war/tomcat8/ApimanTransactionalInterceptor.java
+package io.apiman.manager.api.war.tomcat8;
+
+import io.apiman.manager.api.core.exceptions.StorageException;
+import io.apiman.manager.api.jpa.EntityManagerFactoryAccessor;
+import io.apiman.manager.api.rest.exceptions.SystemErrorException;
+import io.apiman.manager.api.rest.impl.util.DataAccessUtilMixin;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.transaction.Transactional;
+
+/**
+ *
+ */
+@Priority(Interceptor.Priority.PLATFORM_BEFORE)
+@Interceptor
+@Transactional
+public class ApimanTransactionalInterceptor implements DataAccessUtilMixin {
+
+ @Inject
+ EntityManagerFactoryAccessor emf;
+ private EntityManager em;
+
+ public ApimanTransactionalInterceptor() {
+ System.err.println("Apiman transactional interceptor is running");
+ }
+
+ @AroundInvoke
+ public Object intercept(InvocationContext ic) throws Exception {
+ em = emf.getEntityManager();
+ if (!em.getTransaction().isActive()) {
+ return invokeInOurTx(ic);
+ } else {
+ return invokeInCallerTx(ic, em.getTransaction());
+ }
+ }
+
+ private Object invokeInOurTx(InvocationContext ic) throws Exception {
+ EntityTransaction tx = em.getTransaction();
+ tx.begin();
+ try {
+ return ic.proceed();
+ } catch (Exception e) {
+ handleException(e, tx);
+ } finally {
+ endTransaction(tx);
+ }
+ throw new RuntimeException("UNREACHABLE");
+ }
+
+ private void endTransaction(EntityTransaction tx) throws Exception {
+ try {
+ if (!tx.getRollbackOnly()) {
+ if (tx.isActive()) {
+ tx.commit();
+ } else {
+ System.err.println("Someone else closed the TX?");
+ }
+ } else {
+ tx.rollback();
+ }
+ } catch (Exception e) {
+ handleException(e, tx);
+ } finally {
+ em.close();
+ }
+ }
+
+ private void handleException(Exception e, EntityTransaction tx) throws Exception {
+ e.printStackTrace();
+ if (e instanceof RuntimeException || e instanceof StorageException) {
+ tx.setRollbackOnly();
+ throw e;
+ }
+ throw new SystemErrorException(e);
+ }
+
+
+ private Object invokeInCallerTx(InvocationContext ic, EntityTransaction tx) throws Exception {
+ try {
+ return ic.proceed();
+ } catch (Exception e) {
+ handleException(e, tx);
+ }
+ throw new RuntimeException("UNREACHABLE");
+ }
+}

ADD
manager/api/war/tomcat8/src/main/resources/META-INF/persistence.xml
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
+
+ <persistence-unit name="apiman-manager-api-jpa" transaction-type="RESOURCE_LOCAL">
+ <non-jta-data-source>java:/apiman/datasources/apiman-manager</non-jta-data-source>
+ <class>io.apiman.manager.api.beans.idm.UserBean</class>
+ <class>io.apiman.manager.api.beans.idm.RoleBean</class>
+ <class>io.apiman.manager.api.beans.idm.RoleMembershipBean</class>
+ <class>io.apiman.manager.api.beans.orgs.OrganizationBean</class>
+ <class>io.apiman.manager.api.beans.clients.ClientBean</class>
+ <class>io.apiman.manager.api.beans.clients.ClientVersionBean</class>
+ <class>io.apiman.manager.api.beans.apis.ApiBean</class>
+ <class>io.apiman.manager.api.beans.apis.ApiVersionBean</class>
+ <class>io.apiman.manager.api.beans.apis.ApiDefinitionBean</class>
+ <class>io.apiman.manager.api.beans.apis.ApiPlanBean</class>
+ <class>io.apiman.manager.api.beans.plans.PlanBean</class>
+ <class>io.apiman.manager.api.beans.plans.PlanVersionBean</class>
+ <class>io.apiman.manager.api.beans.contracts.ContractBean</class>
+ <class>io.apiman.manager.api.beans.policies.PolicyDefinitionBean</class>
+ <class>io.apiman.manager.api.beans.policies.PolicyDefinitionTemplateBean</class>
+ <class>io.apiman.manager.api.beans.policies.PolicyBean</class>
+ <class>io.apiman.manager.api.beans.audit.AuditEntryBean</class>
+ <class>io.apiman.manager.api.beans.gateways.GatewayBean</class>
+ <class>io.apiman.manager.api.beans.plugins.PluginBean</class>
+ <class>io.apiman.manager.api.beans.download.DownloadBean</class>
+ <class>io.apiman.manager.api.beans.system.MetadataBean</class>
+ <class>io.apiman.manager.api.beans.developers.DeveloperBean</class>
+ <class>io.apiman.manager.api.beans.developers.DeveloperMappingBean</class>
+ <class>io.apiman.manager.api.jpa.blobstore.BlobEntity</class>
+ <class>io.apiman.manager.api.beans.notifications.NotificationPreferenceEntity</class>
+ <class>io.apiman.manager.api.beans.notifications.NotificationTypeEntity</class>
+ <class>io.apiman.manager.api.beans.notifications.NotificationEntity</class>
+ <class>io.apiman.manager.api.beans.apis.KeyValueTag</class>
+ <!-- <exclude-unlisted-classes>true</exclude-unlisted-classes>-->
+ <properties>
+ <property name="hibernate.show_sql" value="false" />
+ <property name="hibernate.id.new_generator_mappings" value="true" />
+ <property name="hibernate.order_updates" value="true"/>
+ <!-- TODO(msavy): Can we safely add batch optimiations here too? -->
+ <property name="hibernate.connection.autocommit" value="false"/>
+<!-- <property name="hibernate.format_sql" value="true"/>-->
+ </properties>
+ </persistence-unit>
+
+</persistence>

RENAME
manager/api/jpa/src/main/resources/META-INF/persistence.xml
manager/api/war/wildfly8/src/main/resources/META-INF/persistence.xml
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.id.new_generator_mappings" value="true" />
<property name="hibernate.order_updates" value="true"/>
+ <!-- TODO(msavy): Can we safely add batch optimiations here too? -->
<property name="hibernate.connection.autocommit" value="false"/>
- <property name="hibernate.format_sql" value="true"/>
-<!-- <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>-->
-
- <!-- Don't allow WF/EAP to manage the persistence unit - vital since we're dynamically setting most of the
- properties *after* WF/EAP has started! -->
-<!-- <property name="jboss.as.jpa.managed" value="true"/>-->
-
+<!-- <property name="hibernate.format_sql" value="true"/>-->
</properties>
</persistence-unit>

MODIFY
parent/pom.xml
parent/pom.xml
<!-- Dependency Versions -->
<version.com.fasterxml.jackson>2.12.4</version.com.fasterxml.jackson>
- <version.com.h2database>1.4.200</version.com.h2database>
+ <version.com.h2database>1.4.199</version.com.h2database> <!-- Unclear why, but 1.4.200 seems to cause some random issues, including inability for DB viewers to parse the data -->
<version.com.lmax>3.4.4</version.com.lmax>
<version.com.github.seancfoley.ipaddress>5.3.3</version.com.github.seancfoley.ipaddress>
<version.com.squareup.okhttp>2.7.5</version.com.squareup.okhttp>

refactor(distro-tomcat): rework Tomcat distro for new features and improvements