Проблемы с использованием автонастройки Springboot для подключения к SQL-серверу в среде FUSE EAP

Это, вероятно, неправильное представление с моей стороны, когда речь идет о работе с SpringBoot в средах FUSE EAP. Я пытался развернуть сервис, который я разработал в соответствии с документацией RedHat и архетипами/примерами, которые я нашел в Интернете, которые смешивают Camel и SpringBoot, но безрезультатно.

Насколько я понимаю, при создании соединения с источником данных JNDI, который был настроен и протестирован на сервере EAP Fuse, я могу использовать application.properties или application.yml, чтобы приложение spring автоматически настраивало соединение. В моем случае требуется, чтобы я использовал @PersistenceContext для вызова EntityManager, поскольку операции CRUD, которые расширяет JpaRepository, на самом деле не покрывают потребности.

Согласно документации RedHat, FUSE 7.2 был установлен в EAP 7.1, а POM использует версию org.jboss.redhat-fuse.fuse-springboot-bom 7.2.0.fuse-720020-redhat-00001.

Я пробовал использовать автоконфигурацию Spring, ручную настройку с объявлением класса @Configuration, ручную настройку с объявлением соединения с базой данных в файле camel-context.xml и некоторые другие второстепенные тесты.

Ошибки различаются в зависимости от того, пытаюсь ли я удалить .jar или .jar.original, сгенерированный с помощью плагина spring-boot-maven с целью выполнения переупаковки, ошибки, полученные до этого момента:

  • NullPointer, потому что EntityManager em имеет значение null (.jar.original)
  • java.lang.NoClassDefFoundError: org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder (.jar.original, когда есть ручная настройка источника данных, будь то в аннотированном классе Java @Configuration или в camel-context.xml с использованием Spring DSL)
  • java.lang.ClassNotFoundException: com.example.dao.genericDAOImpl (.jar со всеми упакованными зависимостями)

Вот фрагменты моей программы, которые включают POM, Application.java и компонент, который пытается получить EntityManager, будут рады предоставить дополнительные фрагменты, если этого недостаточно/неясно.

POM.xml

...
    <properties>
        <fuse.version>7.2.0.fuse-720020-redhat-00001</fuse.version>
        ...
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    </dependencies>
...
<build>
        <defaultGoal>spring-boot:run</defaultGoal>
        <plugins>
            ...
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.5.16.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
    </build>
...

приложение.свойства

spring.datasource.jndi-name=jdbc:sqlserver://ip:1433;DatabaseName=dbname
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.generate-ddl=false

Приложение.java

@ImportResource({"classpath:spring/camel-context.xml"})
@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

верблюд-context.xml

<beans ...>
...
    <camelContext id="identidades_financieras" xmlns="http://camel.apache.org/schema/spring">
        <onException>
            <exception>java.lang.Exception</exception>
            <handled>
                <constant>true</constant>
            </handled>
            <setHeader headerName="Exchange.HTTP_RESPONSE_CODE">
                <constant>500</constant>
            </setHeader>
            <setBody>
                <simple>${exception.message}</simple>
            </setBody>
        </onException>
        <restConfiguration apiContextPath="/openapi.json"
            bindingMode="json" component="undertow"
            contextPath="/restservice/api_v1" enableCORS="true">
            <dataFormatProperty key="prettyPrint" value="true"/>
        </restConfiguration>
        <rest enableCORS="true" id="rest-for-openapi-document" path="/openapi">
            <get id="openapi.json" produces="application/json" uri="openapi.json">
                <description>Gets the OpenAPI document for this service</description>
                <route id="route-for-openapi-document">
                    <setHeader headerName="Exchange.CONTENT_TYPE" id="setHeader-for-openapi-document">
                        <constant>application/vnd.oai.openapi+json</constant>
                    </setHeader>
                    <setBody id="setBody-for-openapi-document">
                        <constant>resource:classpath:openapi.json</constant>
                    </setBody>
                </route>
            </get>
        </rest>
        <rest bindingMode="auto" enableCORS="true"
            id="rest-b5d099c1-1996-458b-b5db-34aadc57a548" path="/">
            <get id="customPaginatexxxVO" produces="application/json" uri="/xxx">
                <to uri="direct:customPaginatexxxVO"/>
            </get>
...

        <route id="route-28f4489d-b354-401b-b774-6425bec1c120">
            <from id="from-17c4205f-8d28-4d3d-a265-cb1c38c9bc32" uri="direct:customPaginatexxxVO"/>
            <log id="customPaginatexxxVO-log-1" message="headers ====> pageSize: ${header.pageSize} - pageNumber: ${header.pageNumber}"/>
            <bean id="to-ee6565efaf-de46-4941-b119-be7aaa07d892"
                method="paginate" ref="genericService"/>
            <log id="customPaginatexxxVO-log-2" message="${body}"/>
        </route>
<beans/>

genericService.java

@Service
public class genericServiceImpl implements genericService {

    @Autowired
    private genericDAO dao;
    ...
    @Override
    public xxxVO paginate(Map<String, Object> reqHeaders) {
        ... pageProps are defined using reqHeaders ...
        xxxVO paginated = dao.customPagination(pageProps);
        return paginated;
    }
    ...
}

genericDAOImpl.java, который выдает ошибку при вызове чего-либо, касающегося em.

@Repository
public class genericDAOImpl implements genericDAO {

    @PersistenceContext //when manually configured, I've added the (unitName="") in reference to the persistence unit, from my understanding, since only one datasource was created, this should pick up by default
    private EntityManager em;
...
    @Override
    public xxxVO customPagination(paginateProps pageProps) {
        xxxVO result = null;
        try {
            CriteriaBuilder paginationBuilder = em.getCriteriaBuilder();
            CriteriaQuery<T> paginationQuery = paginationBuilder.createQuery(entity.class);
            Root<T> entityClass = paginationQuery.from(entity.class);
            paginationQuery.select(entityClass);
            ... some settings with pageProps ...
            TypedQuery<T> query = em.createQuery(paginationQuery);
            entityList = query.getResultList();
            ... entityList is transformed to xxxVO ...
        } catch (Exception e) {
            LOG.error("caught something");
            e.printStackTrace();
        }
        return result;
    }   
...

Как указывалось ранее, я получаю множество различных ошибок в зависимости от вариантов, которые я пробовал, и большинство из них явно сводятся к неправильной настройке или неправильному развертыванию, я все еще несколько неопытен, когда дело доходит до SpringBoot и Camel, и различные вещи, которые я читал в Интернете, создали некоторую путаницу. Просто чтобы убедиться, что метод разбивки на страницы, хотя и очень обрезанный, должен работать, если он имеет не обнуленный EntityManager.

Вот пара логов:

При развертывании .jar (толстый jar со всеми зависимостями), который из сделанных мной тестов правильно развертывается с помощью java -jar, но не в сервисе fuse eap

09:16:01,937 WARN  [org.springframework.context.support.GenericApplicationContext] (MSC service thread 1-3) Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.example.dao.genericDAOImpl] for bean with name 'genericDAO' defined in URL [vfs:/content/identidades_financieras-1.0-SNAPSHOT.jar/BOOT-INF/classes/spring/camel-context.xml]; nested exception is java.lang.ClassNotFoundException: com.example.dao.genericDAOImpl from [Module "deployment.identidades_financieras-1.0-SNAPSHOT.jar" from Service Module Loader]
09:16:01,940 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-3) MSC000001: Failed to start service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": org.jboss.msc.service.StartException in service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": Cannot create camel context: identidades_financieras-1.0-SNAPSHOT.jar
    at org.wildfly.extension.camel.service.CamelContextActivationService.start(CamelContextActivationService.java:71)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:2032)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1955)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.example.dao.genericDAOImpl] for bean with name 'genericDAO' defined in URL [vfs:/content/identidades_financieras-1.0-SNAPSHOT.jar/BOOT-INF/classes/spring/camel-context.xml]; nested exception is java.lang.ClassNotFoundException: com.example.dao.genericDAO from [Module "deployment.identidades_financieras-1.0-SNAPSHOT.jar" from Service Module Loader]
...

При развертывании .jar.original (в основном только java) с настроенным вручную DataSource и EntityManagerFactory. Насколько я понимаю, служба ожидает, что на сервере будут существовать зависимости org.springframework.boot. После проверки модулей в слое предохранителей нет модуля org.springframework.boot. Это предназначено?

09:50:17,265 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-8) MSC000001: Failed to start service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": org.jboss.msc.service.StartException in service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": Failed to start service
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1978)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.getDeclaredMethods(Class.java:1975)
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:613)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:524)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:510)
    at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:570)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:697)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:640)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:609)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1490)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:425)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:395)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525)
    at org.wildfly.extension.camel.SpringCamelContextBootstrap$1.run(SpringCamelContextBootstrap.java:90)
    at org.wildfly.extension.camel.proxy.ProxyUtils$1.invoke(ProxyUtils.java:51)
    at com.sun.proxy.$Proxy68.run(Unknown Source)
    at org.wildfly.extension.camel.proxy.ProxyUtils.invokeProxied(ProxyUtils.java:55)
    at org.wildfly.extension.camel.SpringCamelContextBootstrap.createSpringCamelContexts(SpringCamelContextBootstrap.java:87)
    at org.wildfly.extension.camel.service.CamelContextActivationService.start(CamelContextActivationService.java:58)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:2032)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1955)
    ... 3 more
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder from [Module "deployment.identidades_financieras-1.0-SNAPSHOT.jar" from Service Module Loader]
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:198)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:412)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:400)
    at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
    ... 27 more

Наконец, при загрузке .jar.original с использованием только автонастройки Spring EM имеет значение null, с помощью Postman я получаю статус 500 и «Нет ответа», когда я использую REST

java.lang.NullPointerException
at com.example.dao.genericDAOImpl.customPagination(GenericDAOImpl.java:252)

Строка ссылается на CriteriaBuilder paginationBuilder = em.getCriteriaBuilder() или любое другое место, где вызывается метод EM.

Спасибо за ваше время! Любой комментарий ценен...


person danks    schedule 25.06.2019    source источник


Ответы (2)


Нет поддержки Spring Boot с Fuse EAP и подсистемой Camel. Поэтому вы не видите никаких org.springframework.boot зависимостей на уровне модуля Fuse.

Если вы собираетесь развертывать приложения Camel Spring Boot в EAP, лучше всего либо отключить подсистему Camel для своего развертывания, либо полностью отказаться от установки подсистемы.

person James Netherton    schedule 26.06.2019
comment
Спасибо за ваш ответ. для меня это новость, тем более что в документации RedHat для Fuse 7.0 есть глава под названием: ГЛАВА 5. APACHE CAMEL IN SPRING BOOT, и они рекомендуют использовать спецификацию, называемую fabric8-project-bom-camel-spring-boot, который, насколько я понимаю, импортируется в спецификацию, которую я использовал для своего проекта. Тогда я буду осторожен с поддержкой. - person danks; 26.06.2019

Это ни в коем случае не решение проблемы, с которой я столкнулся, я считаю, что это всего лишь временное исправление моего кода, поскольку версия Fuse 7.4 предположительно будет поддерживать SpringBoot 2.1.x или что-то в этом роде, но разрешены следующие действия. мне создать соединение с базой данных и продолжить свою жизнь. Я не буду отмечать это как приемлемый ответ, если только мне не скажут, что это единственный способ.

В Application.java я прямо отключил SpringBootServletInitializer. Полное раскрытие, я прямо не представляю, какое влияние это может оказать на приложение, но зависимость беспокоила меня, когда я пытался развернуть.

@ImportResource({"classpath:spring/camel-context.xml"})
@SpringBootApplication
public class Application {//extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Я создал файл persistence.xml, в котором настроил имя единицы сохранения и выбрал пакет, содержащий объекты (или перечислил их, оба работали).

В camel-context.xml я объявил следующее перед тегом

    <bean class="org.apache.camel.component.jpa.JpaComponent" id="jpa">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="transactionManager" ref="jpaTxManager"/>
    </bean>
    <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="jpaTxManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <bean
        class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" id="entityManagerFactory">
        <property name="persistenceUnitName" value="PERSISTENCE UNIT NAME IN PERSISTENCE.XML"/>
    </bean>
    <bean class="org.apache.camel.spring.spi.SpringTransactionPolicy" id="requiredPolicy">
        <property name="transactionManager" ref="jpaTxManager"/>
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
    </bean>

Я создал java-класс, отвечающий за создание EntityManager, очень важно, чтобы класс был @Stateless (EJB) и чтобы подключение к блоку постоянства было статическим.

@Stateless
public class persistenceUnitEntityManagerImpl implements IfEntityManager{


    private static EntityManager em;

    static {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("PERSISTENCE UNIT NAME");
        em = entityManagerFactory.createEntityManager();
    }

    public void setEntityManager( EntityManager em ) {
        persistenceUnitEntityManagerImpl.em = em;
    }

    public EntityManager getEntityManager() {
        return persistenceUnitEntityManagerImpl.em;
    }
}

В bean-компоненты, где требовалось подключение к базе данных, в моем случае @Component (должен работать так же хорошо в @Repository), я добавил следующее:

    private IfEntityManagerImpl IfEntityManager;

    @PostConstruct
    public void init() {
         this.persistenceUnitEntityManagerImpl = new persistenceUnitEntityManagerImpl();
     }

И всякий раз, когда нужно вызвать EntityManager, я могу использовать persistenceUnitEntityManagerImpl.getEntityManager()

Просто чтобы убедиться, что компонент не создает новое соединение/менеджер сущностей/что-то еще, вы можете добавить LOG в инициализацию @PostConstruct, если ваш bean-компонент является синглтоном (я полагаю, что он должен быть по умолчанию), вы никогда не получите этот журнал или строку печати.

person danks    schedule 26.06.2019