I mean, this is what i have in my code:
#GetMapping("/get/player/{csvName}")
public void loadPlayers(#PathVariable String csvName) {
/*Irrelevant code here*/
}
This works just because the csv file is in the root of my project.
Is there any way to set the relative path of the csv file on the url?
////////////////////////////////////////////////////EDIT///////////////////////////////////////////////////////
Here is the code of the class:
#RestController
#RequestMapping("/csv")
public class CsvController {
private static final Logger log = LoggerFactory.getLogger(FutbolApplication.class);
#Autowired
private PlayerRepository playerRepository;
#Autowired
private TeamRepository teamRepository;
#Autowired
private MembershipRepository memberRepository;
#GetMapping("/get/player/{csvName}")
public void loadPlayers(#PathVariable String csvName) {
CSVReader reader;
try {
reader = new CSVReaderBuilder(new FileReader(csvName))
.withSkipLines(1).build();
String[] values;
int i;
int count=0;
while ((values = reader.readNext()) != null) {
count++;
i=0;
try {
Player player = new Player(values[i++],values[i++],values[i++],Date.valueOf(values[i++]));
System.out.println(player.getName() + "//" + player.getSurname() + "//" + player.getPosition()
+ "//" + player.getBirthDate());
playerRepository.save(player);
}catch (Exception e) {
log.error("ERROR INTENTANDO ASIGNAR LOS DATOS AL JUGADOR "+(count));
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CsvValidationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
What I can to, is to insert the path of the csv instead of just the name.
At the moment my project's structure is:
>project
>src
>main
>test
>.Settings
>mycsvfile.csv
that's why i can just type "mycsvfile.csv" in the url and it works
But this is what i'd like to get:
>project
>src
>main
>test
>.Settings
>csvs
>mycsvfile.csv
And get it to work by typing "/csvs/mycsvfile.csv"
Because now i just can type "https:localhost:8080/csv/get/player/mycsvfile.csv"
Is it possible?
Use #RequestParam instead of #PathVariable.
If I understood correctly, you want to send the path of the file, you want to load your Player from, via the request.
Sending a file path in the URI won't work from the get go as it will change the path of the request and it will lead to a 404 NOT FOUND.
Using #RequestParam is a different story, you can add full file path there.
#GetMapping("/get/player")
public void loadPlayers(#RequestParam String csvName) {
/*Rest of your code here*/
}
This way your request would look like this:
https://localhost:8080/csv/get/player?csvName=csvs/mycsvfile.csv
If you really want to use #PathVariable to send the path of your file, then you will have to change your endpoint to work with wildcard and extract the file path from the request URI like explained by N. Chicoine here.
And if you need to use this in multiple places you can even get a more elegant solution by implementing an annotation that makes use of the HandlerMethodArgumentResolver interface:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public #interface FilePath {
class Resolver implements HandlerMethodArgumentResolver {
private final PathMatcher pathMatcher;
public Resolver() {
this.pathMatcher = new AntPathMatcher();
}
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
Annotation annotation = methodParameter.getParameterAnnotation(FilePath.class);
return annotation != null;
}
#Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modeContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
if (servletRequest == null) {
return null;
}
String patternAttribute = (String) servletRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
String mappingAttribute = (String) servletRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
return this.pathMatcher.extractPathWithinPattern(patternAttribute, mappingAttribute);
}
}
}
Then you will have to register the annotation in application configuration:
#Configuration
public class Config implements WebMvcConfigurer {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new FilePath.Resolver());
}
}
And finaly you can use it like this:
#GetMapping("/get/player/**")
public void loadPlayers(#FilePath String csvName) {
/*Rest of your code here*/
}
Enabling you to execute the request like:
https://localhost:8080/csv/get/player/csvs/mycsvfile.csv
Hope this will help you.
Related
This is the response I get from the API.
{"get":"statistics","parameters":{"country":"romania"},"errors":[],"results":1,"response":[{"continent":"Europe","country":"Romania","population":19016885,"cases":{"new":"+4521","active":156487,"critical":431,"recovered":2606660,"1M_pop":"148707","total":2827936},"deaths":{"new":"+35","1M_pop":"3407","total":64789},"tests":{"1M_pop":"1149381","total":21857638},"day":"2022-03-24","time":"2022-03-24T07:30:04+00:00"}]}
#RestController
public class CovidTrackerRestController {
#GetMapping("/hello")
public String showCovidInformation() {
// connect to a covid database
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://covid-193.p.rapidapi.com/statistics?country=romania"))
.header("X-RapidAPI-Host", "covid-193.p.rapidapi.com")
.header("X-RapidAPI-Key", "mykey")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = null;
try {
response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// get the information
String responseString = response.body();
System.out.println(responseString);
Response romaniaData = null;
try {
romaniaData = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.readValue(responseString, Response.class);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// format the information
System.out.println(romaniaData);
// send the information to html page
return "/tracker";
}
}
And this is my Bean class which is annotated with #Bean in the configurator class alonside the RestTemplate bean. Other properties such as Cases, Deaths etc are configured same as Response class except being declared as #Bean in the configurator because from what I know once I declare a class #Bean then other references contained automatically become beans as well.
#JsonIgnoreProperties(ignoreUnknown = true)
public class Response {
#JsonProperty("country")
private String country;
#JsonProperty("cases")
private Cases cases;
#JsonProperty("deaths")
private Deaths deaths;
#JsonProperty("day")
private String day;
#JsonProperty("time")
private String time;
#JsonProperty("test")
private Tests tests;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
Your java class needs to be exact representation of received json. Let's call it Wrapper:
public class Wrapper {
#JsonProperty("response")
private List<Response> responses;
public List<Response> getResponses() {
return this.responses;
}
public void setResponses(List<Response> responses) {
this.responses = responses;
}
#Override
public String toString() {
return "Wrapper{" +
"responses=" + responses +
'}';
}
}
I am omiting some properties - get, results, etc. It looks you don't need them. Then deserialization will look like this:
Wrapper data = null;
try {
data = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.readValue("json", Wrapper.class);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(data);
Few notes:
If json property name matches field name in class, there is no need for #JsonProperty
For tests field annotation should be - #JsonProperty("tests"). Property is tests, not test
If you really want to throw the rest of the data, and only need response property, then you need to write custom deserializer and work the json tree. You can see how to do it in my answer here, or this guide, for example. Like this you can parse the response json to your class, even if their structures do not match.
Yes, your class should be like this:
public class ResponseWrapper {
public List<Response> response;
public setResponse(List<Response> response) {
this.response= response;
}
public List<Response> getResponse() {
return response;
}
}
And class Response is your class as you published it. Your class have to have the same structure as JSON
I keep getting this error, the path has been specified in yml and even in the console it is going to the relative path but not reading the file or finding it, how can I get past this ?
I have attached the picture and below is the code in my main method. Any input is deeply appreciated. Thanks!
#SpringBootApplication
#EnableConfigurationProperties(DataStaxAstraProperties.class)
public class BookAppApplication {
#Autowired AuthorRepostories authorRepostories;
#Value("${datadump.location.author}")
private String authorDumpsLocation;
#Value("${datadump.location.works}")
private String authorWorksLocation;
public static void main(String[] args) {
SpringApplication.run(BookAppApplication.class, args);
}
private void initAuthors() {
Path path = Paths.get(authorDumpsLocation);
System.out.println(path.toAbsolutePath());
try(Stream<String> lines = Files.lines(path)){
lines.forEach(line->{
String jsonStr = line.substring(line.indexOf("{"));
try {
JSONObject jsonObject = new JSONObject(jsonStr);
Author author = new Author();
author.setName(jsonObject.optString("name"));
author.setPersonalName(jsonObject.optString("personal_name"));
author.setId(jsonObject.optString("key").replace("/authors/",""));
System.out.println("Saving author" + author.getName() + "....");
authorRepostories.save(author);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
private void initWorks() {
}
#PostConstruct
public void start() {
initAuthors();
initWorks();
}
#Bean
public CqlSessionBuilderCustomizer sessionBuilderCustomizer(DataStaxAstraProperties astraProperties) {
Path bundle = astraProperties.getSecureConnectBundle().toPath();
return builder -> builder.withCloudSecureConnectBundle(bundle);
}
}
found the issue. Had to do with access rights. Java couldn't access the files because it was secured after changing the property of a file, it worked.
Is there any way to implement AOP logging to public method of class that implements Runnable and ran by ExecutorService?
Thread class
#Component
#Scope("prototype")
public class FileProcessor implements Runnable {
private final LinkedBlockingQueue<File> filesQueue;
private final GiftCertificateMapper certificateMapper;
private final File errorFolder;
private static final ReentrantLock LOCK = new ReentrantLock();
private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessor.class);
public FileProcessor(LinkedBlockingQueue<File> filesQueue, GiftCertificateMapper certificateMapper,
File errorFolder) {
this.filesQueue = filesQueue;
this.certificateMapper = certificateMapper;
this.errorFolder = errorFolder;
}
#Override
public void run() {
File file = null;
try {
while ((file = filesQueue.poll(100, TimeUnit.MILLISECONDS)) != null) {
processFile(file);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.warn("File thread was interrupted");
} catch (IOException e) {
LOGGER.error("Error processing file {} \n{}", file.getAbsolutePath(), e);
}
}
public void processFile(File file) throws IOException {
if (file != null) {
try {
ObjectMapper objectMapper = new ObjectMapper();
List<GiftCertificate> certificates = Arrays.asList(objectMapper.readValue(file, GiftCertificate[].class));
certificateMapper.insertList(certificates);
file.delete();
} catch (JsonParseException | UnrecognizedPropertyException | InvalidFormatException | DataIntegrityViolationException e) {
moveFileToErrorFolder(file);
}
}
}
private void moveFileToErrorFolder(File file) throws IOException {
try {
LOCK.lock();
Files.move(Paths.get(file.getAbsolutePath()), getPathForMovingFile(file), StandardCopyOption.ATOMIC_MOVE);
} finally {
LOCK.unlock();
}
}
private Path getPathForMovingFile(File fileForMove) {
File fileList[] = errorFolder.listFiles();
int filesWithSameNameCounter = 0;
if (fileList != null && fileList.length > 0) {
for (File file : fileList) {
if (file.getName().contains(fileForMove.getName())) {
filesWithSameNameCounter++;
}
}
}
return filesWithSameNameCounter > 0 ?
Paths.get(errorFolder.getAbsolutePath(), "(" + filesWithSameNameCounter + ")" + fileForMove.getName()) :
Paths.get(errorFolder.getAbsolutePath(), fileForMove.getName());
}
}
Aspect
#Aspect
#Component
#ConditionalOnProperty(
value = "file-processing.logging.enabled",
havingValue = "true",
matchIfMissing = true)
public class FileProcessingLoggingAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessingLoggingAspect.class);
#Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))")
public void processFilePointcut() {
}
#Around("processFilePointcut()")
public Object logFileProcessing(ProceedingJoinPoint joinPoint) throws Throwable {
// File file = (File) joinPoint.getArgs()[0];
// long time = System.currentTimeMillis();
Object object = joinPoint.proceed();
// long resultTime = System.currentTimeMillis() - time;
LOGGER.info("Processing of file took milliseconds");
return object;
}
}
In Spring AOP , internal method calls cannot be intercepted.
In the code shared , even though the method processFile() is public , it gets called from run(). This is a self reference / internal method call , which cannot be intercepted.
Details can be read in the documentation
Due to the proxy-based nature of Spring’s AOP framework, calls within
the target object are, by definition, not intercepted. For JDK
proxies, only public interface method calls on the proxy can be
intercepted
A pointcut expression to intercept all external method calls to a class implementing Runnable would be as follows
#Around("this(java.lang.Runnable) && within(com.epam.esm.processor..*)")
public Object logFileProcessing(ProceedingJoinPoint pjp) throws Throwable {
try {
return pjp.proceed();
} finally {
//log
System.out.println("****Logged");
}
}
Scoping designator within() limits the scope to apply the advice.
The point cut #Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))") is valid and would work if an external method call happens to it.
Hope this helps.
I followed tutorial for uploading files but I end up with the following error:
Parameter 0 of constructor in nu.pk.cv.storage.FileSystemStorageService required a bean of type 'nu.pk.cv.storage.StorageProperties' that could not be found.
Action:
Consider defining a bean of type 'nu.pk.cv.storage.StorageProperties' in your configuration
The only difference that I know that I have done is that I use #RestController instead of only #Controller And that I have my controller in another subpackage and not in the parent package. My storage classes are in nu.pk.cv.storage while my controller is in nu.pk.cv.cv.
StorageProperties
package nu.pk.cv.storage;
#ConfigurationProperties("storage")
public class StorageProperties {
private String location = "/tmp/cv-gen";
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
FileSystemStorageService
package nu.pk.cv.storage;
#Service
public class FileSystemStorageService implements StorageService {
private final Path rootLocation;
#Autowired
public FileSystemStorageService(StorageProperties properties) {
this.rootLocation = Paths.get(properties.getLocation());
}
#Override
public void store(MultipartFile file) {
String filename = StringUtils.cleanPath(file.getOriginalFilename());
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file " + filename);
}
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"Cannot store the file with relative path outside the current directory "
+ filename);
}
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, this.rootLocation.resolve(filename),
StandardCopyOption.REPLACE_EXISTING);
}
}
catch (IOException e) {
throw new StorageException("Failed to store file " + filename, e);
}
}
#Override
public Stream<Path> loadAll() {
try {
return Files.walk(this.rootLocation, 1)
.filter(path -> !path.equals(this.rootLocation))
.map(this.rootLocation::relativize);
}
catch (IOException e) {
throw new StorageException("Failed to read stored files", e);
}
}
#Override
public Path load(String filename) {
return rootLocation.resolve(filename);
}
#Override
public Resource loadAsResource(String filename) {
try {
Path file = load(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
}
else {
throw new StorageFileNotFoundException(
"Could not read file: " + filename);
}
}
catch (MalformedURLException e) {
throw new StorageFileNotFoundException("Could not read file: " + filename, e);
}
}
#Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
#Override
public void init() {
try {
Files.createDirectories(rootLocation);
}
catch (IOException e) {
throw new StorageException("Could not initialize storage", e);
}
}
}
My controller
package nu.pk.cv.cv;
#RestController
public class CV {
#Autowired
private StorageService storageService;
#PostMapping("/api/cv/generate")
public String generate(#RequestParam("files") MultipartFile file) {
storageService.store(file);
return "Mjau";
}
}
According to Baeldung article you need to add also #Configuration to your class:
#Configuration
#ConfigurationProperties("storage")
public class StorageProperties
add annotation #Configuration or #Component at StorageProperties class.
#Configuration
#ConfigurationProperties("storage")
public class StorageProperties {
or
#Component
#ConfigurationProperties("storage")
public class StorageProperties {
if you receive an Error like "a bean not found or a bean of type configuration", then you have to check your #Component, #Service or #Repository annotations in the related classes.
ref: Spring blog
If you want constructor injection (for example for lombok or missing setters) you must declare it with #ConstructorBinding at the type level or on the specific constructor if there are multiple.
#Configuration
#ConfigurationProperties("storage")
#ConstructorBinding
public class StorageProperties{
or
#Configuration
#ConfigurationProperties("storage")
public class StorageProperties{
public StorageProperties(){...}
#ConstructorBinding
public StorageProperties(String location){this.location = location; ...}
we wanted to watch a file periodically for changes, we are using jboss 7 . Following is my code snippet. I initialized the watcher in the postconstruct method of singleton bean and scheduled a method to poll watch events. I could observe the changes when i modify the file very first time, however the subsequent modifications to the file are not recieved . Can anyone please let me know what could be the issue
#Startup
#ConcurrencyManagement(ConcurrencyManagementType.BEAN)
#Interceptors(NonThrowingPostConstructInterceptor.class)
#Singleton
#Service
#LocalBinding(jndiBinding=IHeartBeatProducerService.JNDI_LOCAL_BINDING)
public class HeartBeatProducerService extends EMSingletonService implements IHeartBeatProducerService{
#EJB(mappedName=IMessageService.JNDI_LOCAL_BINDING)
public IMessageService messageService;
#EJB(mappedName=ICommandExecutionService.JNDI_LOCAL_BINDING)
public ICommandExecutionService commandService;
private final static String LAST_OPERATION_COMPLETED="Last Operation Completed";
private final static String STATUS="Status";
private WatchService watcher;
private Path dir;
private String concServer;
public static final String TOPIC="foo";
private IMLogger logger = new IMLogger("foo");
private String content=null;
#PostConstruct
#Override
public void init() {
// TODO Auto-generated method stub
super.init();
try {
watcher = FileSystems.getDefault().newWatchService();
dir=Paths.get("/shared/foo");
dir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
logger.entering(0, IHeartBeatProducerService.class.getSimpleName(), "Initializing Heart Beat", new String[]{"Entered"});
} catch (IOException e) {
e.printStackTrace();
}
}
#Schedule(second="*/10", minute = "*", hour="*")
private void checkStatus()
{
logger.entering(0, IHeartBeatProducerService.class.getSimpleName(), "Checking Status", new String[]{"Entered"});
final String[] command={"pidof","server"};
commandService.run(command, null, false);
concServer=(commandService.getExitCode()==0)?"UP":"DOWN";
if(concServer.equals("UP"))
{
watch();
}
else
{
content="foo:Failed";
}
produce();
}
public void watch()
{
logger.entering(0, IHeartBeatProducerService.class.getSimpleName(), "Entering watch()", new String[]{"Entered"});
WatchKey key = null;
try
{
key = watcher.take();
}
catch (InterruptedException e)
{
logger.error(HeartBeatProducerService.class.getSimpleName(),"Interupted Exception " + e.getMessage());
}
for ( WatchEvent<?> event: key.pollEvents())
{
WatchEvent.Kind kind = event.kind();
logger.info(HeartBeatProducerService.class.getSimpleName(),"Watch Event :" + kind.name());
if(kind.name().equals("OVERFLOW"))
{
continue;
}
if(kind.name().equals("ENTRY_MODIFY"))
{
Path concLog = (Path) event.context();
logger.info(HeartBeatProducerService.class.getSimpleName(),"Modified File Name:" + concLog.getFileName());
if(concLog.endsWith("current_status.txt"))
{
logger.info(HeartBeatProducerService.class.getSimpleName(), "Reading Status");
readStatus();
}
}
}
boolean valid = key.reset();
if ( !valid)
{
logger.error(HeartBeatProducerService.class.getSimpleName(),"Key Unregistered");
}
}
private void parse(String output)
{
// parse file contents
}
private void readStatus() {
//read status and parse()
}
private void produce()
{
try {
messageService.publish(TOPIC, content, PublishType.ASync);
} catch (MessageException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
There is already a link explaining the same with #Asynchronous tag (EJB 3.1 and NIO2: Monitoring the file system) . however I need to know what could be wrong in this approach.
Your watch method needs to run in an infinite loop. What's happening now is that after
try {
key = watcher.take();
}
you process the event and then the watch() method is finished. Try the effect of
for(;;) {
before the above lines, ending the for block after the validity check. Did you see the example at The Java Tutorials?