Error testing with Spring Cloud Stream Test - spring

We are using spring-cloud-stream to manage messages between our applications.
We have custom bindings:
public interface InboundChannels {
String TASKS = "domainTasksInboundChannel";
String EVENTS = "eventsInboundChannel";
#Input(TASKS)
SubscribableChannel tasks();
#Input(EVENTS)
SubscribableChannel events();
}
public interface OutboundChannels {
String TASKS = "domainTasksOutboundChannel";
String EVENTS = "eventsOutboundChannel";
#Output(TASKS)
MessageChannel tasks();
#Output(EVENTS)
MessageChannel events();
}
There are processors that consumes tasks and generate events:
#EnableBinding({InboundChannels.class, OutboundChannels.class})
public class TasksProcessor {
public TasksProcessor(
UserService userService,
#Qualifier(OutboundChannels.EVENTS) MessageChannel eventsChannel
) {
this.userService = userService;
this.eventsChannel = eventsChannel;
}
#StreamListener(value = TASKS, condition = "headers['" + TYPE + "']=='" + CREATE_USER + "'")
public void createUser(Message<User> message) {
final User user = message.getPayload();
userService.save(user)
.subscribe(created -> {
Message<User> successMessage = fromMessage(message, Events.USER_CREATED, created).build();
eventsChannel.send(successMessage);
});
}
}
Now we wanted to test it using spring-cloud-stream-test-support and its amazing features:
#DirtiesContext
#SpringBootTest
#RunWith(SpringRunner.class)
public class TasksProcessorTest {
private User user;
#Autowired
private InboundChannels inboundChannels;
#Autowired
private OutboundChannels outboundChannels;
#Autowired
private MessageCollector collector;
#Before
public void setup() {
user = new User(BigInteger.ONE, "test#teste.com");
}
#Test
public void createUserTest() {
final Message<User> msg = create(CREATE_USER, user).build();
outboundChannels.tasks().send(msg);
final Message<?> incomingEvent = collector.forChannel(inboundChannels.events()).poll();
final String type = (String) incomingEvent.getHeaders().get(TYPE);
assertThat(type).isEqualToIgnoringCase(USER_CREATED);
}
}
application.properties
##
# Spring AMQP configuration
##
spring.rabbitmq.host=rabbitmq
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
# Events channels
spring.cloud.stream.bindings.eventsOutboundChannel.destination=events
spring.cloud.stream.bindings.eventsInboundChannel.destination=events
spring.cloud.stream.bindings.domainTasksOutboundChannel.destination=domainTasks
spring.cloud.stream.bindings.domainTasksInboundChannel.destination=domainTasks
spring.cloud.stream.bindings.userTasksInboundChannel.group=domainServiceInstances
spring.cloud.stream.bindings.eventsInboundChannel.group=domainServiceInstances
But then we get this error:
java.lang.IllegalArgumentException: Channel [eventsInboundChannel] was not bound by class org.springframework.cloud.stream.test.binder.TestSupportBinder
What are we doing wrong?

In the .subscribe() you do eventsChannel.send(successMessage);, where that eventsChannel is from the OutboundChannels.EVENTS, but what you try to do in the test-case is like inboundChannels.events(). And it doesn't look like you really bind this channel anywhere.
I'm sure if you would use outboundChannels.events() instead, that would work for you.

Related

Entity listener can inject other Spring dependencies but not repository

I have this entity listener class:
#Component
public class AssignmentListener {
private KafkaService kafkaService;
private String topic;
private AssignmentMapper assignmentMapper;
private AttachmentRepository attachmentRepository;
#Autowired
public final void setKafkaService(KafkaService kafkaService) {
this.kafkaService = kafkaService;
}
#Autowired
public final void setTopic(
#Value("${topic}") String topic
) {
this.topic = topic;
}
#Autowired
public final void setAssignmentMapper(AssignmentMapper assignmentMapper) {
this.assignmentMapper = assignmentMapper;
}
#Autowired
public final void setAttachmentRepository(AttachmentRepository attachmentRepository) {
this.attachmentRepository = attachmentRepository;
}
#PostPersist
#PostUpdate
#Transactional("transactionManager")
#TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void postUpdate(Assignment assignment) {
var attachments = attachmentRepository.findAllByAssignmentId(assignment.getId());
var dto = assignmentMapper.mapToKafkaMessage(assignment);
dto.setAttachments(
attachments.stream()
.map(Attachment::getPath)
.collect(Collectors.toSet())
);
kafkaService.sendMessage(
topic,
dto
);
}
}
and it worked normally until adding this last field which is repository. All other dependencies were injected however no matter what I do this won't get injected. Just to mention this is happening in tests. Do you have any suggestion?

Cannot Write Data to ElasticSearch with AbstractReactiveElasticsearchConfiguration

I am trying out to write data to my local Elasticsearch Docker Container (7.4.2), for simplicity I used the AbstractReactiveElasticsearchConfiguration given from Spring also Overriding the entityMapper function. The I constructed my repository extending the ReactiveElasticsearchRepository
Then in the end I used my autowired repository to saveAll() my collection of elements containing the data. However Elasticsearch doesn't write any data. Also i have a REST controller which is starting my whole process returning nothing basicly, DeferredResult>
The REST method coming from my ApiDelegateImpl
#Override
public DeferredResult<ResponseEntity<Void>> openUsageExporterStartPost() {
final DeferredResult<ResponseEntity<Void>> deferredResult = new DeferredResult<>();
ForkJoinPool.commonPool().execute(() -> {
try {
openUsageExporterAdapter.startExport();
deferredResult.setResult(ResponseEntity.accepted().build());
} catch (Exception e) {
deferredResult.setErrorResult(e);
}
}
);
return deferredResult;
}
My Elasticsearch Configuration
#Configuration
public class ElasticSearchConfig extends AbstractReactiveElasticsearchConfiguration {
#Value("${spring.data.elasticsearch.client.reactive.endpoints}")
private String elasticSearchEndpoint;
#Bean
#Override
public EntityMapper entityMapper() {
final ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService());
entityMapper.setConversions(elasticsearchCustomConversions());
return entityMapper;
}
#Override
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(elasticSearchEndpoint)
.build();
return ReactiveRestClients.create(clientConfiguration);
}
}
My Repository
public interface OpenUsageRepository extends ReactiveElasticsearchRepository<OpenUsage, Long> {
}
My DTO
#Data
#Document(indexName = "open_usages", type = "open_usages")
#TypeAlias("OpenUsage")
public class OpenUsage {
#Field(name = "id")
#Id
private Long id;
......
}
My Adapter Implementation
#Autowired
private final OpenUsageRepository openUsageRepository;
...transform entity into OpenUsage...
public void doSomething(final List<OpenUsage> openUsages){
openUsageRepository.saveAll(openUsages)
}
And finally my IT test
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#Testcontainers
#TestPropertySource(locations = {"classpath:application-it.properties"})
#ContextConfiguration(initializers = OpenUsageExporterApplicationIT.Initializer.class)
class OpenUsageExporterApplicationIT {
#LocalServerPort
private int port;
private final static String STARTCALL = "http://localhost:%s/open-usage-exporter/start/";
#Container
private static ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:6.8.4").withExposedPorts(9200);
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(final ConfigurableApplicationContext configurableApplicationContext) {
final List<String> pairs = new ArrayList<>();
pairs.add("spring.data.elasticsearch.client.reactive.endpoints=" + container.getContainerIpAddress() + ":" + container.getFirstMappedPort());
pairs.add("spring.elasticsearch.rest.uris=http://" + container.getContainerIpAddress() + ":" + container.getFirstMappedPort());
TestPropertyValues.of(pairs).applyTo(configurableApplicationContext);
}
}
#Test
void testExportToES() throws IOException, InterruptedException {
final List<OpenUsageEntity> openUsageEntities = dbPreparator.insertTestData();
assertTrue(openUsageEntities.size() > 0);
final String result = executeRestCall(STARTCALL);
// Awaitility here tells me nothing is in ElasticSearch :(
}
private String executeRestCall(final String urlTemplate) throws IOException {
final String url = String.format(urlTemplate, port);
final HttpUriRequest request = new HttpPost(url);
final HttpResponse response = HttpClientBuilder.create().build().execute(request);
// Get the result.
return EntityUtils.toString(response.getEntity());
}
}
public void doSomething(final List<OpenUsage> openUsages){
openUsageRepository.saveAll(openUsages)
}
This lacks a semicolon at the end, so it should not compile.
But I assume this is just a typo, and there is a semicolon in reality.
Anyway, saveAll() returns a Flux. This Flux is just a recipe for saving your data, and it is not 'executed' until subscribe() is called by someone (or something like blockLast()). You just throw that Flux away, so the saving never gets executed.
How to fix this? One option is to add .blockLast() call:
openUsageRepository.saveAll(openUsages).blockLast();
But this will save the data in a blocking way effectively defeating the reactivity.
Another option is, if the code you are calling saveAll() from supports reactivity is just to return the Flux returned by saveAll(), but, as your doSomething() has void return type, this is doubtful.
It is not seen how your startExport() connects to doSomething() anyway. But it looks like your 'calling code' does not use any notion of reactivity, so a real solution would be to either rewrite the calling code to use reactivity (obtain a Publisher and subscribe() on it, then wait till the data arrives), or revert to using blocking API (ElasticsearchRepository instead of ReactiveElasticsearchRepository).

RequestHandlerRetryAdvice cannot be made to work with Ftp.outboundGateway in Spring Integration

My situation is similar to the one described in this SO question. The difference being that I don't use a WebFlux.outboundGateway but an Ftp.outboundGateway on which I call an AbstractRemoteFileOutboundGateway.Command.GETcommand, the common problem being that I can't get the defined RequestHandlerRetryAdvice to be used.
The configuration looks like this (stripped down to the relevant parts):
#RestController
#RequestMapping( value = "/somepath" )
public class DownloadController
{
private DownloadGateway downloadGateway;
public DownloadController( DownloadGateway downloadGateway )
{
this.downloadGateway = downloadGateway;
}
#PostMapping( "/downloads" )
public void download( #RequestParam( "filename" ) String filename )
{
Map<String, Object> headers = new HashMap<>();
downloadGateway.triggerDownload( filename, headers );
}
}
#MessagingGateway
public interface DownloadGateway
{
#Gateway( requestChannel = "downloadFiles.input" )
void triggerDownload( Object value, Map<String, Object> headers );
}
#Configuration
#EnableIntegration
public class FtpDefinition
{
private FtpProperties ftpProperties;
public FtpDefinition( FtpProperties ftpProperties )
{
this.ftpProperties = ftpProperties;
}
#Bean
public DirectChannel gatewayDownloadsOutputChannel()
{
return new DirectChannel();
}
#Bean
public IntegrationFlow downloadFiles( RemoteFileOutboundGatewaySpec<FTPFile, FtpOutboundGatewaySpec> getRemoteFile )
{
return f -> f.handle( getRemoteFile, getRetryAdvice() )
.channel( "gatewayDownloadsOutputChannel" );
}
private Consumer<GenericEndpointSpec<AbstractRemoteFileOutboundGateway<FTPFile>>> getRetryAdvice()
{
return e -> e.advice( ( (Supplier<RequestHandlerRetryAdvice>) () -> {
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
advice.setRetryTemplate( getRetryTemplate() );
return advice;
} ).get() );
}
private RetryTemplate getRetryTemplate()
{
RetryTemplate result = new RetryTemplate();
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod( 5000 );
result.setBackOffPolicy( backOffPolicy );
return result;
}
#Bean
public RemoteFileOutboundGatewaySpec<FTPFile, FtpOutboundGatewaySpec> getRemoteFile( SessionFactory sessionFactory )
{
return
Ftp.outboundGateway( sessionFactory,
AbstractRemoteFileOutboundGateway.Command.GET,
"payload" )
.fileExistsMode( FileExistsMode.REPLACE )
.localDirectoryExpression( "'" + ftpProperties.getLocalDir() + "'" )
.autoCreateLocalDirectory( true );
}
#Bean
public SessionFactory<FTPFile> ftpSessionFactory()
{
DefaultFtpSessionFactory sessionFactory = new DefaultFtpSessionFactory();
sessionFactory.setHost( ftpProperties.getServers().get( 0 ).getHost() );
sessionFactory.setPort( ftpProperties.getServers().get( 0 ).getPort() );
sessionFactory.setUsername( ftpProperties.getServers().get( 0 ).getUser() );
sessionFactory.setPassword( ftpProperties.getServers().get( 0 ).getPassword() );
return sessionFactory;
}
}
#SpringBootApplication
#EnableIntegration
#IntegrationComponentScan
public class FtpTestApplication {
public static void main(String[] args) {
SpringApplication.run( FtpTestApplication.class, args );
}
}
#Configuration
#PropertySource( "classpath:ftp.properties" )
#ConfigurationProperties( prefix = "ftp" )
#Data
public class FtpProperties
{
#NotNull
private String localDir;
#NotNull
private List<Server> servers;
#Data
public static class Server
{
#NotNull
private String host;
#NotNull
private int port;
#NotNull
private String user;
#NotNull
private String password;
}
}
The Controller is mostly just there for testing purposes, in the actual implementation there's a poller. My FtpProperties hold a list of servers because in the actual implementation, I use a DelegatingSessionFactory to pick an instance based on some parameters.
According to Gary Russell's comment, I'd expect a failed download to be retried. But if I interrupt a download server-side (by issuing "Kick user" in a FileZilla instance), I just get an immediate stack trace and no retries:
org.apache.commons.net.ftp.FTPConnectionClosedException: FTP response 421 received. Server closed connection.
[...]
I also need to upload files, for which I use an Ftp.outboundAdapter. In this case and with the same RetryTemplate, if I interrupt an upload server-side, Spring Integration performs two more attempts with a delay of 5s each, and only then logs java.net.SocketException: Connection reset, all as expected.
I tried to debug a little and noticed that right before the first attempt to upload through the Ftp.outboundAdapter, a breakpoint on RequestHandlerRetryAdvice.doInvoke() is hit. But when downloading through the Ftp.outboundGateway, that breakpoint is never hit.
Is there a problem with my configuration, could someone get the RequestHandlerRetryAdvice to work with Ftp.outboundGateway/AbstractRemoteFileOutboundGateway.Command.GET?
Sorry for the delay; we are at SpringOne Platform this week.
The problem is due to the fact that the gateway spec is a bean - the gateway ends up being initialized before the advice is applied.
I changed your code like this...
#Bean
public IntegrationFlow downloadFiles(SessionFactory<FTPFile> sessionFactory) {
return f -> f.handle(getRemoteFile(sessionFactory), getRetryAdvice())
.channel("gatewayDownloadsOutputChannel");
}
...
private RemoteFileOutboundGatewaySpec<FTPFile, FtpOutboundGatewaySpec> getRemoteFile(SessionFactory<FTPFile> sessionFactory) {
return Ftp.outboundGateway(sessionFactory,
AbstractRemoteFileOutboundGateway.Command.GET,
"payload")
.fileExistsMode(FileExistsMode.REPLACE)
.localDirectoryExpression("'/tmp'")
.autoCreateLocalDirectory(true);
}
...and it worked.
It's generally better to not deal with Specs directly and just have them inlined in the flow definition...
#Bean
public IntegrationFlow downloadFiles(SessionFactory<FTPFile> sessionFactory) {
return f -> f.handle(Ftp.outboundGateway(sessionFactory,
AbstractRemoteFileOutboundGateway.Command.GET,
"payload")
.fileExistsMode(FileExistsMode.REPLACE)
.localDirectoryExpression("'/tmp'")
.autoCreateLocalDirectory(true), getRetryAdvice())
.channel("gatewayDownloadsOutputChannel");
}

spring testing #async method

I'm trying to test if #Async annotation of Spring is working as expected on my project. But It doesn't.
I have this test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = GlobalConfiguration.class)
public class ActivityMessageListenerTest {
#Autowired
private ActivityMessageListener activityMessageListener;
private Long USER_ID = 1l;
private Long COMPANY_ID = 2l;
private Date DATE = new Date(10000000);
private String CLASSNAME = "className";
private Long CLASSPK = 14l;
private Integer TYPE = 22;
private String EXTRA_DATA = "extra";
private Long RECIVED_USER_ID = 99l;
#Before
public void setup() throws Exception {
}
#Test
public void testDoReceiveWithException() throws Exception {
System.out.println("Current thread " + Thread.currentThread().getName());
Map<String, Object> values = new HashMap();
values.put(ActivityMessageListener.PARAM_USER_ID, USER_ID);
values.put(ActivityMessageListener.PARAM_COMPANY_ID, COMPANY_ID);
values.put(ActivityMessageListener.PARAM_CREATE_DATE, DATE);
values.put(ActivityMessageListener.PARAM_CLASS_NAME, CLASSNAME);
values.put(ActivityMessageListener.PARAM_CLASS_PK, CLASSPK);
values.put(ActivityMessageListener.PARAM_TYPE, TYPE);
values.put(ActivityMessageListener.PARAM_EXTRA_DATA, EXTRA_DATA );
values.put(ActivityMessageListener.PARAM_RECEIVED_USER_ID, RECIVED_USER_ID);
Message message = new Message();
message.setValues(values);
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
}
}
As you can see I'm printing the name of the current thread.
The class containing the #Async method is:
public class ActivityMessageListener extends BaseMessageListener {
public static final String PARAM_USER_ID = "userId";
public static final String PARAM_COMPANY_ID = "companyId";
public static final String PARAM_CREATE_DATE = "createDate";
public static final String PARAM_CLASS_NAME = "className";
public static final String PARAM_CLASS_PK = "classPK";
public static final String PARAM_TYPE = "type";
public static final String PARAM_EXTRA_DATA = "extraData";
public static final String PARAM_RECEIVED_USER_ID = "receiverUserId";
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
#Override
#Async(value = "activityExecutor")
public void doReceive(Message message) throws Exception {
System.out.println("Current " + Thread.currentThread().getName());
if (1> 0)
throw new RuntimeException("lalal");
Map<String, Object> parameters = message.getValues();
Long userId = (Long)parameters.get(ActivityMessageListener.PARAM_USER_ID);
Long companyId = (Long)parameters.get(ActivityMessageListener.PARAM_COMPANY_ID);
Date createDate = (Date)parameters.get(ActivityMessageListener.PARAM_CREATE_DATE);
String className = (String)parameters.get(ActivityMessageListener.PARAM_CLASS_NAME);
Long classPK = (Long)parameters.get(ActivityMessageListener.PARAM_CLASS_PK);
Integer type = (Integer)parameters.get(ActivityMessageListener.PARAM_TYPE);
String extraData = (String)parameters.get(ActivityMessageListener.PARAM_EXTRA_DATA);
Long receiverUserId = (Long)parameters.get(ActivityMessageListener.PARAM_RECEIVED_USER_ID);
ActivityLocalServiceUtil.addActivity(userId, companyId, createDate, className, classPK, type, extraData, receiverUserId);
}
}
Here I'm printing the name of the current thread inside of the #Async method, and the name is the same as before, main. So it's not working.
The GlobalConfiguration is:
#Configuration
#EnableAspectJAutoProxy
#EnableTransactionManagement
#ComponentScan({
"com.shn.configurations",
...some packages...
})
public class GlobalConfiguration {...}
And inside one of the specified packages has the activityExecutor bean:
#Configuration
#EnableAsync(proxyTargetClass = true)
public class ExecutorConfiguration {
#Bean
public ActivityMessageListener activityMessageListener() {
return new ActivityMessageListener();
}
#Bean
public TaskExecutor activityExecutor()
{
ThreadPoolTaskExecutor threadPoolTaskExecutor =
new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setQueueCapacity(100);
return threadPoolTaskExecutor;
}
}
What I'm doing wrong?
Tricky.
Asynchronous behavior is added through proxying.
Spring provides you with a proxy that wraps the actual object and performs the actual invocation in a separate thread.
It looks something like this (except most of this is done dynamically with CGLIB or JDK proxies and Spring handlers)
class ProxyListener extends ActivityMessageListener {
private ActivityMessageListener real;
public ProxyListener(ActivityMessageListener real) {
this.real = real;
}
TaskExecutor executor; // injected
#Override
public void doReceive(Message message) throws Exception {
executor.submit(() -> real.doReceive(message)); // in another thread
}
}
ActivityMessageListener real = new ActivityMessageListener();
ProxyListener proxy = new ProxyListener(real);
Now, in a Spring world, you'd have a reference to the proxy object, not to the ActivityMessageListener. That is
ActivityMessageListener proxy = applicationContext.getBean(ActivityMessageListener.class);
would return a reference to the ProxyListener. Then, through polymorphism, invoking doReceive would go to the overriden Proxy#doReceive method which would invoke ActivityMessageListener#doReceive through delegation and you'd get your asynchronous behavior.
However, you're in a half Spring world.
Here
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
the reference this is actually referring to the real ActivityMessageListener, not to the proxy. So when, presumably, you send your message on the bus here
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
you're sending it to the real object, which doesn't have the proxy asynchronous behavior.
The full Spring solution would be to have the MessabeBus (and/or its queue) be Spring beans in which you can inject the fully process (proxied, autowired, initialized) beans.
In reality, since CGLIB proxies are really just subclasses of your types, so the ProxyListener above would actually also add itself to the bus since the super constructor would be invoked. It would seem though that only one MessageListener can register itself with a key, like MKTDestinationNames.ACTIVITY_REGISTRY. If this isn't the case, you'd have to show more of that code for explanation.
In your test, if you do
activityMessageListener.doReceive(message);
you should see that asynchronous behavior since activityMessageListener should hold a reference to the proxy.

Use #Autowired in #Webservice (other solutions found did not work)

We are trying to use autowiring in our webservice, but this doens't seem to work (generates nullPointer). We have been searching for a solution for quite a long time, but did not succeed.
Our webservice:
#WebService(wsdlLocation = "/WEB-INF/wsdl/contract.wsdl", serviceName = "BookingService", targetNamespace = "http://realdolmen.com/", portName = "BookingServicePortType")
public class BookingService extends SpringBeanAutowiringSupport implements BookingServicePortType {
#Autowired
BookingServiceBean bookingServiceBean;
#Autowired
TariffService tariffService;
#Override
public BookingResponse createBooking(#WebParam(name = "bookingInput", targetNamespace = "http://realdolmen.com/", partName = "tariffId") BookingInput input) {
Tariff tariff = tariffService.getTariffById(input.getTariffId());
Booking booking = new Booking.BookingBuilder().withBaggageAllowance(tariff.getFlight().getBaggageAllowance())
.withDayOfDeparture(input.getDayOfDeparture()).withHourOfDeparture(input.getHourOfDeparture()).withTariff(tariff).withDuration(input.getDuration()).createBooking();
bookingServiceBean.createBooking(booking);
BookingResponse bookingResponse = new BookingResponse();
bookingResponse.setBookingId(booking.getId());
bookingResponse.setBaggageAllowance(booking.getBaggageAllowance());
bookingResponse.setDayOfDeparture(createWeirdDateClass(booking.getDayOfDeparture()));
bookingResponse.setDuration(booking.getDuration());
bookingResponse.setHourOfDeparture(booking.getHourOfDeparture());
return bookingResponse;
}
private XMLGregorianCalendar createWeirdDateClass(String lexicalRepresentation) {
try {
return DatatypeFactory.newInstance().newXMLGregorianCalendar(lexicalRepresentation);
} catch (DatatypeConfigurationException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
}
}
our spring service:
#Service
#Transactional
public class BookingServiceBeanImpl implements BookingServiceBean {
#Autowired
BookingDAO bookingDAO;
#Override public void createBooking(Booking booking) {
bookingDAO.createBooking(booking);
}
}
The spring bean can be used in the spring controllers so I don't think there's a problem there..

Resources