In my application I'm using the library jersey and Apache CXF for the implementation of the REST calls . In particular, I have created my DTO, a service and a dao to insert a record into a database through the put method.
The code below is working, but my problem is when certain to pass a list of DTO that the client reports an error:
/* Client Rest */
WebResource service = clientJersey.get().resource(this.baseURI);
GaraDTO garaDTO = new GaraDTO();
garaDTO.setVersion("0");
..........................
ClientResponse response = service.path("rest").path("gare").accept(this.mediaType).put(ClientResponse.class, garaDTO);
/* DTO */
#XmlRootElement(name = "gara")
public class GaraDTO {
private Integer version;
. . . . . . . . . . . .
public GaraDTO(){
}
#XmlElement
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
. . . . . . . . . . . .
}
/* Service */
#Override
#PUT
#Consumes(MediaType.APPLICATION_XML)
public void putInsert(GaraDTO garaDto){
....................
//insert DB
....................
}
If, after having created an ArrayList of DTO, step this list to the client I get an error.
List<GaraDTO> listGaraDTO = new ArrayList();
listGaraDTO.add(garaDTO1);
listGaraDTO.add(garaDTO2);
..............................
ClientResponse response = service.path("rest").path("gare").accept(this.mediaType).put(ClientResponse.class, listGaraDTO);
How can I pass a list of DTO?
thanks
If you want your API to accept a List, you need to make it actually accept a List, and not a single GaraDTO.
// Client
package com.project.rest.model;
import java.util.HashSet;
import java.util.Set;
public class Client {
private Long id;
private String email;
private String lang;
public Client() {
}
public Client(Long id) {
this.id = id;
}
public Client(Long id, String email, String lang) {
this.id = id;
this.email = email;
this.lang = lang;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getLang() {
return lang;
}
public void setLang(String lang) {
this.lang = lang;
}
#Override
public String toString() {
return "Client [id=" + id + ", email=" + email + ", lang=" + lang + "]";
}
}
//ClientService
package com.project.rest;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.project.rest.model.Client;
#Path("/client")
public class ClientService {
#POST
#Path("/sendList")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response consumeJSONList(List<Client> clientList) {
String output = "consumeJSONList Client : " + clientList.toString() + "\n\n";
return Response.status(200).entity(output).build();
}
}
//JerseyClient
package com.project.rest;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.MediaType;
import com.project.rest.model.Client;
import com.project.rest.model.Device;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
public class JerseyClient {
public static void main(String[] args) {
try {
List<Client> clientList = new ArrayList<Client>();
clientList.add(new Client(1L, "pruebas#pruebas.com", "es"));
clientList.add(new Client(2L, "pruebas#pruebas.com", "es"));
clientList.add(new Client(3L, "pruebas#pruebas.com", "es"));
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
com.sun.jersey.api.client.Client c = com.sun.jersey.api.client.Client.create(clientConfig);
WebResource webResource = c.resource("http://localhost:8080/project_rest/rest/client/sendList");
ClientResponse response = webResource.accept("application/json").type("application/json").post(ClientResponse.class, clientList);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed sendClientList: HTTP error code : " + response.getStatus());
}
String output = response.getEntity(String.class);
System.out.println("sendClientList... Server response .... \n");
System.out.println(output);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//POM.xml
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-bundle</artifactId>
<version>1.10-b01</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>0.99</version>
</dependency>
Related
I'm using spring-batch along with spring-boot 2.5.6. I decided to use remote-partitioning with Kafka as the middleware. I have one manager and three workers. accordingly, one partition has been assigned for the manager's input topic and three partitions have been assigned for the worker's input.
the manager takes a file, creates multiples ExecutionContexts and sends those over Kafka. workers start processing the respective steps and send the message at the end of their process. manager will aggregate the worker's results and decide to complete the job if all workers are done. so far so good.
now assume first I run a long-running job that requires lots of time to finish and then I run a small job that finishes quickly. not surprisingly the second job finishes sooner and sends a completed signal, the manager consumes this message and continues the process. I even checked AggregatingMessageHandler, the completed message is related to the second job (short-running one) only, I checked the jobExecutionId
now the problem happens, I have a JobListener that has an afterJob method. this method will be run against the first job (the long-running one that is still being processed by workers), not the second one (the short-running one that a completed signal has been sent for it)! I can say this by looking at the jobExecutionId. it's really weird because I never saw in the logs that there's a completion signal for the first job.
after some time and whenever the first long-running job is finished, the final worker sends a completed message and the manager decides to finish the job, now the JobListener is run against the second job (short-running one)!
I couldn't understand what goes wrong? I would like to assume that probably it's a miss-configuration, but by debugging the code and checking AggregatingMessageHandler and TRACE logs in the workers and manager, I can clearly see that the messages are being sent fine and there's nothing wrong with the messages. any suggestions/ideas are welcome.
UPDATE
here is a sample implementation: let's say we have a Customer table.
the job takes minId and maxId (ID column in Customer table is a simple number) then the manager creates multiple ExecutionContexts based on the ids range.
manager config
package com.example.batchdemo.job;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.integration.partition.RemotePartitioningManagerStepBuilderFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.kafka.dsl.Kafka;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.listener.ConsumerProperties;
import org.springframework.scheduling.support.PeriodicTrigger;
#Profile("!worker")
#Configuration
public class JobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final JobExplorer jobExplorer;
private final RemotePartitioningManagerStepBuilderFactory managerStepBuilderFactory;
private final JobListener jobListener;
public JobConfiguration(JobBuilderFactory jobBuilderFactory, JobExplorer jobExplorer, RemotePartitioningManagerStepBuilderFactory managerStepBuilderFactory, JobListener jobListener) {
this.jobBuilderFactory = jobBuilderFactory;
this.jobExplorer = jobExplorer;
this.managerStepBuilderFactory = managerStepBuilderFactory;
this.jobListener = jobListener;
}
#Bean
public Job job() {
return jobBuilderFactory.get("job")
.incrementer(new RunIdIncrementer())
.start(managerStep())
.listener(jobListener)
.build();
}
#Bean
public Step managerStep() {
return managerStepBuilderFactory.get("managerStep")
.partitioner("workerStep", rangePartitioner(null, null))
.outputChannel(requestForWorkers())
.inputChannel(repliesFromWorkers())
.jobExplorer(jobExplorer)
.build();
}
#Bean
#StepScope
public Partitioner rangePartitioner(#Value("#{jobParameters['minId']}") Integer minId, #Value("#{jobParameters['maxId']}") Integer maxId) {
return new CustomerIdRangePartitioner(minId, maxId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
#Bean
public DirectChannel requestForWorkers() {
return new DirectChannel();
}
#Bean
public IntegrationFlow outboundFlow(KafkaTemplate kafkaTemplate) {
return IntegrationFlows
.from(requestForWorkers())
.handle(Kafka.outboundChannelAdapter(kafkaTemplate).topic("requestForWorkers"))
.route("requestForWorkers")
.get();
}
#Bean
public DirectChannel repliesFromWorkers() {
return new DirectChannel();
}
#Bean
public IntegrationFlow inboundFlow(ConsumerFactory consumerFactory) {
return IntegrationFlows
.from(Kafka.inboundChannelAdapter(consumerFactory, new ConsumerProperties("repliesFromWorkers")))
.channel(repliesFromWorkers())
.get();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(10));
return pollerMetadata;
}
}
worker config
package com.example.batchdemo.job;
import com.example.batchdemo.domain.Customer;
import com.example.batchdemo.domain.CustomerRowMapper;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.step.builder.SimpleStepBuilder;
import org.springframework.batch.integration.partition.RemotePartitioningWorkerStepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.JdbcPagingItemReader;
import org.springframework.batch.item.database.Order;
import org.springframework.batch.item.database.support.MySqlPagingQueryProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.kafka.dsl.Kafka;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.listener.ConsumerProperties;
import org.springframework.scheduling.support.PeriodicTrigger;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#Configuration
#Profile("worker")
public class WorkerConfiguration {
private static final int CHUNK_SIZE = 10;
private static final int WAITING_TIME = 3000;
public final DataSource dataSource;
private final RemotePartitioningWorkerStepBuilderFactory workerStepBuilderFactory;
public WorkerConfiguration(DataSource dataSource, RemotePartitioningWorkerStepBuilderFactory workerStepBuilderFactory) {
this.dataSource = dataSource;
this.workerStepBuilderFactory = workerStepBuilderFactory;
}
#Bean
public DirectChannel repliesFromWorkers() {
return new DirectChannel();
}
#Bean
public IntegrationFlow outboundFlow(KafkaTemplate kafkaTemplate) {
return IntegrationFlows
.from(repliesFromWorkers())
.handle(Kafka.outboundChannelAdapter(kafkaTemplate).topic("repliesFromWorkers"))
.route("repliesFromWorkers")
.get();
}
#Bean
public DirectChannel requestForWorkers() {
return new DirectChannel();
}
#Bean
public IntegrationFlow inboundFlow(ConsumerFactory consumerFactory) {
return IntegrationFlows
.from(Kafka.inboundChannelAdapter(consumerFactory, new ConsumerProperties("requestForWorkers")))
.channel(requestForWorkers())
.get();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(10));
return pollerMetadata;
}
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
#Bean
public Step workerStep() {
SimpleStepBuilder workerStepBuilder = workerStepBuilderFactory.get("workerStep")
.inputChannel(requestForWorkers())
.outputChannel(repliesFromWorkers())
.<Customer, Customer>chunk(CHUNK_SIZE)
.reader(pagingItemReader(null, null))
.processor(itemProcessor())
.writer(customerItemWriter());
return workerStepBuilder.build();
}
#Bean
#StepScope
public JdbcPagingItemReader<Customer> pagingItemReader(#Value("#{stepExecutionContext['minValue']}") Long minValue,
#Value("#{stepExecutionContext['maxValue']}") Long maxValue) {
System.out.println("reading " + minValue + " to " + maxValue);
JdbcPagingItemReader<Customer> reader = new JdbcPagingItemReader<>();
reader.setDataSource(this.dataSource);
reader.setFetchSize(1000);
reader.setRowMapper(new CustomerRowMapper());
MySqlPagingQueryProvider queryProvider = new MySqlPagingQueryProvider();
queryProvider.setSelectClause("id, firstName, lastName, birthdate");
queryProvider.setFromClause("from CUSTOMER");
queryProvider.setWhereClause("where id >= " + minValue + " and id < " + maxValue);
Map<String, Order> sortKeys = new HashMap<>(1);
sortKeys.put("id", Order.ASCENDING);
queryProvider.setSortKeys(sortKeys);
reader.setQueryProvider(queryProvider);
return reader;
}
#Bean
#StepScope
public ItemProcessor<Customer, Customer> itemProcessor() {
return item -> {
Thread.sleep(WAITING_TIME);
System.out.println(item);
return item;
};
}
#Bean
#StepScope
public ItemWriter<Customer> customerItemWriter() {
return items -> {
System.out.printf("%d items were written%n", items.size());
};
}
}
Partitioner:
package com.example.batchdemo.job;
import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.item.ExecutionContext;
import java.util.HashMap;
import java.util.Map;
public class CustomerIdRangePartitioner implements Partitioner {
private final int minId;
private final int maxId;
private final int gridSize;
public CustomerIdRangePartitioner(int minId, int maxId, int gridSize) {
this.minId = minId;
this.maxId = maxId;
this.gridSize = gridSize;
}
#Override
public Map<String, ExecutionContext> partition(int gridSize) {
int number = (maxId - minId) / this.gridSize + 1;
Map<String, ExecutionContext> result = new HashMap<>();
for (int i = 0; i < number; i++) {
ExecutionContext executionContext = new ExecutionContext();
int start = minId + (this.gridSize * i);
int end = start + (this.gridSize * (i + 1));
executionContext.putInt("minValue", start);
executionContext.putInt("maxValue", Math.min(end, maxId));
result.put("partition" + i, executionContext);
}
return result;
}
}
JobListener
package com.example.batchdemo.job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.stereotype.Component;
#Component
#JobScope
public class JobListener implements JobExecutionListener {
#Override
public void beforeJob(JobExecution jobExecution) {
}
#Override
public void afterJob(JobExecution jobExecution) {
System.out.println(jobExecution.getJobId() + " was finished: " + jobExecution.getStatus());
}
}
AppConfiguration
package com.example.batchdemo.controller;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.launch.support.SimpleJobOperator;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
#Configuration
public class AppConfiguration {
private final JobExplorer jobExplorer;
private final JobRepository jobRepository;
private final JobRegistry jobRegistry;
private final ApplicationContext applicationContext;
public AppConfiguration(JobExplorer jobExplorer, JobRepository jobRepository, JobRegistry jobRegistry, ApplicationContext applicationContext) {
this.jobExplorer = jobExplorer;
this.jobRepository = jobRepository;
this.jobRegistry = jobRegistry;
this.applicationContext = applicationContext;
}
#Bean
public synchronized JobRegistryBeanPostProcessor jobRegistrar() throws Exception {
JobRegistryBeanPostProcessor registrar = new JobRegistryBeanPostProcessor();
registrar.setJobRegistry(jobRegistry);
registrar.setBeanFactory(applicationContext.getAutowireCapableBeanFactory());
registrar.afterPropertiesSet();
return registrar;
}
#Bean
public JobOperator jobOperator() throws Exception {
SimpleJobOperator simpleJobOperator = new SimpleJobOperator();
simpleJobOperator.setJobLauncher(getJobLauncher());
simpleJobOperator.setJobParametersConverter(new DefaultJobParametersConverter());
simpleJobOperator.setJobRepository(this.jobRepository);
simpleJobOperator.setJobExplorer(this.jobExplorer);
simpleJobOperator.setJobRegistry(this.jobRegistry);
simpleJobOperator.afterPropertiesSet();
return simpleJobOperator;
}
#Bean
public JobLauncher getJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = null;
jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(jobOperatorExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
#Bean
public ThreadPoolTaskExecutor jobOperatorExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(64);
threadPoolTaskExecutor.setMaxPoolSize(256);
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
return threadPoolTaskExecutor;
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>batch-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>batch-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-kafka</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
This is a bug in Spring Batch. The listener is indeed called for the job that finishes earlier with the wrong JobExecution instance. Making the JobExecutionListener job-scoped does not solve the issue.
I will re-open the issue on Github for further investigation.
Below is my code, I am using command line tool, jax-rs, rest apis and java
I get this error: "SEVERE: MessageBodyWriter not found for media type = text/plain, type=class java.util.ArrayList, genericType=class java.util.ArrayList", when I go to localhost:8888/quotes after the command "mvn exec:java"
What am I doing wrong? I have a main class, Quote class, QuoteResource class, QuoteService class, quoteTest class, and pom.xml with the following code:
package edu.depaul.se457.bisbell;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
/**
* Root resource (exposed at "quotes" path)
*/
#Path("quotes")
public class QuoteResource {
private QuoteService quoteService = new QuoteService();
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response getAllQuotes() {
List<Quote> quotes = quoteService.getAllQuotes();
if (!quotes.isEmpty()){
return Response.ok(quotes).build();
}else{
return Response.status(Response.Status.NOT_FOUND).build();
}
}
#Path("/{id}")
#GET
#Produces(MediaType.TEXT_PLAIN)
//public int getQuoteByID(#PathParam("id") int id){
public Response getQuoteById(#PathParam("id") int id) throws NotFoundException{
Quote quote = quoteService.fetchBy(id);
if (quote.id.equals(null)) {
return Response.status(Response.Status.NOT_FOUND).build();
}else{
return Response.ok(quote).build();
}
}
#POST
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.TEXT_PLAIN)
public Response createQuote(Quote quote){
boolean result = quoteService.create(quote);
if (result){
return Response.ok().status(Response.Status.CREATED).build();
}else{
return Response.notModified().build();
}
}
#PUT
#Path("/{id}")
#Consumes(MediaType.TEXT_PLAIN)
public Response updateQuote(#PathParam("id") int id, Quote quote) {
boolean result = quoteService.update(quote);
if (result) {
return Response.ok().status(Response.Status.NO_CONTENT).build();
} else {
return Response.notModified().build();
}
}
#Path("/{id}")
#DELETE
#Produces(MediaType.TEXT_PLAIN)
public Response deleteQuote(#PathParam("id") int id) {
boolean result = quoteService.delete(id);
if (result) {
return Response.ok().status(Response.Status.NO_CONTENT).build();
} else {
return Response.notModified().build();
}
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package edu.depaul.se457.bisbell;
/**
*
* #author bkisb
*/
public class Quote {
Integer id;
String quote;
public Quote() {
}
public Quote(String quote, int id){
super();
this.quote = quote;
this.id = id;
}
public String getQuote(){
return quote;
}
public void setQuote(String quote){
this.quote = quote;
}
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
}
#Override
public String toString() {
return "Quote [id=" + id + "]";
}
boolean add(Quote quote) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
package edu.depaul.se457.bisbell;
import jakarta.ws.rs.NotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* Root resource (exposed at "quote" path)
*/
public class QuoteService {
/**
* Method handling HTTP GET requests. The returned object will be sent
* to the client as "text/plain" media type.
*
* #return String that will be returned as a text/plain response.
*/
List<Quote> quotes = new ArrayList<>();
public List<Quote> getAllQuotes() {
quotes.add(new Quote("Some Quote 1", 1));
quotes.add(new Quote("Some Quote 2", 2));
quotes.add(new Quote("Some Quote 3", 3));
quotes.add(new Quote("Some Quote 4", 4));
quotes.add(new Quote("Some Quote 5", 5));
return quotes;
}
//public int getQuoteByID(#PathParam("id") int id){
public Quote fetchBy(int id) throws NotFoundException{
for (Quote quote: getAllQuotes()) {
if (id == quote.getId()){
return quote;
}
else{
throw new NotFoundException("Resource not found with ID ::" + id);
}
}
return null;
}
public boolean create(Quote quote){
return quote.add(quote);
}
public boolean update(Quote quote){
for (Quote updateQuote: quotes) {
if (quote.getId() == (updateQuote.getId())){
quotes.remove(updateQuote);
quotes.add(quote);
return true;
}
}
return false;
}
public boolean delete(int id) throws NotFoundException {
for (Quote quote: quotes) {
if (quote.getId() == (id)) {
quotes.remove(quote);
return true;
}
}
return false;
}
}
package edu.depaul.se457.bisbell;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import org.glassfish.grizzly.http.server.HttpServer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class quoteTest {
private HttpServer server;
private WebTarget target;
#Before
public void setUp() throws Exception {
// start the server
server = Main.startServer();
// create the client
Client c = ClientBuilder.newClient();
// uncomment the following line if you want to enable
// support for JSON in the client (you also have to uncomment
// dependency on jersey-media-json module in pom.xml and Main.startServer())
// --
// c.configuration().enable(new org.glassfish.jersey.media.json.JsonJaxbFeature());
target = c.target(Main.BASE_URI);
}
#After
public void tearDown() throws Exception {
server.stop();
}
/**
* Test to see that the message "Got it!" is sent in the response.
*/
#Test
public void testGetIt() {
String responseMsg = target.path("quote").request().get(String.class);
assertEquals("Got It!", responseMsg);
}
}
package edu.depaul.se457.bisbell;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import java.io.IOException;
import java.net.URI;
/**
* Main class.
*
*/
public class Main {
// Base URI the Grizzly HTTP server will listen on
public static final String BASE_URI = "http://localhost:8888/";
/**
* Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
* #return Grizzly HTTP server.
*/
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in edu.depaul.se457.bisbell package
final ResourceConfig rc = new ResourceConfig().packages("edu.depaul.se457.bisbell");
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
/**
* Main method.
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format("Jersey app started with endpoints available at "
+ "%s%nHit Ctrl-C to stop it...", BASE_URI));
System.in.read();
server.stop();
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>edu.depaul.se457.bisbell</groupId>
<artifactId>hw2</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>hw2</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<!-- uncomment this to get JSON support:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
</dependency>
-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<inherited>true</inherited>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>edu.depaul.se457.bisbell.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<jersey.version>3.0.4</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
I am working on a simple HelloWorld Spring Boot application and I am trying to configure Swagger to automatically generate my REST service documentation.
I am following this tutorial: http://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
But I am facing some difficulties.
I've added the following Swagger dependencies to the maven pom.xml file:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
and also the Java configuration class SwaggerConfig as follows:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
According to the guide, this should be sufficient to view the generated documentation on http://localhost:8080/v2/api-docs
However, I am getting "Whitespace Error Page: This application has no explicit mapping for /error, so you are seeing this as a fallback."
I am getting the same error for the Swagger UI page at http://localhost:8080/greeting/api/swagger-ui.html too.
For extra information, my HelloWorld code is as follows:
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
and the greeting controller is
package hello;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicLong;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Everything is under main/java
I would really appreciate it if anyone has any ideas/suggestions on how to fix this! Thank you in advance :)
In my Spring Boot web application I use MongoDB to store data. In the application I access the database using interfaces that extend MongoRepository.
How do I set up a unit test for such a repository class? What I would like is to
start an embedded/in memory instance of MongoDB
insert testdata from JSON or XML
use an autowired repository to perform queries on the testdata
I have tried using Embedded MongoDB, but I can't figure out how to insert testdata from a file.
I've also tried using NoSQLUnit, but the SpringApplicationConfiguration conflicts with the unit test configuration, resulting in different databases for reading and writing.
Just use #DataMongoTest from Spring Boot.
#RunWith(SpringRunner.class)
#DataMongoTest
public class FooRepositoryTest {
#Autowired
FooRepository fooRepository;
#Before
public void setUp() throws Exception {
fooRepository.save(new Foo());
}
#Test
public void shouldBeNotEmpty() {
assertThat(fooRepository.findAll()).isNotEmpty();
}
}
Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
An update for 2018 and Spring Boot 2. First of all, you can have data-only integration tests with the embedded db as per the documentation. This uses the DataMongoTest annotation. This configures only the necessary dependencies that make mongoDB tests possible.
If you want to do full integration tests, add the AutoConfigureDataMongo annotation instead:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#AutoConfigureDataMongo
public class PriceApiControllerIT {
Dependencies you should have in your pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
This is what I did.
public interface UserRepository extends MongoRepository<Users, Long> {
public void deleteByUserId(String userId);
public List<Users> findAllByUserStatus(String userStatus);
}
#Document
public class Users {
#Id
private long id;
#Transient
public static final String SEQUENCE_NAME = "users_sequence";
#Indexed
#NotNull
private String userId;
private String firstName;
private String lastName;
private String userType;
private String userStatus;
#Email
private String emailId;
#Size(min = 10, max = 10)
#NumberFormat
private String phoneNumber;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public static String getSequenceName() {
return SEQUENCE_NAME;
}
public String getUserStatus() {
return userStatus;
}
public void setUserStatus(String userStatus) {
this.userStatus = userStatus;
}
}
Here is the junit
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MockodsApplication.class)
#SpringBootTest
#AutoConfigureMockMvc
public class UserControllerIT {
#Autowired
private UserRepository userRepository;
#Autowired
MongoTemplate mongoTemplate;
#Autowired
private MockMvc mvc;
#After
public void tearDown() {
}
#Test
public void test1() {
Users user = new Users();
long userId = 1L;
user.setId(userId);
user.setFirstName("FirstName");
user.setLastName("FirstName");
user.setEmailId("fisrtname.secondname#gmail.com");
user.setPhoneNumber("1234567890");
assertEquals(user, userRepository.save(user));
}
#Test
public void test2() {
List<Users> persistedUser = userRepository.findAll();
assertEquals("fisrtname.secondname#gmail.com", persistedUser.get(0).getEmailId());
}
}
This link helped me to implement https://dzone.com/articles/spring-integration-tests
I faced the same problem and we used a separate MongoConfiguration class to specify a particular configuration for our tests.
You can create an embedded mongo instance by using EmbeddedMongoBuilder like this :
import com.mongodb.Mongo;
import cz.jirutka.spring.embedmongo.EmbeddedMongoBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.net.ServerSocket;
#Configuration
public class MongoConfiguration {
#Bean
public Mongo mongo() throws IOException {
System.setProperty("DB.TRACE","true");
return new EmbeddedMongoBuilder()
.version("2.13.1")
.bindIp("127.0.0.1")
.port(allocateRandomPort())
.build();
}
}
Then in your test class, specify that you want to use that particular configuration with the #Import annotation :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#Import(MongoConfiguration.class)
Hope that helps.
Check out https://mongoUnit.org
From its docs:
It's a data driven Integration testing framework for Spring Boot
based applications that use MongoDB for persistence. The framework
enables the developer to test the data access logic with relative
ease.
First, make sure that you have added the following Spring Boot parent to your project:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
Since we added Spring Boot parent, we can add required dependencies without specifying their versions:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring-boot-starter-data-mongodb will enable Spring support for
MongoDB
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
de.flapdoodle.embed.mongo provides embedded MongoDB for integration
tests
After adding de.flapdoodle.embed.mongo dependency Spring Boot will automatically try to download and start the embedded MongoDB when running tests. The following snippet shows how you can configure the embedded MongoDB instance manually
class EmbeddedMongoDbIntegrationTest {
private MongodExecutable mongodExecutable;
private MongoTemplate mongoTemplate;
#After
void clean() {
mongodExecutable.stop();
}
#Before
void setup() throws Exception {
String ip = "localhost";
int port = 27017;
IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
.net(new Net(ip, port, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
mongoTemplate = new MongoTemplate(new MongoClient(ip, port), "test");
}
#Test
void test() throws Exception {
// given
DBObject objectToSave = BasicDBObjectBuilder.start()
.add("key", "value")
.get();
// when
mongoTemplate.save(objectToSave, "collection");
// then
assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
.containsOnly("value");
}
}
Note, that we can quickly create MongoTemplate bean configured to use
our manually configured embedded database and register it inside the
Spring container, so your mongo repository will start leveraging this
mongoTemplate as well.
I have just started learning JAX-RS and am trying to modify some examples from the O'Reilly RESTful Java with JAX-RS book. I've run into an issue where I am getting a null pointer exception when I try and POST an XML file to one of my JAX-RS services. The specific resource I am posting to uses JPA to persist information to a derby database. After reading several other question/responses and tutorials I am convinced my code is correct but perhaps I am missing some configuration. It seems the entity manager isn't being injected for some reason even though I have the appropriate annotations. Any input on my issue would be appreciated. Please see the following excerpts of my project that I think will be useful:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="jpa-example" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>java:comp/env/jdbc/DerbyConnection</jta-data-source>
<class>com.example.persistence.UserEntity</class>
<class>com.example.persistence.SearchEntity</class>
<properties>
<property name="openjpa.TransactionMode" value="managed"/>
<property name="openjpa.ConnectionFactoryMode" value="managed"/>
<property name="openjpa.LockTimeout" value="30000"/>
<property name="openjpa.jdbc.TransactionIsolation" value="read-committed"/>
<property name="openjpa.Log" value="TRACE"/>
<property name="openjpa.jdbc.UpdateManager" value="operation-order"/>
</properties>
</persistence-unit>
</persistence>
server.xml
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>jsp-2.2</feature>
<feature>jdbc-4.0</feature>
<feature>jpa-2.0</feature>
<feature>localConnector-1.0</feature>
<feature>jaxrs-1.1</feature>
<feature>ejbLite-3.1</feature>
</featureManager>
<httpEndpoint host="localhost" httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>
<jdbcDriver id="derbyJDBCDriver">
<library name="DerbyLib">
<fileset dir="/Users/jackson/Documents/db-derby-10.10.1.1-bin/lib" includes="derby.jar"/>
</library>
</jdbcDriver>
<dataSource id="DerbyConnection" jdbcDriverRef="derbyJDBCDriver" jndiName="jdbc/DerbyConnection">
<properties.derby.embedded createDatabase="create" databaseName="example"/>
</dataSource>
<applicationMonitor updateTrigger="mbean"/>
<webApplication id="REST" location="REST.war" name="REST"/>
</server>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>REST</display-name>
<servlet>
<description>
JAX-RS Tools Generated - Do not modify</description>
<servlet-name>JAX-RS Servlet</servlet-name>
<servlet-class>com.ibm.websphere.jaxrs.server.IBMRestServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.example.services.RESTConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<enabled>true</enabled>
<async-supported>false</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS Servlet</servlet-name>
<url-pattern>
/rest/*</url-pattern>
</servlet-mapping>
<ejb-local-ref>
<ejb-ref-name>ejb/UserResource</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>com.example.services.UserResource</local>
<ejb-link>
com.example.services.UserResourceBean
</ejb-link>
</ejb-local-ref>
</web-app>
RESTConfig.java
package com.example.services;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
public class RESTConfig extends Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(HelloWorld.class);
classes.add(UserResourceBean.class);
return classes;
}
}
UserEntity.java
package com.example.persistence;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#Entity(name = "User")
public class UserEntity {
private long id;
private String login;
private String password;
private String firstName;
private String lastName;
private String email;
private String role;
private String status;
#Id
#GeneratedValue
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#Override
public String toString()
{
return "UserEntity {" +
"id=" + id +
", email='" + email + '\'' +
", password='" + password + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", role='" + role + '\'' +
", status='" + status + '\'' +
'}';
}
}
UserResource.java
package com.example.services;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import com.example.domain.User;
import com.example.domain.Users;
#Path("/users")
public interface UserResource
{
#POST
#Consumes("application/xml")
Response createUser(User user, #Context UriInfo uriInfo);
#GET
#Produces("application/xml")
//#Formatted
Users getUsers(#QueryParam("start") int start,
#QueryParam("size") #DefaultValue("10") int size,
#QueryParam("firstName") String firstName,
#QueryParam("lastName") String lastName,
#Context UriInfo uriInfo);
#GET
#Path("{id}")
#Produces("application/xml")
User getUser(#PathParam("id") long id);
}
UserResourceBean.java
package com.example.services;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import com.example.domain.Link;
import com.example.domain.User;
import com.example.domain.Users;
import com.example.persistence.UserEntity;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
#Stateless
public class UserResourceBean implements UserResource
{
#PersistenceContext(unitName="jpa-example")
private EntityManager em;
public Response createUser(User user, UriInfo uriInfo)
{
UserEntity entity = new UserEntity();
domain2entity(entity, user);
System.out.println(entity);
em.persist(entity);
em.flush();
System.out.println("Created user " + entity.getId());
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.path(Long.toString(entity.getId()));
return Response.created(builder.build()).build();
}
public User getUser(long id)
{
UserEntity user = em.getReference(UserEntity.class, id);
return entity2domain(user);
}
public static void domain2entity(UserEntity entity, User user)
{
entity.setId(user.getId());
entity.setLogin(user.getLogin());
entity.setPassword(user.getPassword());
entity.setFirstName(user.getFirstName());
entity.setLastName(user.getLastName());
entity.setEmail(user.getEmail());
entity.setRole(user.getRole());
entity.setStatus(user.getStatus());
}
public static User entity2domain(UserEntity entity)
{
User u = new User();
u.setId(entity.getId());
u.setLogin(entity.getLogin());
u.setPassword(entity.getPassword());
u.setFirstName(entity.getFirstName());
u.setLastName(entity.getLastName());
u.setEmail(entity.getEmail());
u.setRole(entity.getRole());
u.setStatus(entity.getStatus());
return u;
}
public Users getUsers(int start,
int size,
String firstName,
String lastName,
UriInfo uriInfo)
{
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.queryParam("start", "{start}");
builder.queryParam("size", "{size}");
ArrayList<User> list = new ArrayList<User>();
ArrayList<Link> links = new ArrayList<Link>();
Query query = null;
if (firstName != null && lastName != null)
{
query = em.createQuery("select u from Users u where u.firstName=:first and u.lastName=:last");
query.setParameter("first", firstName);
query.setParameter("last", lastName);
}
else if (lastName != null)
{
query = em.createQuery("select u from Users u where u.lastName=:last");
query.setParameter("last", lastName);
}
else
{
query = em.createQuery("select u from Users u");
}
List userEntities = query.setFirstResult(start)
.setMaxResults(size)
.getResultList();
for (Object obj : userEntities)
{
UserEntity entity = (UserEntity) obj;
list.add(entity2domain(entity));
}
// next link
// If the size returned is equal then assume there is a next
if (userEntities.size() == size)
{
int next = start + size;
URI nextUri = builder.clone().build(next, size);
Link nextLink = new Link("next", nextUri.toString(), "application/xml");
links.add(nextLink);
}
// previous link
if (start > 0)
{
int previous = start - size;
if (previous < 0) previous = 0;
URI previousUri = builder.clone().build(previous, size);
Link previousLink = new Link("previous", previousUri.toString(), "application/xml");
links.add(previousLink);
}
Users users = new Users();
users.setUsers(list);
users.setLinks(links);
return users;
}
}
It is in this last file in which the NPE occurs. Specifically in function createUser, the following code throws a NPE: em.persist(entity);
I solved the injection issue by altering the code in RESTconfig.java to appear as follows:
package com.example.services;
import java.util.HashSet;
import java.util.Set;
import javax.naming.InitialContext;
import javax.ws.rs.core.Application;
public class RESTConfig extends Application {
public Set<Object> getSingletons()
{
HashSet<Object> set = new HashSet();
try
{
InitialContext ctx = new InitialContext();
obj = ctx.lookup(
"java:comp/env/ejb/UserResource");
set.add(obj);
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
return set;
}
}