diff --git a/adastor.iml b/adastor.iml index 7d71d4f..bd4e95b 100644 --- a/adastor.iml +++ b/adastor.iml @@ -90,6 +90,7 @@ + diff --git a/adastor.ipr b/adastor.ipr index 91733a9..2a334d9 100644 --- a/adastor.ipr +++ b/adastor.ipr @@ -13,6 +13,15 @@ + + + hsqldb.local + true + true + org.hsqldb.jdbc.JDBCDriver + jdbc:hsqldb:file:$PROJECT_DIR$/database/adastor + + @@ -167,6 +176,16 @@ + + + + " + + + master_key + sa + + @@ -883,6 +902,17 @@ + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 6dfa25e..b2c81e6 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,12 @@ hsqldb + + org.modelmapper + modelmapper + 2.3.2 + + org.springframework.boot spring-boot-starter-jetty diff --git a/src/main/java/ru/bvn13/adastor/config/Config.java b/src/main/java/ru/bvn13/adastor/config/Config.java index 33b4939..9962d31 100644 --- a/src/main/java/ru/bvn13/adastor/config/Config.java +++ b/src/main/java/ru/bvn13/adastor/config/Config.java @@ -4,6 +4,7 @@ import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; +import javax.annotation.PostConstruct; import java.io.File; /** @@ -16,9 +17,38 @@ public class Config { private String storagePath; @Getter + // #{new Integer.parseInt('${api.orders.pingFrequency}')} @Value("${adastor.storage.space.free}") private Long freeSpace; + @Getter + @Value("${adastor.max-size}") + private Long maxSize; + + @Getter + @Value("${adastor.min-days-storing}") + private Long minDaysStoring; + + @Getter + @Value("${adastor.max-days-storing}") + private Long maxDaysStoring; + + @PostConstruct + public void checkParams() { + if (storagePath == null || storagePath.isEmpty()) { + throw new IllegalArgumentException("Storage path is not specified!"); + } + if (freeSpace == null || freeSpace.equals(0L)) { + throw new IllegalArgumentException("Free space is not specified!"); + } + if (maxSize == null || maxSize.equals(0L)) { + throw new IllegalArgumentException("Max size is not specified!"); + } + if (maxDaysStoring == null || maxDaysStoring.equals(0L)) { + throw new IllegalArgumentException("Max days storing is not specified!"); + } + } + public String getStoragePath() { String absStoragePath = storagePath.startsWith(".") ? new File("").getAbsolutePath() + storagePath.substring(1) : storagePath; File path = new File(absStoragePath); diff --git a/src/main/java/ru/bvn13/adastor/config/WebConfig.java b/src/main/java/ru/bvn13/adastor/config/WebConfig.java index 3371510..18ca59d 100644 --- a/src/main/java/ru/bvn13/adastor/config/WebConfig.java +++ b/src/main/java/ru/bvn13/adastor/config/WebConfig.java @@ -1,5 +1,7 @@ package ru.bvn13.adastor.config; +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** @@ -8,5 +10,9 @@ import org.springframework.context.annotation.Configuration; @Configuration public class WebConfig { + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } } diff --git a/src/main/java/ru/bvn13/adastor/entities/Stortion.java b/src/main/java/ru/bvn13/adastor/entities/Stortion.java index 74fafdb..a864245 100644 --- a/src/main/java/ru/bvn13/adastor/entities/Stortion.java +++ b/src/main/java/ru/bvn13/adastor/entities/Stortion.java @@ -4,10 +4,13 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import ru.bvn13.adastor.config.Config; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.Transient; import java.time.LocalDateTime; /** diff --git a/src/main/java/ru/bvn13/adastor/entities/dtos/StortionDto.java b/src/main/java/ru/bvn13/adastor/entities/dtos/StortionDto.java new file mode 100644 index 0000000..4f645a5 --- /dev/null +++ b/src/main/java/ru/bvn13/adastor/entities/dtos/StortionDto.java @@ -0,0 +1,31 @@ +package ru.bvn13.adastor.entities.dtos; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Transient; +import java.time.LocalDateTime; + +/** + * @author boykovn at 12.03.2019 + */ +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class StortionDto { + + private String uuid; + + private LocalDateTime storeDate; + + private long size; + + private String path; + + @Transient + private long retention; + +} diff --git a/src/main/java/ru/bvn13/adastor/web/controllers/TestController.java b/src/main/java/ru/bvn13/adastor/web/controllers/TestController.java new file mode 100644 index 0000000..e55a7cf --- /dev/null +++ b/src/main/java/ru/bvn13/adastor/web/controllers/TestController.java @@ -0,0 +1,36 @@ +package ru.bvn13.adastor.web.controllers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import ru.bvn13.adastor.entities.Stortion; +import ru.bvn13.adastor.entities.dtos.StortionDto; +import ru.bvn13.adastor.web.repositories.StortionRepository; +import ru.bvn13.adastor.web.services.StortionService; + +import java.util.stream.Stream; + +/** + * @author boykovn at 12.03.2019 + */ +@Controller +public class TestController { + + private StortionService stortionService; + + @Autowired + public void setStortionRepository(StortionService stortionService) { + this.stortionService = stortionService; + } + + @GetMapping("/t") + public @ResponseBody String test() { + + Stream stortions = stortionService.findAllSortedByRetention(); + stortions.forEach(st -> System.out.println(String.format("%s - %s - %s - %s", st.getUuid(), st.getStoreDate(), st.getSize(), st.getRetention()))); + + return "done"; + } + +} diff --git a/src/main/java/ru/bvn13/adastor/web/controllers/UploadController.java b/src/main/java/ru/bvn13/adastor/web/controllers/UploadController.java index 085f65b..9788287 100644 --- a/src/main/java/ru/bvn13/adastor/web/controllers/UploadController.java +++ b/src/main/java/ru/bvn13/adastor/web/controllers/UploadController.java @@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; -import ru.bvn13.adastor.entities.Stortion; +import ru.bvn13.adastor.entities.dtos.StortionDto; import ru.bvn13.adastor.web.services.StortionService; import javax.servlet.http.HttpServletRequest; @@ -24,7 +24,8 @@ public class UploadController { } @PostMapping(value="/a", produces = {"application/json"}) - public @ResponseBody Stortion uploadData(HttpServletRequest request) throws IOException { + public @ResponseBody + StortionDto uploadData(HttpServletRequest request) throws IOException { return stortionService.createStortion(request.getInputStream()); } diff --git a/src/main/java/ru/bvn13/adastor/web/repositories/CustomStortionRepository.java b/src/main/java/ru/bvn13/adastor/web/repositories/CustomStortionRepository.java new file mode 100644 index 0000000..ecefe47 --- /dev/null +++ b/src/main/java/ru/bvn13/adastor/web/repositories/CustomStortionRepository.java @@ -0,0 +1,14 @@ +package ru.bvn13.adastor.web.repositories; + +import ru.bvn13.adastor.entities.Stortion; + +import java.util.stream.Stream; + +/** + * @author boykovn at 12.03.2019 + */ +public interface CustomStortionRepository { + + Stream findAllSortedByRetention(); + +} diff --git a/src/main/java/ru/bvn13/adastor/web/repositories/StortionRepository.java b/src/main/java/ru/bvn13/adastor/web/repositories/StortionRepository.java index 534569e..c13e6cf 100644 --- a/src/main/java/ru/bvn13/adastor/web/repositories/StortionRepository.java +++ b/src/main/java/ru/bvn13/adastor/web/repositories/StortionRepository.java @@ -8,6 +8,6 @@ import ru.bvn13.adastor.entities.Stortion; * @author boykovn at 11.03.2019 */ @Repository -public interface StortionRepository extends JpaRepository { +public interface StortionRepository extends JpaRepository, CustomStortionRepository { } diff --git a/src/main/java/ru/bvn13/adastor/web/repositories/impl/StortionRepositoryImpl.java b/src/main/java/ru/bvn13/adastor/web/repositories/impl/StortionRepositoryImpl.java new file mode 100644 index 0000000..428d6b5 --- /dev/null +++ b/src/main/java/ru/bvn13/adastor/web/repositories/impl/StortionRepositoryImpl.java @@ -0,0 +1,57 @@ +package ru.bvn13.adastor.web.repositories.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import ru.bvn13.adastor.config.Config; +import ru.bvn13.adastor.entities.Stortion; +import ru.bvn13.adastor.web.repositories.CustomStortionRepository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import java.util.stream.Stream; + +/** + * @author boykovn at 12.03.2019 + */ +@Repository +public class StortionRepositoryImpl implements CustomStortionRepository { + + private Config config; + + @Autowired + public void setConfig(Config config) { + this.config = config; + } + + + @PersistenceContext + private EntityManager entityManager; + + /** + * RETENTION = min_age + (-max_age + min_age) * pow((file_size / max_size - 1), 3) + * + * Implemets the query - selecting all stortions ordered by retention ascending + * + * SELECT * FROM STORTION + * ORDER BY 'retention' ASC + * + * @return queried collection + */ + @Override + public Stream findAllSortedByRetention() { + + long min_age = config.getMinDaysStoring(); + long max_age = config.getMaxDaysStoring(); + long max_size = config.getMaxSize(); + + Query query = entityManager.createQuery("SELECT s FROM Stortion s ORDER BY " + +" (CAST(:min_age as double) + (- CAST(:max_age as double) + CAST(:min_age as double)) * POWER((CAST(s.size as double) / CAST(:max_size as double) - 1), 3)) ASC"); + query.setParameter("min_age", (double)min_age); + query.setParameter("max_age", (double)max_age); + query.setParameter("max_size", max_size); + + return (Stream) query.getResultStream(); + + } +} diff --git a/src/main/java/ru/bvn13/adastor/web/services/StortionService.java b/src/main/java/ru/bvn13/adastor/web/services/StortionService.java index f5a9e9c..189b4b3 100644 --- a/src/main/java/ru/bvn13/adastor/web/services/StortionService.java +++ b/src/main/java/ru/bvn13/adastor/web/services/StortionService.java @@ -1,15 +1,18 @@ package ru.bvn13.adastor.web.services; +import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import ru.bvn13.adastor.config.Config; import ru.bvn13.adastor.entities.Stortion; +import ru.bvn13.adastor.entities.dtos.StortionDto; import ru.bvn13.adastor.web.repositories.StortionRepository; import java.io.*; import java.time.LocalDateTime; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; /** * @author boykovn at 11.03.2019 @@ -19,6 +22,7 @@ public class StortionService { private StortionRepository stortionRepository; private Config config; + private ModelMapper modelMapper; @Autowired public void setStortionRepository(StortionRepository stortionRepository) { @@ -30,6 +34,11 @@ public class StortionService { this.config = config; } + @Autowired + public void setModelMapper(ModelMapper modelMapper) { + this.modelMapper = modelMapper; + } + public Optional findStortion(String uuid) { return stortionRepository.findById(uuid); } @@ -45,7 +54,7 @@ public class StortionService { return targetStream; } - public Stortion createStortion(InputStream is) throws IOException { + public StortionDto createStortion(InputStream is) throws IOException { String uuid = UUID.randomUUID().toString(); String path = String.format("/%s", uuid); String fullPath = String.format("%s/%s", config.getStoragePath(), uuid); @@ -62,7 +71,28 @@ public class StortionService { stortion.setSize(bytesCount); stortionRepository.save(stortion); - return stortion; + return convertToDto(stortion); + } + + private StortionDto convertToDto(Stortion stortion) { + StortionDto stortionDto = modelMapper.map(stortion, StortionDto.class); + stortionDto.setRetention(computeRetention(stortion)); + return stortionDto; + } + + public Stream findAllSortedByRetention() { + Stream stortions = stortionRepository.findAllSortedByRetention(); + return stortions.map(this::convertToDto); + } + + /** + * RETENTION = min_age + (-max_age + min_age) * pow((file_size / max_size - 1), 3) + * @return retention + */ + private long computeRetention(Stortion stortion) { + double retention = config.getMinDaysStoring() + + (-config.getMaxDaysStoring() + config.getMinDaysStoring()) * Math.pow((double) stortion.getSize() / config.getMaxSize() - 1, 3); + return Math.round(retention); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 93c027b..295fe16 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,7 +1,13 @@ #relative or absolute adastor.storage.path=./storage -#in Megabytes -adastor.storage.space.free=100Mb +#in bytes +adastor.storage.space.free=200000000 +#max stortion size in bytes +adastor.max-size=100000000 +#min days storing +adastor.min-days-storing=30 +#max days storing +adastor.max-days-storing=356 spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.datasource.url=jdbc:hsqldb:file:./database/adastor