Hateoas - No suitable constructor found for Link(java.lang.String) - spring-boot

For a REST API, in the controller I'm applying hateoas. When adding the part of Link in the methods, I get the follow error:
Cannot resolve constructor 'Link(String)'
In the pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
The code is as follows:
#GetMapping
public #ResponseBody ResponseEntity<List<UserResponseDTO>> get() {
// Retrieve users
List<UserResponseDTO> responseDTOS = new ArrayList<>();
List<User> users = userService.getUsers();
// Convert to responseDTOS
for (User user : users) {
UserResponseDTO userResponseDTO = new UserResponseDTO(user.getId(), user.getFirstName(), user.getLastName());
Link get = new Link("http://localhost:8081/user/").withRel("GET");
userResponseDTO.add(get);
responseDTOS.add(userResponseDTO);
}
return new ResponseEntity<>(responseDTOS, HttpStatus.OK);
}
Does anyone know how to solve this?

Link(String) is deprecated and may be removed in some new version. Also Link(String) uses the protected access modifier meaning you should access it only from the same package.
You can still create the Link using the of static method which by the way is defined with public access modifier.
So it should be
Link get = Link.of("http://localhost:8081/user/").withRel("GET");

Related

Transactions in R2BC for MYSQL using dev.miku

I was trying to implement simple transaction mechanism just to check how it works in reactive world using dev.miku for Mysql but getting below error
Receiver class dev.miku.r2dbc.mysql.MySqlConnection does not define or inherit an implementation of the resolved method 'abstract org.reactivestreams.Publisher beginTransaction(io.r2dbc.spi.TransactionDefinition)'
Code I have written is as below
#Autowired
private TransactionalOperator operator;
public Mono<ServerResponse> insertUserData(ServerRequest serverRequest) {
return serverRequest.bodyToMono(UserAndFamilyRequest.class)
.flatMap(saveFamilyMembers())
.as(operator::transactional) //using transactional operator
.flatMap(familyMemberEntity -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(familyMemberEntity));
}
private Function<UserAndFamilyRequest, Mono<List<FamilyMemberEntity>>> saveFamilyMembers() {
return userAndFamilyRequest -> {
User user = userAndFamilyRequest.getUser();
UserEntity userEntity = new UserEntity();
userEntity.setName(user.getName());
userEntity.setAge(user.getAge());
userEntity.setSeats(user.getSeats());
userRepo.save(userEntity);
List<FamilyMember> list = userAndFamilyRequest.getFamilyMemberList();
var entityList = list.stream().map(familyMember -> {
FamilyMemberEntity familyMemberEntity = new FamilyMemberEntity();
familyMemberEntity.setName(familyMember.getName());
familyMemberEntity.setAge(familyMember.getAge());
return familyMemberEntity;
}).collect(Collectors.toList());
return familyRepo.saveAll(entityList).collect(Collectors.toList());
};
}
I have tried annotation the method insertUserData with #Transactional but getting the same error.
Using below dependency
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<version>0.8.2.RELEASE</version>
</dependency>
Do I need to import some other dependency for my purpose, or Am i doing something wrong

Images not getting saved under resources directory spring boot mvc application

I am trying to save image files under a directory that resides in project's resources folder, below code doesn't give any error neither it saves file to the mentioned location, i know saving file to the resources is a bad idea but just giving it a try will move to upload file to S3 bucket later on.
#PostMapping("/add")
public String addBook(#ModelAttribute("book") Book book, HttpServletRequest request) throws IOException {
bookService.save(book);
MultipartFile bookImage = book.getBookImage();
try {
byte[] bytes = bookImage.getBytes();
String bookName = book.getId() + ".png";
BufferedOutputStream bf = new BufferedOutputStream(new FileOutputStream(new File(
"src/main/resources/static/image/books/"+bookName
)));
bf.write(bytes);
bf.close();
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:bookList";
}
Based on your question and comments above. Also as you are thinking of moving from filesystem storage to S3 then you may want to take a look at community project called [Spring Content][1]. This project allows you to manage content (i.e. your generated book images) and associate them with your Spring Data Entities. It provides the same programming model as Spring Data, just for unstructured content like files, images, videos etc.
For example, assuming you are using Spring Data, you could add this to your projects as follows.
pom.xml (for Spring Web MVC. Spring Boot Starters also available)
<!-- Spring dependencies -->
...
<!-- Java API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-fs</artifactId>
<version>1.0.0.M11</version>
</dependency>
<!-- REST API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>1.0.0.M11</version>
</dependency>
StoreConfig.java
#Configuration
#EnableFilesystemStores
#Import(RestConfiguration.class)
public class EnableFilesystemStoresConfig {
#Bean
File filesystemRoot() throws IOException {
return new File("/path/to/your/book/images");
}
#Bean
FileSystemResourceLoader fileSystemResourceLoader() {
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
}
}
BookImageStore.java
#StoreRestResource(path="bookImages")
public interface BookImageStore extends ContentStore<Book, String> {
}
And to add Spring Content-annotated fields to your Spring Data entities, like this:
Book.java
#Entity
public class Book {
#Id
#GeneratedValue
private long id;
...other existing fields...
#ContentId
private String contentId;
#ContentLength
private long contentLength = 0L;
#MimeType
private String mimeType;
...
}
This is all you need. When your application starts it will see the `spring-content-fs` dependency on your classpath and the BookImageStore interface and it will inject a file-based implementation. Moreover, it will see the spring-content-rest dependency and inject an #Controller implementation providing a REST API for handling your book images that will forward REST calls onto the BookStoreImage so you dont have to worry about implementing any of this yourself.
So:
`POST /bookImages/{bookId} -F "image=#/some/path/to/an/image.jpg"`
will upload `image.jpg` to `/path/to/your/uploaded/images/` and update the fields on the Book entity so the content is associated.
`GET /bookImages/{bookId}` -H 'Accept: image/jpeg'
will fetch it again.
A couple of additional notes;
- I would strongly recommend that you use a path outside of src/resources so that have more flexibility with how you deploy your war/jar
- if later on you want to change the backend storage from the filesystem to s3 all you would have to do it switch out the `spring-content-fs` dependency for `spring-content-s3` and update the StoreConfig to provide an S3 client bean instead of a FilesystemResourceLoader bean.
HTH
[1]: https://paulcwarren.github.io/spring-content/

Multipart File to file error

I want to upload a multipart file to AWS S3. So, i have to convert it.
But new File method needs a local location to get the file.
I am able to do in local. But running this code in every machine seems like a issue.
Please find both scenarios.
Working
private File convertMultiPartToFile(MultipartFile multipartFile) throws IOException {
File convFile = new File("C:\\Users\\" + multipartFile.getOriginalFilename());
multipartFile.transferTo(convFile);
return convFile;
}
Not working
private File convertMultiPartToFile(MultipartFile multipartFile) throws IOException {
File convFile = new File(multipartFile.getOriginalFilename());
multipartFile.transferTo(convFile);
return convFile;
}
Error received :
java.io.FileNotFoundException: newbusiness.jpg (Access is denied)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at java.io.FileOutputStream.<init>(FileOutputStream.java:162)
You could use Spring Content S3. This will hide the implementation details so you don't need to worry about them.
There are Spring Boot starter alternatives but as you are not using Spring Boot add the following dependency to your pom.xml
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-s3</artifactId>
<version>0.0.11</version>
</dependency>
Add the following configuration that creates a SimpleStorageResourceLoader bean:
#Configuration
#EnableS3Stores
public class S3Config {
#Autowired
private Environment env;
public Region region() {
return Region.getRegion(Regions.fromName(env.getProperty("AWS_REGION")));
}
#Bean
public BasicAWSCredentials basicAWSCredentials() {
return new BasicAWSCredentials(env.getProperty("AWS_ACCESS_KEY_ID"), env.getProperty("AWS_SECRET_KEY"));
}
#Bean
public AmazonS3 client(AWSCredentials awsCredentials) {
AmazonS3Client amazonS3Client = new AmazonS3Client(awsCredentials);
amazonS3Client.setRegion(region());
return amazonS3Client;
}
#Bean
public SimpleStorageResourceLoader simpleStorageResourceLoader(AmazonS3 client) {
return new SimpleStorageResourceLoader(client);
}
}
Create a "Store":
S3Store.java
public interface S3Store extends Store<String> {
}
Autowire this store into where you need to upload resources:
#Autowired
private S3Store store;
WritableResource r = (WritableResource)store.getResource(getId());
InputStream is = // plug your input stream in here
OutputStream os = r.getOutputStream();
IOUtils.copy(is, os);
is.close();
os.close();
When your application starts it will see the dependency on spring-content-s3 and your S3Store interface and inject an implementation for you, therefore, you don't need to worry about implementing this yourself.
IF you writing some sort of web application or microservice and you need a REST API then you can also add this dependency:
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>0.0.11</version>
</dependency>
Update your S3Config.java as follows:
#Configuration
#EnableS3Stores
#Import(RestConfiguration.class)
public class S3Config {
...
Update your store as follows:
S3Store.java
#StoreRestResource(path="s3docs")
public interface S3Store extends Store<String> {
}
Now when your application starts it will see your Store interface and also inject an #Controller implementation that will forward REST request onto your store. This replaces the autowiring code above obviously.
Then:
curl -X POST /s3docs/example-doc
with a multipart/form-data request will store the image in s3.
curl /s3docs/example-doc
will fetch it again and so on. This controller supports full CRUD and video streaming by the way.
If you want to associate this "content" with JPA Entity or something like that then you can have your S3Store extend AssociateStore or ContentStore and you have additional methods available that provide for associations.
There are a couple of getting started guides here. The s3 reference guide is here. And there is a tutorial video here. The coding bit starts about 1/2 way through.
HTH
Since it needs a temporary location to place files. Below code worked after deploying war on AWS.
private File convertMultiPartToFile(MultipartFile multipartFile) throws IOException {
File convFile = new File(System.getProperty("java.io.tmpdir") + System.getProperty("file.separator") +
multipartFile.getOriginalFilename());
multipartFile.transferTo(convFile);
return convFile;
}
You have problems with relative Paths
You can do this
public class UploadStackoverflow {
private String location = "upload-dir";
private Path rootLocation;
public File convertFile(MultipartFile file) throws IOException {
rootLocation = Paths.get(location);
Files.createDirectories(rootLocation);
String filename = StringUtils.cleanPath(file.getOriginalFilename());
InputStream inputStream = file.getInputStream();
Files.copy(inputStream, this.rootLocation.resolve(filename),
StandardCopyOption.REPLACE_EXISTING);
return new File(this.rootLocation.resolve(filename).toAbsolutePath().toString());
}
}

Spring-Boot Elasticseach EntityMapper can not be autowired

Based on this answer and the comments I implemented the code to receive the scores of an elastic search query.
public class CustomizedHotelRepositoryImpl implements CustomizedHotelRepository {
private final ElasticsearchTemplate elasticsearchTemplate;
#Autowired
public CustomizedHotelRepositoryImpl(ElasticsearchTemplate elasticsearchTemplate) {
super();
this.elasticsearchTemplate = elasticsearchTemplate;
}
#Override
public Page<Hotel> findHotelsAndScoreByName(String name) {
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
.should(QueryBuilders.queryStringQuery(name).lenient(true).defaultOperator(Operator.OR).field("name"));
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder)
.withPageable(PageRequest.of(0, 100)).build();
DefaultEntityMapper mapper = new DefaultEntityMapper();
ResultsExtractor<Page<Hotel>> rs = new ResultsExtractor<Page<Hotel>>() {
#Override
public Page<Hotel> extract(SearchResponse response) {
ArrayList<Hotel> hotels = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
try {
Hotel hotel = mapper.mapToObject(hit.getSourceAsString(), Hotel.class);
hotel.setScore(hit.getScore());
hotels.add(hotel);
} catch (IOException e) {
e.printStackTrace();
}
}
return new PageImpl<>(hotels, PageRequest.of(0, 100), response.getHits().getTotalHits());
}
};
return elasticsearchTemplate.query(nativeSearchQuery, rs);
}
}
As you can see I needed to create a new instance of DefaultEntityMapper mapper = new DefaultEntityMapper(); which should not be the case because it should be possible to #Autowire EntityMapper. If I do so, I get the exception that there is no bean.
Description:
Field entityMapper in com.example.elasticsearch5.es.cluster.repository.impl.CustomizedCluserRepositoryImpl required a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' in your configuration.
So does anybody know if its possible to autowire EntityMapper directly or does it needs to create the bean manually using #Bean annotation.
I use spring-data-elasticsearch-3.0.2.RELEASE.jar where the core package is inside.
My pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
</dependency>
I checked out the source code of spring-data-elasticsearch. There is no bean/comoponent definition for EntityMapper. It seems this answer is wrong. I test it on my project and get the same error.
Consider defining a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' in your configuration.
I couldn't find any other option by except defining a #Bean

Spring repository without database but external api

We are building an API for our service and we would like to leverage Spring Data Rest as much as possible.
This API and the new model underneath will substitute a legacy API (and it's old model) that we still need to support.
Our idea is to build an "adapter" web app that replicates the structure of the old api and serve the old model using some internal transformations.
Also the old api is using Spring Data Rest, so here the idea:
build a repository implementation that instead of querying a database will query our brand new API, retrieve the new model, apply some transformations, and return the old model.
Unfortunately, even if I'm annotating the repository implementation with the #Repository annotation, Spring is not exposing the repository in the API.
I'm not sure if this is actually something possible to do or is just a matter of me not implementing some core functionalities.
What I would like to avoid is reimplement all spring data rest methods manually in a controller.
Here my Repository class
// Method are not implemented, this is just the backbone
#Repository
public class SampleRespositoryImpl implements ReadOnlyRepository<OldSample, String> {
NewApiClient client;
public SampleRespositoryImpl(NewApiClient client) {
this.client = client;
}
#Override
public OldSample findOne(String accession) {
NewSample newSample = client.fetch(accession)
OldSample oldSample = //apply transformation to newSample
return oldSample;
}
#Override
public boolean exists(String accession) {
return client.fetch(accession) != null;
}
#Override
public Iterable<OldSample> findAll() {
return new ArrayList<>();
}
#Override
public Iterable<OldSample> findAll(Iterable<String> var1) {
return new ArrayList<>();
}
#Override
public long count() {
return 0;
}
#Override
public Iterable<OldSample> findAll(Sort var1) {
return new ArrayList<>();
}
#Override
public Page<OldSample> findAll(Pageable var1) {
List<OldSample> OldSampleList = new ArrayList<>();
Page<OldSample> page = new PageImpl<>(OldSampleList);
return page;
}
}
Here what I would like to get back when I hit the api root (http://localhost:8080/)
{
"_links": {
"samples": {
"href": "http://localhost:8080/samples{?page,size,sort}
}
}
}
Someone else linked me to another answer in StackOverflow available here as possible duplication.
Reading through that answer, I decided that is too much effort to follow this path for our needs, so I'm more oriented to create a custom controller to expose necessary methods.
This solution was reported by Kevin as answer to Implementing methods of Spring Data repository and exposing them through REST

Resources