I use mapstruct in my projects and it works fine for the straight forward way (All mapper in one package).
Now I have the requirement to move one mapper to another package, but this doesn't work well.
working package structure (1):
de.zinnchen
├── dto
│ └── Car.java
├── entity
│ └── CarEntity.java
└── mapper
└── a
├── CarMapper.java
└── DateMapper.java
NOT working package structure (2):
de.zinnchen
├── dto
│ └── Car.java
├── entity
│ └── CarEntity.java
└── mapper
├── a
│ └── CarMapper.java
└── b
└── DateMapper.java
my java files:
package de.zinnchen.dto;
import java.time.LocalDateTime;
public class Car {
private String manufacturer;
private String model;
private String color;
private LocalDateTime productionDate;
...
package de.zinnchen.entity;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
public class CarEntity {
private String manufacturer;
private String model;
private String color;
private Instant productionDate;
...
package de.zinnchen.mapper.a;
import de.zinnchen.dto.Car;
import de.zinnchen.entity.CarEntity;
import de.zinnchen.mapper.b.DateMapper;
import org.mapstruct.Mapper;
#Mapper(
uses = DateMapper.class
)
public interface CarMapper {
Car asDto(CarEntity entity);
CarEntity asEntity(Car car);
}
package de.zinnchen.mapper.b;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
public class DateMapper {
LocalDateTime instantToLocalDateTime(Instant instant) {
return instant
.atZone(ZoneId.of("UTC"))
.toLocalDateTime();
}
Instant LocalDateTimeToInstant(LocalDateTime localDateTime) {
return localDateTime.toInstant(ZoneOffset.UTC);
}
}
Once I try to compile the variant with mappers in different packages I get the following error message:
Can't map property "Instant productionDate" to "LocalDateTime productionDate". Consider to declare/implement a mapping method: "LocalDateTime map(Instant value)".
Can you please help me for solving this problem?
Edit
Resulting CarMapperImpl.java of package structure 1:
package de.zinnchen.mapper.a;
import de.zinnchen.dto.Car;
import de.zinnchen.entity.CarEntity;
import javax.annotation.processing.Generated;
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-01-06T09:36:43+0100",
comments = "version: 1.4.1.Final, compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)"
)
public class CarMapperImpl implements CarMapper {
private final DateMapper dateMapper = new DateMapper();
#Override
public Car asDto(CarEntity entity) {
if ( entity == null ) {
return null;
}
Car car = new Car();
car.setManufacturer( entity.getManufacturer() );
car.setModel( entity.getModel() );
car.setColor( entity.getColor() );
car.setProductionDate( dateMapper.instantToLocalDateTime( entity.getProductionDate() ) );
return car;
}
#Override
public CarEntity asEntity(Car car) {
if ( car == null ) {
return null;
}
CarEntity carEntity = new CarEntity();
carEntity.setManufacturer( car.getManufacturer() );
carEntity.setModel( car.getModel() );
carEntity.setColor( car.getColor() );
carEntity.setProductionDate( dateMapper.LocalDateTimeToInstant( car.getProductionDate() ) );
return carEntity;
}
}
The reason why it isn't working is due to the fact that the methods in the DateMapper are package protected and are not available from other packages.
So if you add public to the methods then it will work.
Related
I have a custom annotation in Java, that uses ByteBuddy to generate a new class based on the annotated one. Since ByteBuddy is already compiling the class, I am outputting the bytecode directly, rather than source.
My gradle build compiles fine, with references to the generated class.
The problem is that in IntelliJ, the editor does not recognize the classes that are output directly. It does recognize classes generated as source.
My Annotation sub-project
gradle.build
plugins {
id 'java-library'
id 'maven-publish'
}
group 'org.example'
version 'unspecified'
repositories {
mavenCentral()
}
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.12.23'
}
MyAnnotation.java
package org.example.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.CLASS)
public #interface MyAnnotation {}
MyAnnotationProcessor.java
package org.example.annotation.processor;
import net.bytebuddy.ByteBuddy;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.io.IOException;
import java.util.Set;
#SupportedAnnotationTypes({"org.example.annotation.MyAnnotation"})
#SupportedSourceVersion(SourceVersion.RELEASE_11)
public class MyAnnotationProcessor extends AbstractProcessor {
Filer filer;
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
annotations.forEach(a -> {
try {
// Generate a java source file
var className = a.getSimpleName()+"_source";
var fooFile = filer.createSourceFile(className);
var writer = fooFile.openWriter();
writer.write("package org.example.annotation; public class "+className+" {}");
writer.close();
// Generate a java class file
var otherClassName = a.getSimpleName()+"_bytecode";
var bb = new ByteBuddy();
var dtoClass = bb.subclass(Object.class)
.name(otherClassName)
.make();
var javaFileObject = filer.createClassFile(otherClassName);
var dtoOutStr = javaFileObject.openOutputStream();
dtoOutStr.write(dtoClass.getBytes());
dtoOutStr.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
return false;
}
}
Notice that I am outputting one class called MyAnnotation_source and one called MyAnnotation_bytecode
My sub-project that uses the annotation
gradle.build (NOTE that core is the name of the annotation sub-project)
plugins {
id 'java'
}
group 'org.example'
version 'unspecified'
repositories {
mavenCentral()
}
dependencies {
implementation project(':core')
annotationProcessor project(':core')
}
UserTest.java
import org.example.annotation.MyAnnotation;
import org.example.annotation.MyAnnotation_source;
import org.example.annotation.MyAnnotation_bytecode; //Cannot resolve symbol 'MyAnnotation_bytecode'
#MyAnnotation
public class UserTest {
public static void main(String[] args) {
MyAnnotation_source foo = new MyAnnotation_source();
MyAnnotation_bytecode other = new MyAnnotation_bytecode(); //Cannot resolve symbol 'MyAnnotation_bytecode'
}
}
The build directory ends up looking like this:
build/
├─ classes
│ └─ java
│ └─ main
│ ├─ MyAnnotation_bytecode.class <-- Generated
│ ├── UserTest.class
│ └── org
│ └─ example
│ └─ annotation
│ └─ MyAnnotation_source.class <-- Compiled
└─ generated
└─ sources
└─ annotationProcessor
└─ java
└─ main
└─ MyAnnotation_source.java <-- Generated
I have already tried various configurations of the annotation processor settings in IntelliJ, but I think the answer is somewhere else.
OK, before the downvotes come here is how this question is different from:
Repository Injection not been recognized as bean: Annotating the main class to make it scan the base package does not work for me.
'Field required a bean of type that could not be found.' error spring restful API using mongodb: well, I'm not using MongoDB, but even if I was, the answers to that question do not solve my problem. My packages are set up correctly, I tried the #Service annotation, made sure IntelliJ imported the right Service, tried annotating the controller with #Component, made sure there's no 2 beans with the same name, renamed the Bean Spring refused to see, tried clearing the cache of IntelliJ, restarted my machine, nothing.
D:.
│ .classpath
│ .project
│ DiscordConfApplication.java
│
├───bin
├───controller
│ Controller.java
│ DiscordUserController.java
│
├───entity
│ DiscordEntity.java
│ DiscordUser.java
│ Guild.java
│ Member.java
│ Permissions.java
│ Role.java
│ Settings.java
│
└───service
DiscordUserRepo1.java
DiscordUserService.java
This is the structure of my project. com.example.discordconf is defined as package and it contains bin, controller, entity and service.
When I try to run my Spring app I get this error:
APPLICATION FAILED TO START
***************************
Description:
Field discordUserRepo1 in com.example.discordconf.service.DiscordUserService required a bean of type 'com.example.discordconf.service.DiscordUserRepo1' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.example.discordconf.service.DiscordUserRepo1' in your configuration.
Here is DiscordUserRepo1.java itself:
import com.example.discordconf.entity.DiscordUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DiscordUserRepo1 extends JpaRepository<DiscordUser, Integer> {
//Optional<DiscordUser> findById(Integer integer);
}
DiscordUserService.java
import com.example.discordconf.entity.DiscordUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
#Service
public class DiscordUserService {
#Autowired
DiscordUserRepo1 discordUserRepo1;
public void addNewDiscordUser(DiscordUser discordUser) {
discordUserRepo1.save(discordUser);
}
public List<DiscordUser> getAllDiscordUsers() {
return discordUserRepo1.findAll();
}
public Optional<DiscordUser> getById(int id) {
return discordUserRepo1.findById(id);
}
}
Controller.java
import com.example.discordconf.entity.DiscordUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.example.discordconf.service.DiscordUserService;
#RestController
public class Controller {
#Autowired
DiscordUserService discordUserService;
#PostMapping("/adduser")
public void addNewDiscordUSer(#RequestBody DiscordUser discordUser) {
discordUserService.addNewDiscordUser(discordUser);
}
}
DiscordUserController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class DiscordUserController {
#GetMapping("/")
public String home() {
return "<h1>Welcome to Discord Conf!</h1>";
}
}
DiscordConfApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan(basePackages="com.example.discordconf")
//#EnableJpaRepositories(basePackages = {"com.example.discordconf.Service"})
public class DiscordConfApplication {
public static void main(String[] args) {
SpringApplication.run(DiscordConfApplication.class, args);
}
}
What am I supposed to do? I've been trying to solve this for days and it makes me wanna tear my hair out.
I think you need to add #Repository to the DiscordUserRepo1 class
I want to expose an additional Rest endpoint along a Websocket endpoint.
The websocket endpoint is already implemented and works, but I have trouble hitting the Rest endpoint.
Any idea what I'm missing here?
I expect to run localhost:8080/hello with a text plain result of hello.
Btw, I'm using Quarkus if that matters.
Here is my project structure
│ │ ├── java
│ │ │ └── org
│ │ │ └── company
│ │ │ ├── chat
│ │ │ │ ├── boundary
│ │ │ │ ├── control
│ │ │ │ └── entity
│ │ │ ├── JAXRSConfiguration.java // Rest endpoint ???
│ │ │ └── websockets
│ │ │ └── ChatSocket.java // Websocket server endpoint (works)
│ │ └── resources
│ │ ├── application.properties
│ │ ├── application.properties.example
│ │ └── META-INF
│ │ └── resources
│ │ ├── index.html
JAXRSConfiguration.java
package org.company;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
#ApplicationPath("hello")
public class JAXRSConfiguration extends Application {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}
}
ChatSocket.java
package org.company.websockets;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.time.LocalDateTime;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.company.chat.boundary.ChatResource;
import org.company.chat.entity.Chat;
import org.company.chat.entity.ChatGroup;
import org.company.chat.entity.ChatMessage;
import org.company.time.Time;
import org.jboss.logging.Logger;
#ServerEndpoint("/chat/websocket/{username}/{chatRoom}")
#ApplicationScoped
public class ChatSocket {
#Inject
Time time;
#Inject
ChatResource chatResource;
// Chatroom, user, session dictionary
final Dictionary<String, Map<String, Session>> chatRooms = new Hashtable<>();
#OnOpen
public void onOpen(final Session session, #PathParam("username") final String username, #PathParam("chatRoom") final String chatRoom) {
// ...
}
#OnClose
public void onClose(final Session session, #PathParam("username") final String username, #PathParam("chatRoom") final String chatRoom)
throws Exception {
// ...
}
#OnError
public void onError(final Session session, #PathParam("username") final String username, #PathParam("chatRoom") final String chatRoom,
final Throwable throwable) throws Exception {
// ...
}
#OnMessage
public void onMessage(String json, #PathParam("username") final String username, #PathParam("chatRoom") final String chatRoom)
throws Exception {
// ...
}
private void broadcast(final String event, final String chatRoom) {
// ...
}
The solution was simply to add another #Path annotation.
Here I changed JAXRSConfiguration.java as you can see in the following.
Works now, if I call the localhost:8080/api/hello endpoint.
#ApplicationPath("api") // Changed the basic route to 'api'
#Path("hello") // Added a new path
public class JAXRSConfiguration extends Application {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}
}
Trying to move files from a sub-directory along with the structure to a parent directory. And am not able to accomplish this using Files.move(). To illustrate the issue please see the below directory structure.
$ tree
.
└── b
├── c
│ ├── cfile.gtxgt
│ └── d
│ ├── dfile.txt
│ └── e
└── x
└── y
└── z
├── 2.txt
└── p
├── file1.txt
└── q
├── file
├── file2.txt
└── r
└── 123.txt
I want to emulate the below move command via Java.
$ mv b/x/y/z/* b/c
b/x/y/z/2.txt -> b/c/2.txt
b/x/y/z/p -> b/c/p
And the output should be something similar to
$ tree
.
└── b
├── c
│ ├── 2.txt
│ ├── cfile.gtxgt
│ ├── d
│ │ ├── dfile.txt
│ │ └── e
│ └── p
│ ├── file1.txt
│ └── q
│ ├── file
│ ├── file2.txt
│ └── r
│ └── 123.txt
└── x
└── y
└── z
In this move all the files and directories under directory z have been moved to c.
I have tried to do this:
public static void main(String[] args) throws IOException{
String aPath = "/tmp/test/a/";
String relativePathTomove = "b/x/y/z/";
String relativePathToMoveTo = "b/c";
Files.move(Paths.get(aPath, relativePathTomove), Paths.get(aPath, relativePathToMoveTo), StandardCopyOption.REPLACE_EXISTING);
}
However this causes this exception to the thrown java.nio.file.DirectoryNotEmptyException: /tmp/test/a/b/c and if the the REPLACE_EXISTING option is taken out the code throws a java.nio.file.FileAlreadyExistsException: /tmp/test/a/b/c.
This question has an answer that uses a recursive function to solve this problem. But in my case it will involve further complexity as I need to even re-created the sub-dir structure in the new location.
I have not tried the option of using the commons-io utility method org.apache.commons.io.FileUtils#moveDirectoryToDirectory as this code seems to be first copying files and then deleting them from the original location. And In my case the files are huge and hence this is not a preferred option.
How can I achieve the the move functionality in java without resorting to copying. Is individual file move my only option?
TLDR: How can I emulate the mv functionality in java for moving sub dir with files and structure to parent directory.
I ended up doing this:
Create a FileVisitor Implementation like so:
package com.test.files;
import org.apache.log4j.Logger;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Objects;
import static java.nio.file.FileVisitResult.TERMINATE;
public class MoveFileVisitor implements FileVisitor<Path> {
private static final Logger LOGGER = Logger.getLogger(MoveFileVisitor.class);
private final Path target;
private final Path source;
public MoveFileVisitor(#NotNull Path source, #NotNull Path target) {
this.target = Objects.requireNonNull(target);
this.source = Objects.requireNonNull(source);
}
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path relativePath = source.relativize(dir);
Path finalPath = target.resolve(relativePath);
Files.createDirectories(finalPath);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Path relativePath = source.relativize(file);
Path finalLocation = target.resolve(relativePath);
Files.move(file, finalLocation);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
LOGGER.error("Failed to visit file during move" + file.toAbsolutePath(), exc);
return TERMINATE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
}
And then walking the path with this Visitor like so:
String source = "/temp/test/a/b/x/y/z";
String target = "/temp/test/a/b/c";
MoveFileVisitor visitor = new MoveFileVisitor(Paths.get(source), Paths.get(target));
Files.walkFileTree(Paths.get(source), visitor);
For the following classes Texts ...
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.Collections;
import java.util.List;
import hrisey.Parcelable;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#Parcelable
public final class Texts implements android.os.Parcelable {
#NonNull List<Text> texts = Collections.emptyList();
public boolean hasTexts() {
return !texts.isEmpty() && textsHaveValues();
}
private boolean textsHaveValues() {
for (Text text : texts) {
if (TextUtils.isEmpty(text.getValue())) {
return false;
}
}
return true;
}
}
... and Text ...
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import hrisey.Parcelable;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#Parcelable
public final class Text implements android.os.Parcelable {
private String textKey;
private String value;
}
... I wrote this unit test:
#RunWith(JUnit4.class)
public class TextsTest {
private Texts texts;
#Before
public void setUp() {
texts = new Texts();
}
#Test
public void hasTextsWithSingleEmptyItem() throws Exception {
texts.setTexts(Collections.singletonList(new Text()));
assertThat(texts.hasTexts()).isFalse();
}
}
The test succeeds in Android Studio 2.1.3 but it fails when I run ./gradlew clean test on my machine (MacOS 10.11.6, El Capitain, Java 1.7.0_79). Here is the error output:
com.example.model.TextsTest > hasTextsWithSingleEmptyItem FAILED
org.junit.ComparisonFailure: expected:<[fals]e> but was:<[tru]e>
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(
NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(
DelegatingConstructorAccessorImpl.java:45)
at com.example.model.TextsTest.hasTextsWithSingleEmptyItem(TextsTest.java:31)
Related posts
creating instance of object using reflection , when the constructor takes an array of strings as argument
IllegalArgumentException with constructing class using reflection and array arguments
How do you mock TextUtils? The part TextUtils.isEmpty(text.getValue()) should always be false when using the default Android test stubs.
Be sure to use a suitable implementation or consider using a different set of string utilities you already might have available with some other dependencies.
Edit by JJD
You are right, thanks! I use the Unmock plugin. So I had to unmock the relevant package to expose TextUtils in the unit tests:
unMock {
keepStartingWith "android.text."
}