I can not figure out how to implement the structure of the following functionality:

I try to stick to the structure of the division of functional by layers in the project: Model, DAO, Service.

For example, there are 2 entities Parser ( Parser ) and Configuration (Parameters for running Parser).

  1. Layer Model: Parser and Configuration . Class that describes the fields (attributes) of the entity;
  2. DAO Layer: ParserDAO and ConfigurationDAO . The logic of access to data in the database (CRUD);
  3. Service layer: ParserService and ConfigurationService . Layer high-level logic. Essentially the same CRUD methods calling DAO methods. Plus a few specific methods.

In the ParserServiceImpl service, the enable(List<Configuration> listConfiguration) creates the object of the child ParserServiceChild1 , which contains the parsing logic of a particular document. ParserServiceChild1 in the enable(Configuration configuration) method enable(Configuration configuration) starts the looped parsing logic for execution in the new thread. Execution becomes background.

The ParserService itself ParserService to be stored somewhere so that it can be turned off by calling the disable() method. For this, I added a HashMap (so far I did it, there were no other normal ideas)

Problem: I can not figure out whether I created the program architecture and, accordingly, the structure? properly organize the storage of the child Services ParserServiceChild1 , ParserServiceChild2 (in the future, and others). While stopped at HashMap .

  • I can not figure out whether I created the program architecture and, accordingly, the structure? Is it right to create child Services to describe the specific logic of Parsers?
  • How to continue working with objects of child Services that we run in the background execution? After all, in order to stop the child Service, I must have access to the object. While I keep in HashMap in ParserDAOImpl .

The question was formulated more than once, so if something is incomprehensibly happy to add a question, thank you!


ADDITION

The relationship between entities: Parser 1 --- N Configuration . Those. One Parser can be run with different Configurations.

Bean initialization is described in xml.

Parser.class :

 @Entity @Table(name = "parser") public class Parser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID") private int id; public int getId() { return id; } @Column(name = "NAME") private String name; public String getName() { return name; } } 

ParserDAOImpl.class :

 @Repository public class ParserDAOImpl implements ParserDAO { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } private Map<Integer, ParserService> mapEnabledParserServices = new HashMap<>(); @Override public Parser getById(int id) { return this.sessionFactory.getCurrentSession().get(Parser.class, new Integer(id)); } @Override public List<Parser> list() { return this.sessionFactory.getCurrentSession().list(); } @Override public ParserService getEnabledParserServiceByConfigurationId(int configurationId) { return this.mapEnabledParserServices.get(configurationId); } @Override public void addEnabledParserService(int configurationId, ParserService enabledParserService) { this.mapEnabledParserServices.put(configurationId, enabledParserService); } @Override public void deleteEnabledParserService(int configurationId) { this.mapEnabledParserServices.remove(configurationId); } } 

ParserServiceImpl.class :

 @Service public class ParserServiceImpl implements ParserService { private ParserDAO parserDAO; public void setParserDAO(ParserDAO parserDAO) { this.parserDAO = parserDAO; } private ConfigurationService configurationService; public void setConfigurationService(ConfigurationService configurationService) { this.configurationService = configurationService; } @Override @Transactional public Parser getById(int id) { return parserDAO.getById(id); } @Override @Transactional public List<Parser> list() { return parserDAO.list(); } @Override @Transactional public boolean enable(List<Configuration> listConfiguration) { try { // Обходим все Конфигураций for (Configuration configuration : listConfiguration) { // Дочерний сервис со специфичной логикой ParserService parserServiceChild = null; // PS Можно было бы хранить класс в БД, и тогда условие (ниже) можно избежать, но пока решил оставить так // Если Конфигурация относится к Парсеру с идентификатором 1 if (configuration.getParserId() == 1) { // Создаем дочерний Сервис, который умеет парсить один тип документов parserServiceChild = new ParserServiceChild1(); } else if (configuration.getParserId() == 2) { // Создаем дочерний Сервис, который умеет парсить другой тип документов parserServiceChild = new ParserServiceChild2(); } // Проверка других идентификаторов // В объекте Конфигурация меняем статус на true - указывающий что Парсер с такой Конфигурацией запущен configuration.setStatus(true); // Обновляем Конфигурацию в БД, для сохранения измененного статуса this.configurationService.update(configuration); // Добавляем дочерний Сервис в "Список запущенных дочерних Сервисов" this.parserDAO.addEnabledParserService(configuration.getId(), parserServiceChild); // Запускаем дочерний Сервис parserServiceChild.enable(configuration); } return true; } catch (Exception ex) { return false; } } @Override @Transactional public boolean disable(List<Configuration> listConfiguration) { try { // Обходим все Конфигураций for (Configuration configuration : listConfiguration) { // Извлекаем из "Списка запущенных дочерних Сервисов" дочерний Сервис, который работает с переданной в параметрах метода Конфигурацией ParserService parserService = this.parserDAO.getEnabledParserServiceByConfigurationId(configuration.getId()); // В объекте Конфигурация меняем статус на false - указывающий что Парсер с такой Конфигурацией выключен (не запускался) configuration.setStatus(false); // Обновляем Конфигурацию в БД, для сохранения измененного статуса this.configurationService.update(configuration); // Удаляем объект дочерний Сервис из "Списка запущенных дочерних Сервисов" this.parserDAO.deleteEnabledParserService(configuration.getId()); // Выключаем дочерний Сервис parserService.disable(); } return true; } catch (Exception ex) { return false; } } } 

ParserServiceChild1 , ParserServiceChild2 :

 @Service public class ParserServiceChild1 implements ParserService { @Override @Transactional public void enable(Configuration configuration) { // Запуск зацикленной логики в новом потоке } @Override @Transactional public void disable() { // Остановка } @Override ... } 

Configuration.class :

 @Entity @Table(name = "parser_configuration") public class Configuration { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID") private int id; public int getId() { return id; } // Параметры для настройки и запуска дочернего Сервиса `ParserServiceChild1` и др. дочерних Сервисов @Column(name = "PARAMETER") private byte[] parameter; public byte[] getParameter() { return parameter; } public void setParameter(byte[] config) { this.parameter = config; } @Column(name = "STATUS") private boolean status; public boolean isStatus() { return status; } public void setStatus(boolean status) { this.status = status; } @Column(name = "PARSER_ID") private int parserId; public int getParserId() { return parserId; } public void setParserId(int parserId) { this.parserId = parserId; } } 

ConfigurationDAOImpl.class

 @Repository public class ConfigurationDAOImpl implements ConfigurationDAO { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } @Override public Configuration getById(int id) { return this.sessionFactory.getCurrentSession().get(Configuration.class, new Integer(id)); } @Override public List<Configuration> list() { return this.sessionFactory.getCurrentSession().list(); } @Override public void add(Configuration configuration) { this.sessionFactory.getCurrentSession().persist(configuration); } @Override public void update(Configuration configuration) { this.sessionFactory.getCurrentSession().update(configuration); } @Override public void delete(Configuration configuration) { this.sessionFactory.getCurrentSession().delete(configuration); } } 

ConfigurationServiceImpl.class

 @Service public class ConfigurationServiceImpl implements ConfigurationService { private ConfigurationDAO configurationDAO; public void setConfigurationDAO(ConfigurationDAO configurationDAO) { this.configurationDAO = configurationDAO; } @Override @Transactional public Configuration getById(int id) { return this.configurationDAO.getById(id); } @Override @Transactional public List<Configuration> list() { return this.configurationDAO.list(); } @Override @Transactional public void add(Configuration configuration) { configurationDAO.add(configuration); } @Override @Transactional public void update(Configuration configuration) { configurationDAO.update(configuration); } @Override @Transactional public void delete(Configuration configuration) { configurationDAO.delete(configuration); } } 
  • you seem to have dealt with the structure and understand why you need a Service, ParserService does not need to be stored anywhere, it’s just a singlton, the state of which is managed by the container. You can make @Autowired in any other bin and get ParserService and call disable() . For clarity, your application code would not hurt - Sergii Getman
  • @SergiiGetman thanks! added clarification and addition to the question - Alexey
  • one
    I can not understand the logic, you need to parse something, you run several threads in parallel, but then you stop them. What's the point of this? It is logical to simply transfer the task with which it should cope to the flow, and when a new task arrives, transfer it to the new flow. If periodicity is important to you, then there are tasks and triggers in the spring - scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI")); - 15 minutes at the beginning of each hour from 9 to 17 from Mon to Fri Does this suit you? - Sergii Getman Nov
  • one
    the option with dachshunds and triggers is the best practices, I think that stopping the threads and then starting them again is a bad practice. would not advise creating a bunch of services of the same type in the service - Sergii Getman
  • one
    Logically, your child services are components. just read why @Service annotation is needed - this should be enough, respectively, this is projected onto the naming of your classes docs.spring.io/spring/docs/current/javadoc-api/org/… - Sergii Getman

0