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

вторник, 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){...}

четверг, 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, на остальных не проверял

пятница, 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)