What is the correct way to get entitymanager in a jax-rs subresource?
When I go to http://localhost/api/roots/1/branches the logger outputs null for the em. Also tried passing em into the constructor and that "works" as it is not null, but when I try to persist a new branch it throws a javax.persistence.TransactionRequiredException: No active transaction for PuId=TestAPIEAR#TestAPI.war#TestAPI
RootResource.java
#Path(value = "/roots")
#Stateless
public class RootResource {
#Context
UriInfo uriInfo;
#PersistenceContext(unitName = "TestAPI")
private EntityManager em;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Collection<Root> getRoots() {
TypedQuery<Root> query = em
.createNamedQuery("Root.findAll", Root.class);
List<Root> rootList = query.getResultList();
return rootList;
}
// Sub Resources
#Path("{rootid}/branches")
#Produces(MediaType.APPLICATION_JSON)
public BranchResource getBranches() {
return new BranchResource();
}
}
BranchResource.java
#Path("/")
#Stateless
public class BranchResource {
#PersistenceContext(unitName = "TestAPI")
private EntityManager em;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Collection<Branch> getBranches(#Context UriInfo uriInfo,
#PathParam("rootid") long rootId) {
logger.info("Entity Manager: " + em);
TypedQuery<BriefcaseContent> query = em.createNamedQuery(
"Branch.findAllforRoot", Branch.class);
query.setParameter("rootid", rootId);
List<Branch> branchList = query.getResultList();
return branchList;
}
}
Related
So I have been trying to make this rest service work and I have been on it for a few days but just can't get the URL to work no matter what I try. I first tried every suggestion the internet gave regarding to the web.xml file. Once I realised it's a last cause I tried the JAXRSConfiguration class with an '#ApplicationPath("/")' annotation.
So here is the JAXRS Class:
#ApplicationPath("api")
public class JAXRSConfiguration extends Application {
public JAXRSConfiguration() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("2.0.0");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8080");
beanConfig.setBasePath("/MyApp/api");
beanConfig.setResourcePackage("rest");
beanConfig.setPrettyPrint(true);
beanConfig.setScan();
getClasses();
}
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(UserResource.class);
classes.add(MessageService.class);
return classes;
}
}
And here is one of my resource files:
#Path("/users")
public class UserResource {
private UserService service = new UserService();
#DELETE
#Path("/{userId}")
public void deleteMessage(#PathParam("userId") int id) {
service.deleteUser(id);
}
#POST
#Path("/{userId}/follow/{userToFollowId}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void followUser(#PathParam("userToFollowId") int userId, int userToFollowId) {
service.followUser(userId, userToFollowId);
}
#POST
#Path("/register")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public User registerUser(User user) {
return service.registerUser(user);
}
#PUT
#Path("/{userId}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public User updateUser(User user) {
return service.updateUser(user);
}
#GET
#Path("/{userId}")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_XML)
public User getUser(#PathParam("userId") int id) {
return service.getUser(id);
}
#GET
#Produces(MediaType.APPLICATION_XML)
public List<User> getAllUsers() {
return service.getAllUsers();
}
#GET
#Path("/{userId}/followers")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_XML)
public List<User> getFollowers(#PathParam("userId") int id) {
return service.getFollowers(id);
}
#GET
#Path("/{userId}/followings")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_XML)
public List<User> getFollowings(#PathParam("userId") int id) {
return service.getFollowings(id);
}
#GET
#Path("/{userId}/kweets")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_XML)
public List<Kweet> getKweets(#PathParam("userId") String username) {
return service.getKweets(username);
}
#GET
#Path("/{userId}/timeline")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_XML)
public List<Kweet> loadTimeline(#PathParam("userId") int id) {
return service.loadTimeline(id);
}
}
The Error I'm getting is:
HTTP Status 404 - Not Found
type Status report
messageNot Found
descriptionThe requested resource is not available.
GlassFish Server Open Source Edition 5.0
Open your IDEA module settings, and look under Artifacts. You'll find something like this
The highlighted name of the war/exploded artifact is going to be your server context path.
In my case it would become
http://localhost:8080/kwetter-0.0.1
You should replace it with yours.
Now, you need to add the #ApplicationPath path, which is /api. So it becomes
http://localhost:8080/kwetter-0.0.1/api
Now, you need to add the REST service #Path, which is /users. So it becomes
http://localhost:8080/kwetter-0.0.1/api/users
An example:
GET http://localhost:8080/kwetter-0.0.1/api/users/{userId}/followers
Be sure you don't have this checked, under Run/Debug configuration.
I am trying to add logging to my application using ClientHttpRequestInterceptor.My interceptor is not being called.
Not sure what is going wrong here -
Here is my code -
#Component
#Slf4j
public final class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
protected static final LoggingAspect aspect = new LoggingAspect();
private final RequestContext requestContext;
private boolean logResponseBody = true;
public RestTemplateInterceptor(RequestContext requestContext) {
this.requestContext = requestContext;
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
populateHeader(request);
traceRequest(request, body);
ClientHttpResponse response = clientHttpRequestExecution.execute(request,body);
traceResponse(response);
return response;
}
private void populateHeader(HttpRequest request) {
final HttpHeaders headers = request.getHeaders();
// Propagate TAM headers
headers.add("iv-user", requestContext.getUser());
headers.add("MessageId", requestContext.getMessageId());
headers.add("CorrelationId", requestContext.getConversationId());
headers.add("BusinessId", requestContext.getBusinessId());
headers.add("ApplicationName", requestContext.getSourceSystem());
headers.add("iv-groups", requestContext.getGroups());
headers.add("MessageDateTime", requestContext.getSourceTimestamp());
}
...................
Here is my config file
#Configuration
public class RestTemplateConfig {
/**
* initialise restTemplate
*
* #param restTemplateInterceptor autowired in RestTemplateInterceptor
* #return
*/
#Bean
public RestTemplate restTemplate(ClientHttpRequestInterceptor restTemplateInterceptor, ObjectMapper objectMapper) {
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(restTemplateInterceptor);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
Here is my WebMVC file
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Bean
public WebMvcConfigurer webAuthentication() {
return new WebMvcConfigurer() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
//registry.addInterceptor(myInterceptor());
registry.addInterceptor(new MVCLoggingInterceptor()).addPathPatterns("/api/**");
registry.addInterceptor(new WebAuthentication()).addPathPatterns("/api/**/");
}
};
}
}
Here is my application file
#EnableAsync
#EnableScheduling
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class XManagementApplication {
public static void main(String[] args) {
SpringApplication.run(XManagementApplication.class, args);
}
}
Can anybody tell why my interceptor class is not called when I try to call any API
Any help would be appreciate?
I don't really understand why you want to instantiate your RestTemplateInterceptor as a Bean. Why not simply instantiate your interceptor inside the method RestTemplateConfig.restTemplate() ?
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RestTemplateInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
Btw, why do you need to pass RequestContext to the constructor of your interceptor ?
I use Spring Cloud's ResourceLoader to access S3, e.g.:
public class S3DownUpLoader {
private final ResourceLoader resourceLoader;
#Autowired
public S3DownUpLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public String storeOnS3(String filename, byte[] data) throws IOException {
String location = "s3://" + bucket + "/" + filename;
WritableResource writeableResource = (WritableResource) this.resourceLoader.getResource(location);
FileCopyUtils.copy( data, writeableResource.getOutputStream());
return filename;
}
It works okey and I need help to test the code with Localstack/Testcontainers. I've tried following test, but it does not work - my production profile gets picked up(s3 client with localstack config is not injected):
#RunWith(SpringRunner.class)
#SpringBootTest
public class S3DownUpLoaderTest {
#ClassRule
public static LocalStackContainer localstack = new LocalStackContainer().withServices(S3);
#Autowired
S3DownUpLoader s3DownUpLoader;
#Test
public void testA() {
s3DownUpLoader.storeOnS3(...);
}
#TestConfiguration
#EnableContextResourceLoader
public static class S3Configuration {
#Primary
#Bean(destroyMethod = "shutdown")
public AmazonS3 amazonS3() {
return AmazonS3ClientBuilder
.standard()
.withEndpointConfiguration(localstack.getEndpointConfiguration(S3))
.withCredentials(localstack.getDefaultCredentialsProvider())
.build();
}
}
}
as we discussed on GitHub,
We solve this problem in a slightly different way. I've actually never seen the way you use the WritableResource, which looks very interesting. None the less, this is how we solve this issue:
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.profiles.active=test")
#ContextConfiguration(classes = AbstractAmazonS3Test.S3Configuration.class)
public abstract class AbstractAmazonS3Test {
private static final String REGION = Regions.EU_WEST_1.getName();
/**
* Configure S3.
*/
#TestConfiguration
public static class S3Configuration {
#Bean
public AmazonS3 amazonS3() {
//localstack docker image is running locally on port 4572 for S3
final String serviceEndpoint = String.format("http://%s:%s", "127.0.0.1", "4572");
return AmazonS3Client.builder()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(serviceEndpoint, REGION))
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("dummyKey", "dummySecret")))
.build();
}
}
}
And a sample test:
public class CsvS3UploadServiceIntegrationTest extends AbstractAmazonS3Test {
private static final String SUCCESS_CSV = "a,b";
private static final String STANDARD_STORAGE = "STANDARD";
#Autowired
private AmazonS3 s3;
#Autowired
private S3ConfigurationProperties properties;
#Autowired
private CsvS3UploadService service;
#Before
public void setUp() {
s3.createBucket(properties.getBucketName());
}
#After
public void tearDown() {
final String bucketName = properties.getBucketName();
s3.listObjects(bucketName).getObjectSummaries().stream()
.map(S3ObjectSummary::getKey)
.forEach(key -> s3.deleteObject(bucketName, key));
s3.deleteBucket(bucketName);
}
#Test
public void uploadSuccessfulCsv() {
service.uploadSuccessfulCsv(SUCCESS_CSV);
final S3ObjectSummary s3ObjectSummary = getOnlyFileFromS3();
assertThat(s3ObjectSummary.getKey(), containsString("-success.csv"));
assertThat(s3ObjectSummary.getETag(), is("b345e1dc09f20fdefdea469f09167892"));
assertThat(s3ObjectSummary.getStorageClass(), is(STANDARD_STORAGE));
assertThat(s3ObjectSummary.getSize(), is(3L));
}
private S3ObjectSummary getOnlyFileFromS3() {
final ObjectListing listing = s3.listObjects(properties.getBucketName());
final List<S3ObjectSummary> objects = listing.getObjectSummaries();
assertThat(objects, iterableWithSize(1));
return Iterables.getOnlyElement(objects);
}
}
And the code under test:
#Service
#RequiredArgsConstructor
#EnableConfigurationProperties(S3ConfigurationProperties.class)
public class CsvS3UploadServiceImpl implements CsvS3UploadService {
private static final String CSV_MIME_TYPE = CSV_UTF_8.toString();
private final AmazonS3 amazonS3;
private final S3ConfigurationProperties properties;
private final S3ObjectKeyService s3ObjectKeyService;
#Override
public void uploadSuccessfulCsv(final String source) {
final String key = s3ObjectKeyService.getSuccessKey();
doUpload(source, key, getObjectMetadata(source));
}
private void doUpload(final String source, final String key, final ObjectMetadata metadata) {
try (ReaderInputStream in = new ReaderInputStream(new StringReader(source), UTF_8)) {
final PutObjectRequest request = new PutObjectRequest(properties.getBucketName(), key, in, metadata);
amazonS3.putObject(request);
} catch (final IOException ioe) {
throw new CsvUploadException("Unable to upload " + key, ioe);
}
}
private ObjectMetadata getObjectMetadata(final String source) {
final ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(CSV_MIME_TYPE);
metadata.setContentLength(source.getBytes(UTF_8).length);
metadata.setContentMD5(getMD5ChecksumAsBase64(source));
metadata.setSSEAlgorithm(SSEAlgorithm.KMS.getAlgorithm());
return metadata;
}
private String getMD5ChecksumAsBase64(final String source) {
final HashCode md5 = Hashing.md5().hashString(source, UTF_8);
return Base64.getEncoder().encodeToString(md5.asBytes());
}
}
It seems the only way to provide custom amazonS3 bean for ResourceLoader is to inject it manually. The test looks like
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = S3DownUpLoaderTest.S3Configuration.class)
public class S3DownUpLoaderTest implements ApplicationContextAware {
private static final String BUCKET_NAME = "bucket";
#ClassRule
public static LocalStackContainer localstack = new LocalStackContainer().withServices(S3);
#Autowired
S3DownUpLoader s3DownUpLoader;
#Autowired
SimpleStorageProtocolResolver resourceLoader;
#Autowired
AmazonS3 amazonS3;
#Before
public void setUp(){
amazonS3.createBucket(BUCKET_NAME);
}
#Test
public void someTestA() throws IOException {
....
}
#After
public void tearDown(){
ObjectListing object_listing = amazonS3.listObjects(QLM_BUCKET_NAME);
while (true) {
for (S3ObjectSummary summary : object_listing.getObjectSummaries()) {
amazonS3.deleteObject(BUCKET_NAME, summary.getKey());
}
// more object_listing to retrieve?
if (object_listing.isTruncated()) {
object_listing = amazonS3.listNextBatchOfObjects(object_listing);
} else {
break;
}
};
amazonS3.deleteBucket(BUCKET_NAME);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (applicationContext instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
configurableApplicationContext.addProtocolResolver(this.resourceLoader);
}
}
public static class S3Configuration {
#Bean
public S3DownUpLoader s3DownUpLoader(ResourceLoader resourceLoader){
return new S3DownUpLoader(resourceLoader);
}
#Bean(destroyMethod = "shutdown")
public AmazonS3 amazonS3() {
return AmazonS3ClientBuilder
.standard()
.withEndpointConfiguration(localstack.getEndpointConfiguration(S3))
.withCredentials(localstack.getDefaultCredentialsProvider())
.build();
}
#Bean
public SimpleStorageProtocolResolver resourceLoader(){
return new SimpleStorageProtocolResolver(amazonS3());
}
}
I have this entity class
#Entity
#XmlRootElement
#Table(name="user")
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id_user", unique=true, nullable=false)
private String idUser;
#Column(nullable=false, length=50)
private String docnum;
#Column(nullable=false, length=50)
private String email;
#Column(nullable=false, length=50)
private String firstname;
#Column(nullable=false, length=50)
private String lastname;
#Column(nullable=false, length=45)
private String pwd;
//bi-directional many-to-many association to Transaction
#ManyToMany
#JoinTable(
name="transaction_users"
, joinColumns={
#JoinColumn(name="user", nullable=false)
}
, inverseJoinColumns={
#JoinColumn(name="transaction", nullable=false)
}
)
private List<Transaction> transactions;
public User() {
}
public String getIdUser() {
return this.idUser;
}
public void setIdUser(String idUser) {
this.idUser = idUser;
}
public String getDocnum() {
return this.docnum;
}
public void setDocnum(String docnum) {
this.docnum = docnum;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstname() {
return this.firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return this.lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public List<Transaction> getTransactions() {
return this.transactions;
}
public void setTransactions(List<Transaction> transactions) {
this.transactions = transactions;
}
}
generated from a database table. Then i have this rest service
#Path("service/2.0")
public class ServiceTest {
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_JSON)
public Response getUser() {
EntityManager entityManager = EntityManagerUtil.getEntityManager();
entityManager.getTransaction().begin();
Query q = entityManager.createQuery("SELECT u FROM User u");
#SuppressWarnings("unchecked")
List<User> listOfUser = q.getResultList();
System.out.print(listOfUser);
return Response.ok(listOfUser).build();
}
I'm (supposed) to be using the jackson API to handle json but i'm not using maven. For this reason, i've added in my buildpath the following .jars:
jackson-annotations-2.9.3.jar
jackson-core-2.9.3.jar
jackson-databind-2.9.3.jar
jackson-jaxrs-base-2.9.3.jar
jackson-module-jaxb-annotations-2.9.3.jar
jersey-media-json-jackson-2.26.jar
jackson-jaxrs-json-provider-2.9.3.jar
Then i have an ApplicationConfig.java class
package prova;
import com.fasterxml.jackson.jaxrs.json.*;
import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.ApplicationPath;
#ApplicationPath("rest")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("com.fasterxml.jackson.jaxrs.json");
packages("prova");
}
}
When i try to submit a GET request with postman, i obtain an "HTTP 500 internal server error" with the description:
"The server encountered an unexpected condition that prevented it from fulfilling the request."
While from the eclipse console i can see
[EL Fine]: sql: 2017-12-16 17:44:54.251--ServerSession(1869059368)--
Connection(771012214)--Thread(Thread[http-nio-8080-exec-80,5,main])--
SELECT id_user, DOCNUM, EMAIL, FIRSTNAME, LASTNAME, PWD FROM user
[prova.User#3c713cb0, prova.User#49e51730, prova.User#d9ecdd7,
prova.User#383fe468]dic 16, 2017 5:44:54
PM org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWr
iterInterceptor aroundWriteTo
GRAVE: MessageBodyWriter not found for media type=application/json,
type=class java.util.Vector, genericType=class java.util.Vector.
So what i can deduce is that the query is correctly executed and it returns an array of 4 object (prova is the name of my entity manager) but then i have the GRAVE:MessageBodyWriter Error
What the hell i'm not doing correctly?why i can not retrieve my JSON data?
Thanks
UPDATE
Following the advice, i've modified the GET resource into
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String getUser() {
return String.valueOf(10+4);
}
and submitting the GET request gives me the expected JSON answer from Postman "14"....
Can the problem be the conversion of a List into Json?if yes, what to do?
Thanks
UPDATE 2
I've edited the code of the REST resource in this way:
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String getUser() {
EntityManager entityManager = EntityManagerUtil.getEntityManager();
entityManager.getTransaction().begin();
Query q = entityManager.createQuery("SELECT u FROM User u");
List<User> listOfUser = q.getResultList();
System.out.print(listOfUser);
if (listOfUser.isEmpty()) {
System.out.println("VOID LIST");
entityManager.close();
return String.valueOf(2);
}
for (User user : listOfUser) {
System.out.println(user.getFirstname());
System.out.println("---");
}
return String.valueOf(3);
}
The postman Output is "3" so, everything fine while the consoloe output is:
[EL Fine]: sql: 2017-12-17 13:48:33.214--ServerSession(286337061)--
Connection(2132504260)--Thread(Thread[http-nio-8080-exec-2,5,main])--
SELECT id_user, DOCNUM, EMAIL, FIRSTNAME, LASTNAME, PWD FROM USER
[prova.User#2d3017ff, prova.User#6361d00, prova.User#7ab0944a,
prova.User#5945162f]
matteo
---
tony
---
bruce
---
peter
---
which is perfectly consistent with what i have in the table of the DB... :(
Try to convert your response entity in an array of users. There is proper equivalent for array in json.
Other way ist to have a wrapper class for your list of users.
#XmlType
#XmlRootElement
class Wrapper {
#XmlElement
List<User> users;
}
Return this in your response.
I am trying to build Spring Boot test to test rest API, so that I can get Principal from the request and use that to identify the user.
Server returns
{"timestamp":1502014507361,"status":403,"error":"Forbidden","message":"Access
Denied","path":"/hello"}
What am I missing here?
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RestTemplateTest {
#Autowired
TestRestTemplate testRestTemplate;
#Test
public void testit() {
testRestTemplate.withBasicAuth("user", "password");
String responsePayload = testRestTemplate.getForObject("/hello", String.class);
}
#RestController
public class GreetingController {
#RequestMapping("/hello")
public String heipat(Principal principal) {
String string = "hello there";
return string;
}
#Configuration
#EnableWebSecurity
static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().anyRequest().hasRole("USER");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
You need to be authenticated first. like requesting a /login API.
Also you need to make the login API accessible by everyone by doing this:
http.csrf().disable().authorizeRequests()
.antMatchers("/login").permitAll()
When you includes WebSecurityConfig you will have basic usernamerAndPassowrd authentication.