Показаны сообщения с ярлыком Java. Показать все сообщения
Показаны сообщения с ярлыком Java. Показать все сообщения

среда, 22 февраля 2017 г.

Java 8 example: two String arrays to one Set with Streams


Just an example


@Test
public void testArraysToStream() {
    String[] s1 = null;
    String[] s2 = null;
    Set<String> res = Stream.of(s1, s2)
                .filter(Objects::nonNull)  // <-- Filter out null values
                .flatMap(Stream::of)
                .collect(Collectors.toSet());
    Assert.assertNotNull(res);
    Assert.assertEquals(0,res.size());


    s1 = new String[]{"1","2"};
    Set<String> res2 = Stream.of(s1, s2)
                .filter(Objects::nonNull)  // <-- Filter out null values
                .flatMap(Stream::of)       // two arrays to one stream
                .collect(Collectors.toSet());
    Assert.assertNotNull(res2);
    Assert.assertEquals(2,res2.size());
    s2 = new String[]{"3","4"};
    Set<String> res3 = Stream.of(s1, s2)
                .filter(Objects::nonNull)  // <-- Filter out null values
                .flatMap(Stream::of)       // two arrays to one stream
                .collect(Collectors.toSet());
    Assert.assertNotNull(res3);
    Assert.assertEquals(4,res3.size());
    Assert.assertNotNull(res3.iterator().next());
}

вторник, 25 октября 2016 г.

Формат даты-времени в параметрах функции, в mvc-контроллере spring

При получении параметра из query-строки в http-запросе, можно задать шаблон для даты-времени (по умолчанию это ISO).
Задаётся аннотацией org.springframework.format.annotation.DateTimeFormat с указанием шаблона.

Пример


@RequestMapping(path = "/test", method = RequestMethod.GET)
public List<MyObject> getTest(
  @RequestParam(name="in_baseDateTime") @DateTimeFormat(pattern="dd.MM.yyyy HH:mm:ss") LocalDateTime baseDateTime){...}

вторник, 18 октября 2016 г.

Тест junit с hamcrest с проверкой коллекции на содержание чего-либо

Часто, я первой проверкой на коллекции в unit-тесте, ставлю простое определение есть ли что-то там или нет.

Обычно (по ленивой привычке) я делал это примерно так:


@Test
public void testNotEmptyList(){
    List<String> list = Arrays.asList("item_1","item_2");
    Assert.assertNotNull(list);
    Assert.assertTrue(list.size() > 0);
}

Но тут решил попробовать Hamcrest matchers и почему-то не сразу понял как там сделать такого рода проверку с коллекциями. Без коллекций всё достаточно понятно, а вот именно с коллекциями как-то не сразу въехал. Но в результате получилось вот так:

...
import org.hamcrest.collection.IsEmptyCollection;
import static org.hamcrest.CoreMatchers.*;
...
@Test
public void testNotEmptyList(){
    List<String> list = Arrays.asList("item_1","item_2");
    Assert.assertThat(list, notNullValue());
    Assert.assertThat(list, not(IsEmptyCollection.emptyCollectionOf(String.class)));
}

Версия Hamcrest должна быть не меньше 1.3

пятница, 30 сентября 2016 г.

Сертификация OCA Java SE 8 Programmer (1Z0-808). Complete

Сегодня сдал Oracle Certified Associate, Java SE 8 Programmer I (1Z0-808) 
(Результат 90%)
С одной стороны хорошо, а с другой - это только часть экзамена :)
В любом случае, полезно.  Но в нём только базовые знания, более глубоко в части II. Посмотрим, может и до неё доберусь.

Готовился со средней интенсивностью около 3-х месяцев. Пользовался следующими вещами:
Книга: 
Тесты:
  - всё, что бесплатно нашёл в интернете
  - книжка "OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z0-808" ( в ней, кстати, указано где тесты можно взять)
  - набор тестов http://enthuware.com/ (10$)

Этого, на мой взгляд вполне достаточно для успешной сдачи.


понедельник, 18 июля 2016 г.

Русские буквы в имени файла, отдаваемого сервлетом

Проблема
При отдаче файла сервлетом, имя ,которое сохраняется у пользователя не содержит русские буквы (вместо них пробелы).

Решение
Имя файла должно быть закодировано  javax.mail.internet.MimeUtility.encodeText()

Т.е. пример должен выглядеть как-то так:

response.setHeader("Content-Type", "application/file; charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename="+ MimeUtility.encodeText(dataFilePath.getFileName().toString()));

response.getOutputStream().write(FileUtils.readFileToByteArray(dataFilePath.toFile()));



понедельник, 30 мая 2016 г.

Напоминалка: ссылка на статическое поле в jаvadoc

Чтобы каждый раз заново не вспоминать, как ссылаться на статическое поле(константу) в javadoc-е:

/**
*  Ссылка на описание внешней константы {@link ru.pevgen.test.Consts#MY_CONST} в документации
**/

четверг, 5 мая 2016 г.

GreenMail - простая библиотека для unit-тестирования получения/приёма email в java (spring)

Собственно по теме, чтобы не забыть :  http://www.icegreen.com/greenmail/

Представляет из себя простой почтовый сервер (с разными протоколами  - pop,smtp,imap), который может быть внедрён в код и использован для простого тестирования кода, связанного с посылкой/приёмом email-ов.

Пример конфигурации и работы в тестовой spring-конфигурации

Фактически тут просто в тестовой конфигурации подменяется mail-сервер, на greenmail. А у самого greenmail-объекта есть методы для проверки того, что на него посылалось.
 
https://stackoverflow.com/questions/9661907/testing-greenmail-without-installing-a-smtp-server/9663399#9663399


import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.GreenMailUtil;
import com.icegreen.greenmail.util.ServerSetupTest; ...

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("ApplicationContext-Greenmail.xml")
public class EmailServiceIntegrationTest {

    /** Class under test */
    @Resource
    private EmailService emailService;

    private GreenMail greenMail;

    @Before
    public void startMailServer() {
        greenMail = new GreenMail(ServerSetupTest.SMTP);
        greenMail.start();
    }

    @After
    public void stopMailServer() {
        greenMail.stop();
    }

    @Test
    public void testPledgeReminder()
                throws InterruptedException, MessagingException {

        String mailText = "Hallo World";
        String mailSubject = "Hallo";
        String mailTo = "test@excaple.com";

        /** when: sending a mail */
        emailService.send(mailSubject, mailTo, mailText);

        assertTrue(greenMail.waitForIncomingEmail(5000, 1));
        Message[] messages = greenMail.getReceivedMessages();
        assertEquals(1, messages.length);
        assertEquals(mailText, messages[0].getSubject());       
        String body = GreenMailUtil.getBody(messages[0]).replaceAll("=\r?\n", "");
        assertEquals(mailText, body);       
    }

}
important: use port 3025
<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="javaMailProperties">
        <util:properties>
            <prop key="mail.debug">false</prop>
            <prop key="mail.transport.protocol">smtp</prop>
            <prop key="mail.smtp.port">3025</prop>
            <prop key="mail.smtp.auth">true</prop>
            <prop key="mail.smtp.user">test@mail.extern</prop>
            <prop key="mail.smtp.host">localhost</prop>
            <prop key="mail.smtp.from">test@mail.extern</prop>
        </util:properties>
    </property>
    <property name="username" value="test"/>
    <property name="password" value="xxx"/>
    <property name="defaultEncoding" value="utf8" />
</bean>
Then this configuration will configure a Spring JavaMailSender to send its mails to the GreenMail-Server started and finished by the test code.

четверг, 5 ноября 2015 г.

Решение для "Unable to locate Spring NamespaceHandler for XML schema namespace"

Дано

Jar-файл, собранный с зависимыми библиотеками прямо внутри (это когда просят сделать весь проект в одном файле, и тогда сторонние библиотеки разжимаются и копируются в целевой jar в виде классов);
Конфигурация spring в xml-файлах, также внутри jar-а
Внешний проект запускает метод, в котором создаётся ApplicationContext spring-а.
Для сборки используется ant

Проблема

При попытке загрузить конфиг во внешнем проекта получаеv ошибку при создании ApplicationContext : "Unable to locate Spring NamespaceHandler for XML schema namespace"

Что дали поиски проблемы 
В двух словах, при сборке нескольких spring-библиотек в один файл, перекрываются схемы и другие настройки, которые хранятся в META-INF/spring.* файлах, которые для каждой библиотеки спринга свои. Т.е. остаются в итоге те, что собирались последними - они просто накрывают предыдущие.

Источники

http://stackoverflow.com/questions/8523997/unable-to-locate-spring-namespacehandler-for-xml-schema-namespace-http-www-sp/8574629#8574629
http://stackoverflow.com/questions/23574979/creating-an-uber-jar-with-spring-dependencies (это уже почти решение)

Решение (быстрое)

По мотивам описанного на stackoverflow был сделан отдельный ant-скрипт, который создаёт общие META-INF/spring.* схемы и настройки. Затем эти файлы были добавлены в папку с META-INF, которая подкладывается в финальный jar при сборке.
А в процессе сборки jar-а, исключаются те spring-настройки, которые разжимаются из spring-библиотек.

Скрипт ant, который собирает из отдельных spring-настроек, всё в общие настройки для всех библиотек spring-а, которые используются в проекте.

<?xml version="1.0" encoding="UTF-8"?>
<project name="springschemamake" default="create.spring.schema.full">

    <taskdef resource="net/sf/antcontrib/antlib.xml" classpath="lib/ant-contrib-1.0b3.jar"/>

    <property name="lib.dir" value="${basedir}/lib"/>
    <property name="dist.dir" value="${basedir}/templib"/>
    <target name="create.spring.schema.full">

        <!-- iterate over the dependencies -->
        <for param="file">
            <path>
                <fileset dir="${lib.dir}">
                    <include name="**/*spring*.jar"/>
                </fileset>
            </path>
            <sequential>

                <local name="jar.name"/>
                <basename  property="jar.name" file="@{file}"/>
                <!-- put the spring.* jars into special sub-directories -->
                <mkdir dir="${dist.dir}/${jar.name}"/>
                <unzip dest="${dist.dir}/${jar.name}" src="@{file}">
                    <patternset>
                        <include name="**/META-INF/spring.*"/>
                    </patternset>
                </unzip>

            </sequential>
        </for>

        <mkdir dir="${dist.dir}/META-INF"/>
        <!-- build the concatenated spring.* files -->
        <mkdir dir="${dist.dir}/META-INF"/>
        <concat destfile="${dist.dir}/META-INF/spring.handlers" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.handlers"/>
        </concat>
        <concat destfile="${dist.dir}/META-INF/spring.schemas" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.schemas"/>
        </concat>
        <concat destfile="${dist.dir}/META-INF/spring.tooling" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.tooling"/>
        </concat>
        <concat destfile="${dist.dir}/META-INF/spring.factories" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.factories"/>
        </concat>

        <delete includeemptydirs="true">
            <fileset dir="${dist.dir}" includes = "**/*spring*/**" excludes="/META-INF/**"/>
        </delete>
    </target>
</project>

Решение правильное, но пока не сделанное
Включить сборку схем spring-а в общий ant-скрипт. Он и до такого включения был довольно большой, поэтому потребуется некоторое время на рефакторинг и реализацию. 

четверг, 12 марта 2015 г.

Перехват обращений к статическим ресурсам в Spring(3.2.2)


Дано

MVC-конфигурация с контроллерами и статическими ресурсами, которые не проходят через DispatcherServlet.

Требуется

"Перехватить" вызовы статических ресурсов (например, для подсчёта количества обращений к ним, времени, которое требуется на отдачу сервером и т.п.)


Конфигурация

web.xml:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>testServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


applicationContext.xml

    <!-- статические ресурсы (которые не должны проходить через DispatcherServlet) -->
    <mvc:resources mapping="/static/**" location="/static/"/>
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/js/*"/>
            <bean class="ru.pevgen.test.RequestProcessingTimeInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

RequestProcessingTimeInterceptor.java

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class RequestProcessingTimeInterceptor extends HandlerInterceptorAdapter {
    private static final Logger logger = LoggerFactory.getLogger(RequestProcessingTimeInterceptor.class);
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        logger.info("Request URL::" + request.getRequestURL().toString()
                + ":: Start Time=" + System.currentTimeMillis());
        request.setAttribute("startTime", startTime);
        //if returned false, we need to make sure 'response' is sent
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("Request URL::" + request.getRequestURL().toString()
                + " Sent to Handler :: Current Time=" + System.currentTimeMillis());
        //we can add attributes in the modelAndView and use that in the view page
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        long startTime = (Long) request.getAttribute("startTime");
        logger.info("Request URL::" + request.getRequestURL().toString()
                + ":: End Time=" + System.currentTimeMillis());
        logger.info("Request URL::" + request.getRequestURL().toString()
                + ":: Time Taken=" + (System.currentTimeMillis() - startTime));
    }
}

Пример честно содран отсюда: http://www.journaldev.com/2676/spring-mvc-interceptors-example-handlerinterceptor-and-handlerinterceptoradapter
Я использовал spring 3.2.2, на остальных не проверял

понедельник, 1 сентября 2014 г.

Доступ к Appender-ам logback в коде

Мы почти везде используем для логирования logback. Иногда надо получить доступ к его Appender-ам в runtime. Зачем ? Например, если я хочу получить пути файлов, в которые идёт запись логов - чтобы архивировать и передать по http клиенту (если это, скажем, web-приложение).

Пример доступа :

LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
// проходим по всем loggers
for (ch.qos.logback.classic.Logger logger : lc.getLoggerList()) {
    // от каждого logger получаем его appenders
    Iterator iter = logger.iteratorForAppenders();
    while (iter.hasNext()) {
        Appender appender = (Appender) iter.next();
        // тут приходится анализировать конкретные классы 
        if (appender instanceof FileAppender) {
                  ....
        }
    }
}

пятница, 29 августа 2014 г.

Два контекста Spring MVC. Порядок загрузки.

По мотивам прочтения вот этого поста http://sgdev-blog.blogspot.sg/2014/07/spring-mvc-common-mistakes.html

В web приложениях со springframework, я чётко разделяю srping-овыйе контексты -
  - mvc.xml:  настройки, относящиеся к view, а точнее к "внешнему" web-взаимодействию (сюда входят намапленные web-контроллеры, ресурсы и т.п.)
  - services.xml: настройки, которые содержат непосредственно компоненты логики, в том числе, связки с БД, репозитории, и т.п.

Собственно, сделано это, чтобы не грузились компоненты по два раза, в двух контекстах. А в статье указанной выше  описано почему это происходит и разумные рекомендации. Которым я оказывается уже следую.


Чего я не знал, и что иногда имеет важное значение - это последовательность загрузки контекстов.

1. Сначала грузится тот контекст, что описан в ContextLoadListener(в моём случае services.xml)
2. Потом, при инициализации сервлета, грузится контекст, описанный в DispatcherServlet (mvc.xml в параметре contextConfigLocation или, по умолчанию, ${servlet.name}-context.xml)

вторник, 5 августа 2014 г.

Перевод строк в PLSQL. Особенности

Для того, чтобы в строке, сформированной в PLSQL и переданной в java можно было увидеть перевод строки - "\r\n"(0D0A), надо использовать стандартную plsql-константу "utl_tcp.CRLF".
Если использовать chr(13), то в java мы увидим только "\r", хотя в PLSQLDeveloper визуально будет казаться, что перевод строки есть. Это происходит из-за того, что в своём редакторе PSQLDeveloper, при показе, добавляет недостающие символы перевода строки .

четверг, 27 марта 2014 г.

Java-библиотека для работа с файлами свойств

Попалась на глаза интересная библиотека для работы со свойствами (Properties) в java : http://owner.aeonbits.org/

Использовать ещё не пробовал, но набор возможностей впечатляет: и аннотации, и значения по умолчанию, и разнообразные варианты загрузки, и объединение нескольких наборов свойств, и т.д. и т.п. :

  • Loading strategies
  • Importing properties
  • Parametrized properties
  • Type conversion
  • Variables expansion
  • Reload and Hot Reload
  • Accessible and Mutable
  • Debugging
  • Disabling features
  • Metaconfiguring
  • XML support
  • Event support

Лицензия BSD. Соответственно, в исходниках всегда можно покопаться. Документация хорошая.

В общем показалась очень и очень впечатляющей библиотекой. Попробую в ближайшее время обязательно.

среда, 6 ноября 2013 г.

Заметка про Сервлеты: getParameter/getParameterMap и getInputStream

Невнимательное чтение документации, а также принцип Гейзенберга попортили немало крови. Поэтому заношу сюда.

А всё ведь просто оказалось:
if the parameter information came in as encoded POST data, it may not be available if the POST data has already been read manually using the getReader() or getInputStream() method of ServletRequest (because POST data can be read only once).

Что на человеческом означает - если http-вызов осуществляется методом POST, и при этом параметры передаются в данных, т.е. после пропущенной строки (с заголовком application/x-www-form-urlencoded), а не в QueryString, то ВЫЗОВ getParameterValue(s)/getParameterMap ОБНУЛЯЕТ InputStream. Причём даже, если этот вызов был сделан в дебаггере (на что я и наступил).
Аналогично и в обратную сторону: если сначала вызвать getInputStream(), то параметров в getParameter* не будет.

среда, 4 сентября 2013 г.

Хорошие советы про ant

Хорошие советы, "странный" перевод.

Содержание:
  • Сборка только в IDE
  • Копировать-и-вставить
  • Длинные объекты
  • Большие файлы сборки
  • Не чистить
  • Сильно закодированные значения
  • Успешная сборка при неуспешных тестах
  • Волшебные машины
  • Недостаток стиля

Автоматизация для людей: Очищаем скрипты сборки от запахов

вторник, 5 февраля 2013 г.

Логирование SQL в mybatis

Чтобы логировать в SL4J(с logback) SQL-запрос и его параметры, надо настроить логгер c уровнем DEBUG:

    <logger name="java.sql" level="DEBUG">
        <appender-ref ref="STDOUT"/>
    </logger>


а чтобы логировать ещё и результаты, возвращаемые запросом, необходимо установить уровень TRACE :

    <logger name="java.sql" level="TRACE">
        <appender-ref ref="STDOUT"/>
    </logger>



Детальнее : http://www.mybatis.org/core/logging.html

четверг, 29 ноября 2012 г.

Загрузка ресурсов из WEB-INF/lib/jar-ов


Проблема

    При попытке доступа к ресурсу из каталога веб-приложения WEB-INF/lib (а именно к jar-библиотеке), по URL полученному из ServletContext-а не получается создать объект класса JarFile. На данный момент проверено под Tomcat 7.x (позже проверю под OAS и WLS)

Решение

    Для того, чтобы ресурс мог потом быть доступен через JarFile (например для чтения описания readme.txt из файла внутри jar-а), необходимо использовать хак :

  1. Добавить к строке, полученного url-а ресурса "jar:" впереди и "!\" позади. 
  2. Использовать url.openConnection()

Пример


// получить список ресурсов в WEB-INF/lib
Set<String> jarLibPaths = servletContext.getResourcePaths("/WEB-INF/lib");

for (String jarPath : jarLibPaths) {

  // так будет неправильное преобразование типа ClassCastException, т.к. открытое соединение не JarURLConnection
//  URL urlBad = servletContext.getResource(jarPath);  
//  JarURLConnection conn = (JarURLConnection) urlBad.openConnection();

  // так будет FileNotFoundException
//   URL urlBad2 = servletContext.getResource(jarPath);  
//   JarFile jarFile = new JarFile(urlBad2.getFile());
//   JarFile jarFile = new JarFile(urlBad2.toString()); // та же проблема

//  URL urlBad3 = new URL("jar:" + servletContext.getResource(jarPath).toString() + "!/");  
//  JarFile jarFile = new JarFile(urlBad3.getFile());   // снова ошибка

  
  // а так всё будет работать
  URL urlOk = 
      new URL("jar:" + servletContext.getResource(jarPath).toString() + "!/");  
  JarURLConnection conn = (JarURLConnection) urlOk.openConnection();
  JarFile jarFile = conn.getJarFile();
}

четверг, 8 ноября 2012 г.

Oracle WLS и springframework

Спасибо "суровому челябинскому программисту" за подробное описание как включить и настроить интеграцию SpringFramework и консоли администрирования Oracle WLS.
http://samolisov.blogspot.ru/2012/11/spring-framework-weblogic.html
Существенно может помочь в эксплуатации. Будем пробовать.

вторник, 4 сентября 2012 г.

spring-jdbc или mybatis


Несколько плюсов и минусов при сравнении spring-jdbc и mybatis

spring-jdbc
+Удобно вызывать функции Oracle, возвращающие значения
+Удобно(просто) вызывать простые запросы
-Создавать сложные объекты (с коллекциями связанных объектов) можно только "вручную"
-Не очень хорошее логирование (обещают сделать в v.3.2)

mybatis
+Сложные объекты, с коллекциями связанных, создаются через описание
-В определённых случаях поведение сложных объектов не соответствует ожидаемому (например, если разные машины содержат одинаковый груз, то этот груз - фактически один объект, и следовательно, при изменении данных одного груза, изменятся все остальные)

Вывод
Идеал не достижим :) - для разных случаев использования, удобнее пользоваться разными библиотеками. Там, где простые объекты - spring-jdbc, где сложные - mybatis (с осторожностью и тестами)