понедельник, 16 июля 2018 г.

Взаимодействие между приложениями и Watch Service

Взаимодействие между разными приложениями Java можно сделать с помощью файловой системы, базы данных и JMS. Рассмотрим случай ФС - в Java SE 7 появился новый и очень интересный механизм отслеживания изменений файловой системы в заданном каталоге - так называемый Watch Service, позволяющий остлеживать события создания / удаления / изменения файлов в реальном времени. То есть вы можете например создать каталоги /tmp/tasks/task1, /tmp/tasks/task2 и тогда к примеру одно приложение будет вносить в эти каталоги какие-либо изменения - а другое приложение будет отслеживать эти изменения в real-time и в зависимости от каталога и характера изменений запускать соответствующие задачи. Вот метод из десктопного проекта с использованием Spring, который работает в отдельном потоке от основного и отслеживает изменения в каталоге /tmp/tasks/task1 и когда вы руками (вместо другого приложения:)) кладете / удаляете файл в /tmp/tasks/task1 - выводится соответствующее сообщение.
// async-метод для получения задач
@Async // СКАНИРУЕМ НА НОВОЕ ЗАДАНИЕ в отдельном потоке
public void receiveTaskChecker() {
  try {
    WatchService watcher = FileSystems.getDefault().newWatchService();
   // сканируем на изменения соотв каталог для задачи номер 1:
    Path dir = FileSystems.getDefault().getPath("/tmp/tasks/task1");
    WatchKey key = dir.register(watcher,    
             StandardWatchEventKinds.ENTRY_CREATE, 
             StandardWatchEventKinds.ENTRY_DELETE);
   // Экземпляры WatchKey  потокобезопасны.
   while (true) {
      key = watcher.take(); // блокирует поток пока не появится событие
      // есть также неблокирующий способ получения key:
     //key = watcher.poll(5, TimeUnit.SECONDS);
     for (WatchEvent<?> event : key.pollEvents()) {
        if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
            // запускаем выполнение нужной задачи
            System.out.println("File task created \n");
        }
        if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
            // запускаем выполнение нужной задачи
            System.out.println("File task deleted \n");
        }                   
     }
    key.reset();
    }
   } catch (IOException | InterruptedException ex) {     }
}
demo-пример с исходниками - https://github.com/harp077/Watch-Service-Demo

Хеши - это просто ! Apache Commons Codec.

Расскажем об очень полезной библиотеке Apache Commons Codec ( https://commons.apache.org/proper/commons-codec/ ) - а точнее о классе утилит DigestUtils, который позволяет считать хеши от текста, файлов и т д. Поддерживает хеши md2, md5, sha1, sha256, sha384, sha512. Зависимость maven:
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>

Подсчет хешей для текста:

import javax.swing.JOptionPane;
import org.apache.commons.codec.digest.DigestUtils;

public class HashTextTest {

    public static String GetTextHash(String buf, String Tip) {
        if (!buf.isEmpty()) {
            switch (Tip) {
                case "md2":   return DigestUtils.md2Hex(buf);
                case "md5":   return DigestUtils.md5Hex(buf);
                case "sha1":   return DigestUtils.sha1Hex(buf);
                case "sha256": return DigestUtils.sha256Hex(buf);
                case "sha384": return DigestUtils.sha384Hex(buf);
                case "sha512": return DigestUtils.sha512Hex(buf);
                default:       return "wrong hash type";
            }
        } else {
            JOptionPane.showMessageDialog(null, "'Input Text' is empty !");
        }
        return "some error";
    }

    public static void main(String[] args) {
        String tip = JOptionPane.showInputDialog("input Hash Type:");
        String str = JOptionPane.showInputDialog("input string for hash:");
        JOptionPane.showMessageDialog(null, GetTextHash(str, tip));
    }

}
исходники - https://github.com/harp077/HashTextDemo
подсчет хеша строки для кодировки UTF-8:
import static org.apache.commons.codec.binary.StringUtils.getBytesUtf8;
String str = "bla-bla" ;
DigestUtils.md5Hex(getBytesUtf8(str)); 

Подсчет хешей для файлов:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import org.apache.commons.codec.digest.DigestUtils;

public class HashFileTest {

    public static String hash_of_File(File buffile, String tip) {
        if (!buffile.isFile()) {
            JOptionPane.showMessageDialog(null, "not a file !");
            return "";
        }
        try (FileInputStream fis = new FileInputStream(buffile);
                BufferedInputStream bis = new BufferedInputStream(fis);) {
            switch (tip) {
                case "md2":   return DigestUtils.md2Hex(bis);
                case "md5":   return DigestUtils.md5Hex(bis);
                case "sha1":   return DigestUtils.sha1Hex(bis);
                case "sha256": return DigestUtils.sha256Hex(bis);
                case "sha384": return DigestUtils.sha384Hex(bis);
                case "sha512": return DigestUtils.sha512Hex(bis);
                default:       return "wrong hash type";
            }
        } catch (FileNotFoundException fex) {
        } catch (IOException ex) {        }
        return "";
    }

    public static void main(String[] args) {
        String tip = JOptionPane.showInputDialog("input Hash Type:");
        JFileChooser fileopen = new JFileChooser();
        int ret = fileopen.showDialog(null, "Открыть файл");
        if (ret == JFileChooser.APPROVE_OPTION) {
            File file = fileopen.getSelectedFile();
            JOptionPane.showMessageDialog(null, hash_of_File(file, tip));
        }
    }

}
исходники - https://github.com/harp077/HashFileDemo 
В commons-codec еще много что хорошего.

Полиморфизм в java

Через абстратный класс:
public abstract class Skot {
    public abstract String Zvuk ();
}
public class Korova extends Skot {
    @Override
    public String Zvuk() {
       return "Korova = mu-mu";
    }
}
public class Koza extends Skot {
    @Override
    public String Zvuk() {
       return "Koza = be-be";
    }
}
Через интерфейс:
public interface Skotina {
    String Golos ();
}
public class Dog implements Skotina {
    @Override
    public String Golos() {
       return "Dog = gav";
    }   
}
public class Cat implements Skotina {
    @Override
    public String Golos() {
       return "Cat = mau";
    }   
}
Метод main:
 public class PolimorfizmDemo {

    public static void main(String[] args) {
        Skot korova = new Korova();
        Skot koza = new Koza();
        System.out.println(korova.Zvuk());
        System.out.println(koza.Zvuk());
        ///////////
        Skotina dog = new Dog();
        Skotina cat = new Cat();
        System.out.println(dog.Golos());
        System.out.println(cat.Golos());
    }
}
вывод:
Korova = mu-mu
Koza = be-be
Dog = gav
Cat = mau
При обьявлении переменных вы указываете более всеобьемлющий тип чем тот, который вы используете при создании переменных в операторе new, однако вызываются именно те методы, которые нужны - происходит это за счет динамического связывания (во время выполнения) в отличие от статического связывания (во время компиляции). Здесь также следует упомянуть знаменитый холивар - "что лучше - абстрактный класс или интерфейс ?".

суббота, 14 июля 2018 г.

Способы создания/уничтожения бинов и паттерн "фабрика"

Коротко - паттерн "фабрика" - это реализация класса который обеспечивает настройку, создание и получение обьектов с помощью фабричных методов.
Spring
Вы можете отмечать классы аннотациями @Component, @Service, @Repository, @Controller, @Named - и тогда при наличии аннотации @ComponentScan для класса конфигурации приложения контейнер Spring автоматически определит бины и внедрит их куда нужно. Также бины можно создавать с помощью настроек XML - но мы это не будем рассматривать. Есть еще способ - это фабричные методы в классе конфигурации приложения, отмеченные аннотацией @Bean. Наиболее яркий пример - это бин, реализующий интерфейс DataSource. Вот класс конфига приложения:
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"PKG"})
public class AppContext {

    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }
....................   
}
Теперь можно внедрить этот бин куда и как угодно - через поле / метод / конструктор, например:
@Inject
private DataSource ds;
JavaEE CDI
Таким же функционалом обладает контейнер CDI JavaEE - только здесь такие фабричные методы называют продюсерами а используются аннотации @Produces и @Disposes и использовать их можно в любом бине CDI, аналог примера выше для CDI:
@Named
public class DSproducer {

    @Produces @ApplicationScoped
    @Named("ds") 
     public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }

    public void dsClose (@Disposes DataSource ds) {
        ds.close();
    }

}
отметим, что аннотация  @Disposes применяется к параметру метода уничтожения и кроме того - методы создания и уничтожения бина должны быть в одном классе. По сути @Produces - это аналог @Bean, а метод уничтожения с аннотацией @Disposes - это аналог параметра "destroyMethod" аннотации @Bean из Spring. Метод уничтожения вызывается по окончании действия контекста данного бина (здесь - @ApplicationScoped).
И да - точно так же теперь можно внедрить этот бин куда и как угодно - через поле / метод / конструктор (все эти способы контейнер CDI поддерживает точно так же как и Spring), например:
@Inject
private DataSource ds;


Spring - как бину узнать свое имя

Для этого надо реализовать интерфейс BeanNameAware и его единственный метод setBeanName(String str):

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
public class HashOfFile implements BeanNameAware {
   
    private String myName;
   
    @Override
    public void setBeanName (String myName) {
        this.myName=myName;
    }

   
    @PostConstruct
    public void afterBirn(){
       System.out.println("HashOfFile: my name is = " + myName);
    }
..................................
}
Пример исходного кода - https://github.com/harp077/hofat

Интеграция Spring и JPA EclipseLink

Класс конфигурации приложения:

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = {"jennom"})
@EnableLoadTimeWeaving
@PropertySource(value = "classpath:jennom.properties")
public class AppContext {


    @Bean
    public JpaTransactionManager jpaTransactionManager() {
        JpaTransactionManager jtm = new JpaTransactionManager();
        jtm.setDataSource(this.ds());
        jtm.setJpaDialect(new EclipseLinkJpaDialect());
        jtm.setPersistenceUnitName("jennomPU");
        return jtm;
    }

    @Bean(destroyMethod = "close")
    public DataSource ds() {
        BasicDataSource dsNodes = new BasicDataSource();
        dsNodes.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        dsNodes.setUrl("jdbc:derby:./db/primer");
        dsNodes.setPassword("12345");
        dsNodes.setUsername("romka");
        return dsNodes;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean eemf = new LocalContainerEntityManagerFactoryBean();
        eemf.setDataSource(this.ds());
        eemf.setJpaVendorAdapter(elJpaVendorAdapter());
        eemf.setPackagesToScan("jennom.jpa");
        eemf.setPersistenceUnitName("jennomPU");
        eemf.setJpaDialect(new EclipseLinkJpaDialect());
        eemf.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE); 
        return eemf;
    }

    @Bean
    public EclipseLinkJpaVendorAdapter elJpaVendorAdapter() {
        EclipseLinkJpaVendorAdapter jpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
        jpaVendorAdapter.setDatabase(Database.DERBY);
        jpaVendorAdapter.setGenerateDdl(true);
        jpaVendorAdapter.setShowSql(true);
        jpaVendorAdapter.setDatabasePlatform("org.eclipse.persistence.platform.database.DerbyPlatform");
        return jpaVendorAdapter;
    }

}

после этого вы можете создавать свой DAO:


@Service
@Repository
@Transactional
public class EjbDaoJPA {

    @PersistenceContext(unitName = "jennomPU")
    private EntityManager em;

    @Transactional(readOnly=true)
    public List<JpaNodes> findAllNodesJPQL () {
        List<JpaNodes> nodes=em.createNamedQuery("JpaNodes.findAll", JpaNodes.class).getResultList();
        return nodes;
    }
    @Transactional(readOnly=true)
    public JpaNodes findNodeJPQL (int id) {
        JpaNodes nodes=em.createNamedQuery("JpaNodes.findById", JpaNodes.class)
        .setParameter("id", id).getSingleResult();
        return nodes;
    }   
    public void removeNode (JpaNodes jpaNode) {
        em.remove(jpaNode);
    } 

    public void createNode (JpaNodes jpaNode) {
        em.persist(jpaNode);
    }   

    public void editNode (JpaNodes jpaNode) {
        em.merge(jpaNode);
    } 
      public void clearAllNodesSQL () {

        em.createNativeQuery("delete from nodes");
    } 

    public void clearAllNodesJPQL () {
        em.createNamedQuery("JpaNodes.clear", JpaNodes.class);
    }   

}


Сущность JpaNodes

@Entity
@Cacheable(true)
@Table(name="nodes")
@NamedQueries ({
    @NamedQuery(name="JpaNodes.clear",
            query="delete from JpaNodes"),   
    @NamedQuery(name="JpaNodes.findAll",
            query="select c from JpaNodes c order by c.ip"),   
    @NamedQuery(name="JpaNodes.findByIP",
            query="select c from JpaNodes c where c.ip = :ip"),
    @NamedQuery(name="JpaNodes.findById",
            query="select c from JpaNodes c where c.id = :id")   
})
public class JpaNodes implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO) /// !!!!!!!! = IDENTITY
    private int id;
   
    @Basic   
    @Column(name = "time", nullable=true, insertable=true, updatable=true, length=255)    
    private String time="";
   
    @Basic 
    @Size(min=1,max=5,message="stateFar")
    @Column(name = "StateFar", nullable=true, insertable=true, updatable=true, length=255)    
    private String StateFar="-";
   
    @Basic
    @NotNull(message="IP/DNS not null")
    //@Pattern(regexp="",message="")
    @Column(name = "ip", nullable=false, insertable=true, updatable=true, length=255)    
    private String ip;
   
    @Basic   
    @Column(name = "info", nullable=true, insertable=true, updatable=true, length=255)    
    private String info="";
   
    @Basic 
    @NotNull(message="Type not null")
    @Column(name = "tip", nullable=false, insertable=true, updatable=true, length=255)    
    private String tip="";   
   
    @Basic   
    @Column(name = "loss", nullable=true, insertable=true, updatable=true, length=255)    
    private String loss="0";
   
    @Basic
    @Size(min=1,max=5,message="state")
    @Column(name = "state", nullable=true, insertable=true, updatable=true, length=255)    
    private String state="-";
    ...........геттеры + сеттеры + equals + hashCode + toString}

пятница, 13 июля 2018 г.

Асинхронный вызов методов

При таком вызове метод выполняется в отдельном потоке не прерывая основной поток приложения. Иногда такой прием называют "паттерн асинхронность".
Spring
Класс конфигурации приложения надо отметить аннотацией @EnableAsync. Если это веб-приложение - то достаточно снабдить метод аннотацией @Async - и любой контейнер сервлетов (например Tomcat) запустит метод в отдельном потоке. Для десктоп-приложения надо будет добавить в класс конфигурации следующий бин:
@Configuration
@EnableAsync
public class AppContext {
   
    @Bean
    public TaskExecutor taskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
........................................
}

JavaEE
Такой же функционал обеспечивает контейнер EJB из "коробки" без всяких дополнительных настроек (начиная с JavaEE 6 - EJB 3.1 - Например TomEE v1.7.4 с профайлом jaxrs - http://tomee.apache.org/download-archive.html или http://openejb.apache.org/downloads.html ) - достаточно в бине EJB отметить метод аннотацией @Asynchronous - она применима для всех типов бинов EJB - @Stateless, @Stateful, @Singleton. Если отметить весь класс - то все его методы станут асинхронными.

суббота, 7 июля 2018 г.

Уточнение внедрения бинов

Бывают ситуации когда есть несколько реализаций бина одного и того же типа - тогда при внедрении надо будет уточнять какая именно реализация внедряется:
Spring:
в стеке Spring для этого есть аннотация @Qualifier("beanName"):
public class MyClass {
    @Inject
    @Qualifier("finebean1")
    private Fine fine1;

    @Inject
    @Qualifier("finebean2")
    private Fine fine2;
 ..........................................  
}
Соответственно в файле конфигурации есть 2 реализации бина одинакового типа:
@Configuration
public class AppCfg {

   @Bean("
finebean1")
   public Fine oneFine() {
       ................

   }
   @Bean("
finebean2")
   public
Fine twoFine() {
       .................

   }
}
JavaEE CDI:
Допустим продюсер (фабрика) реализует 2 бина одного типа:
public class MyProducer {
    @Produces 
    @SessionScoped
    @Named("finebean1") 
     public Fine getFine1() {
        return new Fine();
    }

    @Named("finebean1")   
    public void killFine1 (@Disposes Fine fine1) {
        fine1.close();
    }

    @Produces 
    @SessionScoped
    @Named("finebean2") 
     public Fine getFine2() {
        return new Fine();
    }

    @Named("finebean2")   
    public void killFine2 (@Disposes Fine fine2) {
        fine2.close();
    }

}
Здесь есть аналог @Qualifier - аннотация @Named, которую поддерживает и Spring и при внедрении уточнение бинов выглядит так:
public class MyClass {
    @Inject
    @Named("finebean1")
    private Fine fine1;

    @Inject
    @Named("finebean2")
    private Fine fine2;
 ..........................................  
}
также можно использовать и для аннотации @Resource:
public class AmbiguousResourceFine {
    @Resource
    @Named("fineDay")
    private Fine fine;
....................................
  }
@Named можно использовать как замену для @Component / @Service / @Controller / @Repository
Для аннотации @Resource имя можно задать как параметр:
public class MyResss {
    @Resource(name = "rrresss")
    private Resss resss;

}
Для поддержки аннотаций @Named и @Inject CDI стека JavaEE в проекте Spring надо добавить зависимость:
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

Для поддержки всех аннотаций CDI в проект Spring можно добавить зависимость:
<dependency>
     <groupId>javax.enterprise</groupId>
     <artifactId>cdi-api</artifactId>
     <version>1.2</version>
</dependency>

тогда будут работать все аннотации из CDI, которые поддерживает Spring - @Named, @Inject, @PostConstruct, @PreDestroy, @Resource и др.

среда, 4 июля 2018 г.

Потокобезопасный синглтон

Лучший способ создания одиночек в Java - использование Enum. Перечисления по своей сущности одиночки, так что JVM берет на себя большую часть работы по созданию одиночки. Таким образом, при использовании Enum  вы освобождаетесь от забот по синхронизации создания и обеспечения деятельности объекта и избегаете про­блем, связанных с инициализацией. Реализация паттерна Singleton на основе типа Enum:

publc enum MySingletonEnum {
      INSTANCE:
      public void doSomethinglnteresting () {.....}
}

Вы получаете ссылку на экземпляр одиночки следующим образом:

MySingletonEnum mse  =  MySingletonEnum.INSTANCE;

Как только у вас есть ссылка на одиночку, вы можете вызвать любой из его методов:

mse.doSomethinglnteresting();

пятница, 22 июня 2018 г.

Зависимые бины и аннотация @DependsOn

Бывают случаи когда ваш бин зависит от 1 или нескольких других бинов приложения Spring - здесь на помощь приходит аннотация @DependsOn, которая заставляет контейнер сначала создавать бины зависимостей - и только потом зависящий от них бин. В параметрах аннотации указываются имена бинов. Аннотацию можно использовать или в файле конфигурации приложения совместно с аннотацией @Bean:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
 
@Configuration
public class AppConfig {

   @Bean("beanOne")
   @DependsOn(value = { "beanTwo", "beanThree" })
   public BeanOne getBeanOne() {
      return new BeanOne();
   }

   @Bean("beanTwo")
   public BeanTwo getBeanTwo() {
      return new BeanTwo();
   }

   @Bean("beanThree")
   public BeanThree getBeanThree() {
      return new BeanThree();
   }
}
 или аннотировать класс бина:
@Component
@DependsOn(value = { "beanTwo", "beanThree" })
 public class BeanOne {
 
   @Autowired
   private BeanTwo beanTwo;

   @Autowired
   private BeanThree beanThree;

...............
}
Аналогичный механизм есть для синглтонов EJB стека JavaEE - только там в качестве параметров аннотации указывают имена классов и кроме того, учитывая что контейнер EJB по-умолчанию использует ленивое создание бинов (в отличие от Spring - он по-умолчанию создает бины сразу при старте приложения), то аналогичная аннотация используется как правило совместно с аннотацией @Startup, которая заставляет EJB создавать бины сразу при старте приложения - пример:
import javax.ejb.DependsOn;
import javax.ejb.Singleton;
import javax.ejb.Startup;

@Singleton
@Startup
@DependsOn("EjbConfig")
public class EjbMain {

    .....................

}

четверг, 21 июня 2018 г.

Spring - класс JdbcDaoSupport и паттерн DAO

Паттерн DAO - Data Access Object - очень коротко - это когда все методы доступа к данным вы собираете (инкапсулируете) в одном классе.
Класс JdbcDaoSupport является оболочкой для JdbcTemplate и позволяет удобно делать свои DAO расширяя класс JdbcDaoSupport - при этом в нашем классе для работы необходимо только указать через конструктор реализацию DataSource при создании экземпляра нашего DAO. Напишем свой класс DaoJDBC который расширяет класс JdbcDaoSupport и сделаем внедрение через конструктор бина, реализующего DataSource:

@Repository
public class DaoJDBC extends JdbcDaoSupport {
   
    @Inject   // или @Autowired
    public DaoJDBC(DataSource dataSource) {
        this.setDataSource(dataSource);
    }

    public void clearAll() {
        String sql = "delete from hosts";
        this.getJdbcTemplate().update(sql);
    }
.................// + другие операции с базой
}
при этом экземпляр JdbcTemplate будет создан автоматически и будет доступен из нашего DAO через this.getJdbcTemplate() - дальше в классе DaoJDBC нам доступен весь инструментарий JdbcTemplate через this.getJdbcTemplate().

Spring - класс JdbcTemplate и внедрение через конструктор - constructor injection.

Spring обеспечивает очень удобную поддержку JDBC с помощью класса JdbcTemplate, которому для работы необходимо только указать через конструктор реализацию DataSource при создании экземпляра JdbcTemplate.
Пусть например мы хотим воспользоваться паттерном DAO (Data Access Object - обьект доступа к данным ) - напишем свой класс DaoJDBC который расширяет класс JdbcTemplate и сделаем внедрение через конструктор бина, реализующего DataSource:
@Repository
public class DaoJDBC extends JdbcTemplate {
   
    @Inject   // или @Autowired
    public DaoJDBC(DataSource dataSource) {
        this.setDataSource(dataSource);
    }

    public void clearAll() {
        String sql = "delete from hosts";
        this.update(sql);
    }
.................// + другие операции с базой
}
 Здесь как раз и происходит внедрение через конструктор бина dataSource,  который мы рассматривали в предыдущей заметке. Дальше в классе DaoJDBC нам доступен весь инструментарий JdbcTemplate через this.
Можно сделать и по-другому - сделать реализацию бина JdbcTemplate в файле конфигурации приложения - а дальше внедрять его куда нам нужно и использовать:
@Inject   // или @Autowired
private JdbcTemplate jdbcTemplate;
В этом случае файл конфигурации приложения следующий:
@Configuration
@ComponentScan(basePackages = {"PKG"})
public class AppContext {

   @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate () {
        return new JdbcTemplate(this.dataSource());
    }
}
И еще - класс JdbcTemplate является безопасным в отношении потоков.
Исходники - https://sourceforge.net/projects/j-pkg/

Spring - интерфейс DataSource и JDBC пул соединений

Для доступа к данным в JDBC необходима реализация интерфейса javax.sql.DataSource - таких реализаций может быть много - в том числе и таких, которые на каждую операцию с БД открывают новое соединение - это очень затратно и медленно - мы их сразу опустим и рассмотрим пул как рабочую схему. Существует много реализаций пула:
- Apache commons dbcp2  (dbcp = database connection pool);
- HikariCP;
а также c3p0, BoneCP, Vibur и много других - настраиваются они все идентично и мы рассмотрим Apache.
Пул постоянно поддерживает несколько готовых соединений с базой - для текущей операции с базой практически мгновенно предоставляется готовое соединение из пула - а затем когда операция будет отработана это соединение менеджер пула помечает как свободное и возвращает в пул - в результате почти не тратится время на создание нового соединения если сравнивать с обычным подходом - это очень краткое и упрощенное описание работы пула.
Необходимо будет добавить зависимость в maven:
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.4.0</version>
</dependency>
в свою очередь эта зависимость "зацепит" и другую - commons-pool2. Будем использовать БД Derby в embedded-mode.
В файл конфигурации приложения Spring надо будет добавить бин, реализующий интерфейс javax.sql.DataSource с помощью класса BasicDataSource из apache commons dbcp2:
@Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }
Вот и все пока - а дальше мы покажем как его использовать.
Да чуть не забыл - вот пример с исходниками

суббота, 9 июня 2018 г.

Spring - внешний файл свойств приложения

Файл конфигурации приложения:

@Configuration
@ComponentScan(basePackages = {"jspv"})
@PropertySource(value = {"file:cfg/jivam.properties"})
//@PropertySource(value = {"classpath:hofat.properties"})
public class AppContext {
   
    @Value("${top}")
    private String top;
  
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

   
}
то что нужно добавить выделено жирным шрифтом - это аннотация @PropertySource и бин propertySourcesPlaceholderConfigurer. Теперь в любом компоненте Spring из пакета "jspv" можно внедрять свойства из внешнего файла:
    @Value("${top}")
    private String top;
пример содержимого файла свойств:
####
perevod=en
skin=com.jtattoo.plaf.mcwin.McWinLookAndFeel
top=JIVAM - Java Image Viewer And Manipulator, build 26-01-18.
###
исходники лежат на https://sourceforge.net/projects/jivam/files/?source=navbar

четверг, 24 мая 2018 г.

Spring - ресурсы

Spring предоставляет унифицированный механизм для доступа к ресурсам независимым от протокола способом. Приложение может работать с файловым ресурсом одинаковым образом, где бы он не был: в файловой системе, в classpath или на удаленном сервере.
   В основе всей поддержки ресурсов Spring находится интерфейс Resource. В нем определены 10 методов: contentLength(), exists(), getDescription(), getFile(), getFileName(), getURI(), getURL(), isOpen(), isReadable() и lastModified(). Есть еще 1 метод: createRelative() - он создает новый экземпляр Resource, используя путь относительно экземпляра, на котором он вызывается. Можно сделать свои реализации Resource, но в большинстве случаев будет применяться одна из встроенных реализаций для доступа к файлу (класс FileSystemResource), пути классов (класс ClassPathResource) или URL-ресурсам (класс UrlResource). Для поиска и создания экземпляров Resource Spring внутри использует другой интерфейс, ResourceLoader , и его стандартную реализацию DefaultResourceLoader. Однако обычно вы не будете взаимодействовать с DefaultResourceLoader, а вместо этого работать с другой реализацией ResourceLoader — ApplicationContext:
public class ResDemo {
   public static void main(String[] args) throws Exception{
      ApplicationContext ctx = new
                     AnnotationConfigApplicationContext(AppConfig.class);
      Resource r1 = ctx.getResource("file:///c:/tmp/tst.txt");
      Resource r2 = ctx.getResource("classpath:tst.txt");
      Resource r3 = ctx.getResource("http://www.google.com");
   }
}
Для протоколов file: и http: Spring возвращает экземпляр UrlResource. Она включает класс FileSystemResource, но DefaultResourceLoader вообще не использует этот класс. Причина в том, что стандартная стратегия загрузки ресурсов в Spring трактует URL и файл как один и тот же тип ресурса, но с отличающимися протоколами (т.е. file: и http:). Если экземпляр FileSystemResource обязателен, применяйте FileSystemResourceLoader. Получив экземпляр Resource, вы можете работать с содержимым ресурса по своему усмотрению, используя getFile(),
getInputStream() или getURL(). В ряде случаев, например, когда применяется протокол http:, вызов getFile() дает в результате исключение FileNotFoundException. По этой причине рекомендуется использовать метод getInputStream() - он будет работать со всеми возможными типами ресурсов.

События жизненного цикла бинов

Spring
В Spring их два - метод выполняемый сразу после создания бина + метод выполняемый перед уничтожением бина. Наиболее простой способ использования этих методов - это аннотировать их с помощью @PostConstruct и @PreDestroy соответственно - это аннотации из JSR-250, который вошел в Java SE начиная с версии 6 а также входит в Java EE CDI (Contexts and Dependency Injection) и который поддерживается Spring. Есть еще 2 способа сделать это (с помощью XML и интерфейсов) - но я специально не говорю об этом чтобы не "грузить" читателей, т к все эти способы эквивалентны.
@Component
public class MyBean {

    @PostConstruct
    public void afterBirn() {
       .....код выполняемый сразу после создания бина....
    }
    ................

    @PreDestroy
    public void beforeKill() {
       .....код выполняемый перед уничтожением бина....
    }

}
Методы должны быть только по 1 на событие + без аргументов + именно void. Методы уничтожения @PreDestroy работают для синглтонов - для бинов со scope=prototype Spring не запускает эти методы согласно проектному решению. Минус обратных вызовов уничтожения в Spring в том, что они не запускаются автоматически - нужно не забыть вызвать AbstractApplicationContext.destroy() перед закрытием приложения. Когда приложение выполняется как сервлет, указанный метод destroy() можно вызвать в методе destroy() сервлета. Для Desktop-приложений чтобы обеспечить выполнение методов @PreDestroy необходимо в контексте Spring зарегистрировать хук:

public static void main(String args[]) {
   // Shutdown Spring container gracefully in non-web applications !!
   //ConfigurableApplicationContext ctx = new
   AbstractApplicationContext ctx = new       AnnotationConfigApplicationContext(AppContext.class);
                ctx.registerShutdownHook();
 // main method exits, hook is called prior to the app shutting down...
 // @PreDestroy methods is called before close App               
}
Теперь перед выходом из метода main будет обеспечено выполнение всех методов отмеченных как @PreDestroy. Вместо AbstractApplicationContext можно использовать ConfigurableApplicationContext.
JavaEE
Точно такие же аннотации ( @PostConstruct и @PreDestroy ) поддерживают бины EJB (Enterprise Java Beans) и CDI (Contexts and Dependency Injection) из стека JavaEE. Stateful-бины EJB (сохраняющие состояние) помимо вышеуказанных 2-х также поддерживают еще 3 события:
@PrePassivate - метод выполняемый перед пассивацией - сериализацией и сохранением на носитель бина;
@PostActivate - метод выполняемый после активации - десериализации и восстановления бина с носителя;
@Remove - после выполнения этого метода контейнер EJB уничтожит бин.

Режимы загрузки бинов: Spring - @Lazy и JavaEE(EJB) - @Startup

Если в Spring бин аннотирован как @Lazy(true), то он будет создан только при первом обращении к нему:
@Component
@Lazy(true)
public class MyBean {
.........
}
Соответственно при @Lazy(false) бин создается сразу при старте приложения (режим по-умолчанию в Spring) - кстати для сравнения - аналогично работает аннотация @Startup для синглтонов EJB стека JavaEE. По умолчанию Spring создает все бины при старте приложения, т е работает режим @Lazy(false) - а вот в CDI и EJB из стека JavaEE все наоборот - там бины создаются лениво при первом обращении к ним. Если c помощью @Lazy аннотирован класс конфигурации Spring - то это будет работать для всех методов отмеченных аннотацией @Bean:
@Lazy(true)
@Configuration
@ComponentScan(basePackages = "my.app")
public class AppConfig {
    @Bean
    public Region getRegion(){
        return new Region();
    }
    @Bean
    public Country getCountry(){
        return new Country();
    }
}
Можно также использовать в классе конфигурации для отдельного бина:
@Configuration
@ComponentScan(basePackages = "my.app")
public class AppConfig {
    @Bean
    @Lazy(true)
    public Region getRegion(){
        return new Region();
    }

}
А также при внедрении совместно с @Autowired или @Inject:
    @Lazy(true)
    @Autowired
    private City city;

среда, 23 мая 2018 г.

Spring - внедрение через метод установки - Setter Injection

Spring поддерживает аннотацию @Inject, которая является частью специфика­ции JSR-330 (JavaEE CDI - Contexts and Dependency lnjection). Аннотация @Inject эквивалентна аннотации @Autowired в Spring. Для внедрения через метод нужно только добавить аннотацию @Autowired или @Inject к методу установки:

@Component("eprst")
public class Eprst {

   private Provider provider;

   @Autowired
   public void setProvider(Provider provider) {
       this.provider = provider;
   }
   ...
}
Для получения тех же результатов вместо @Autowired или @Inject можно также использовать @Resource(name="provider"). Аннотация @Resource присутствует в стандарте JSR-250 и поддерживает параметр name для уточнения DI. В итоге можно использовать несколько аннотаций для внедрения:
аннотация @Autowired, определяемая самим фреймворком Spring;
аннотация @Inject из JSR-330;
аннотация @Resource из JSR-250.

Обьявление бинов - аннотирование классов для автоматического определения

 Spring
       По умолчанию аннотация @ComponentScan в классе конфигурации Spring ищет классы, отмеченные одной из аннотаций:
     @Component – универсальная аннотация, указывающая, что класс
     является компонентом Spring;
     @Controller – указывает, что класс определяет контроллер
     Spring MVC;
     @Repository – указывает, что класс определяет репозиторий дан-
     ных;
     @Service – указывает, что класс определяет службу;
     @Named("myBean") - аннотация из JavaEE CDI - Contexts and Dependency Injection (JSR-330) - равнозначна @Component или @Service при аннотировании класса;
     Spring поддерживает аннотации JavaEE CDI (JSR-330 = @Inject, @Named, @Singleton и др) - только для этого необходимо добавить зависимость в Maven:
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
    </dependency>

Пример:
@Service("message")
public class HelloWorld {
    ..........
}
     Здесь аннотация @Service указывает на то, что этот бин предоставляет службы, которые используются другими бинами, параметр аннотации - имя бина. Во время начальной загрузки ApplicationContext Spring будет искать эти компоненты и создавать бины с указанными именами.
     Применение @Component дает тот же эффект, что и @Service. Обе аннотации инструктируют Spring о том, что класс является кандидатом на автоматическое обнаружение с применением конфигурации, основанной на аннотациях. На практике @Service является специализацией @Component, отражающей тот факт, что класс предоставляет бизнес-службу другим уровням приложения.
JavaEE
Здесь никакого класса конфигурации не нужно - все предоставляет сервер JavaEE - работает так называемый принцип конфигурации по умолчанию принятый для JavaEE - если вы ничего не настраиваете то работает конфигурация по умолчанию. Только для включения механизма  CDI - Contexts and Dependency Injection необходимо наличие даже пустого файла beans.xml в каталоге META-INF или WEB-INF - вот этот файл:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
после этого все классы (если они не отмечены исключающими аннотациями) будут источниками для бинов CDI.

@Named - бин CDI c именем из имени класса - только первая буква будет малой
@Named("myBean") - бин CDI c заданным именем
@Vetoed - появилась в JavaEE-7 - запрещает создавать бины CDI из отмеченного класса
Для бинов EJB работают аннотации:
@Singleton - без комментариев)
@Stateless - бин без сохранения состояния
@Stateful - бин с сохранением состояния

 

Bean Scope

Spring:
По умолчанию все компоненты Spring единичны.
Обьявление в бине:
@Component
@Scope("prototype")
public class Primer {
.........
}
или так:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Начиная с версии 4.3 в web-приложении можно также использовать аннотации:
@ApplicationScope
@RequestScope
@SessionScope
 Бывают:
- singleton = В каждом контейнере Spring может быть создан только
один компонент (по умолчанию);
- prototype = Позволяет создавать произвольное количество
компонентов (по одному на каждое обращение)
- request = Область действия ограничивается HTTP-запросом.         Применяется только в веб-приложениях Spring (Spring MVC);
- session = Область действия ограничивается HTTP-сеансом. Применяется только в веб-приложениях Spring (Spring MVC);
- global-session = Область действия ограничивается
глобальным HTTP-сеансом. Применяется только в портлетах;
      Понятие единичных компонентов ограничено областью действия контекста Spring. В отличие от истинных классов-одиночек, гарантирующих существование единственного экземпляра на каждый загрузчик классов (classloader), для единичных компонентов в Spring гарантируется только наличие единственного экземпляра компонента в контексте приложения – ничто не мешает создать экземпляр того же класса традиционным способом или даже создать несколько объявлений бина для одного и того же класса.
          Теперь для сравнения стек JavaEE:
JavaEE CDI:
@RequestScoped
@SessionScoped
@ApplicationScoped
@Dependent - по умолчанию - аналог "prototype" в Spring
@Singleton - В реализации CDI данного скоупа есть одна особенность: в процессе инъекции клиент получает ссылку на реальный объект созданный контейнером, а не proxy. В результате чего могут быть проблемы неоднозначности данных, если состояние синглтона будет меняться, а использующие его бины, например, были или будут сериализованы. 
JavaEE EJB:
@Stateful - сохраняющие состояние бины;
@Stateless - не сохраняющие состояние бины;
@Singleton  
И еще - по своей природе Request-бины потокобезопасны - отработал запрос и удалился.
 

Доступ к Spring context из бина

Здесь поможет интерфейс ApplicationContextAware - он объявляет метод setApplicationContext(). Контейнер Spring автоматически обнаружит компоненты, реализующие этот интерфейс, и обеспечит передачу бину ссылки на контекст:

public class ExampleService implements ApplicationContextAware {

     private ApplicationContext context;

     public void setApplicationContext(ApplicationContext context) {
          this.context = context;
     }
     ...
}

Взаимодействие между приложениями и Watch Service

Взаимодействие между разными приложениями Java можно сделать с помощью файловой системы, базы данных и JMS. Рассмотрим случай ФС - в Java ...