Страницы

Поиск по вопросам

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

четверг, 2 апреля 2020 г.

Spring Security: как сделать разделение ролей?

#java #spring #spring_mvc #spring_security

                    
Как в Spring сделать разделение ролей? Если, например, стоит, что администратор (значение
из базы) true, то открыта конкретная ссылка для просмотра в jsp и на эту страницу (на
которую вела ссылка) вход закрыт прочим пользователям (с другими ролями).

Это делается через Spring Security, верно?

UPD: Буду рад любым советам. Кто-то поставил минус, просьба отписаться в комментариях.
    


Ответы

Ответ 1



Как-то так, например some_page.jsp Админка SomeController.java @Controller public class SomeController { @RequestMapping(path = "/admin", method = RequestMethod.GET) @PreAuthorize("hasRole('ROLE_ADMIN')") public String admin(Model model) { return "admin"; } } Совет простой - попробуйте почитать документацию, там всё очень подробно описано.

пятница, 13 марта 2020 г.

Spring перехват ошибок (404)

#spring_mvc #http_status_code_404


Всем привет. Изучаю Java Web и Spring MVC. Столкнулся с такой проблемой. Нужно сделать
свою страницу ошибок. Я не до конца понял как эти ошибки отлавливать. Понятно как отлавливать
ошибки в контроллере типа ("page/${id}"). Мы ищем инфу по данному id и если такой нет,
выбрасываем исключение или тупо редиректим куда нибудь. Тут всё понятно. Но я хочу,
что бы это всё делалось не в каждом контроллере, а где то в одном месте и не важно
по какому роуту была эта ошибка.
Что то в роде такого. 
Прописать, что для всех исключений (404), мы резолвим такую jsp и передаём в модель
то-то и то-то. И вообще пофиг какой роут вызвал эту ошибку.
В общем как то так.
Заранее благодарен.
    


Ответы

Ответ 1



Spring Web построен на сервлетах. Чтобы наиболее полно ответить на ваш вопрос, давайте посмотрим под DispatcherServlet, который в Spring отвечает за обработку всех запросов. Вот код, который вызывается, если Spring не находит метод контроллера, который должен обрабатывать запрос: protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { if (pageNotFoundLogger.isWarnEnabled()) { pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) + "] in DispatcherServlet with name '" + getServletName() + "'"); } if (this.throwExceptionIfNoHandlerFound) { throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), new ServletServerHttpRequest(request).getHeaders()); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND); } } Из него видно, что: Пишется сообщение в лог Если установлен флаг throwExceptionIfNoHandlerFound, то бросается исключение NoHandlerFoundException Если флаг throwExceptionIfNoHandlerFound не установлен, то вызывается метод объекта HttpServletResponse sendError(); По умолчанию, флаг сброшен throwExceptionIfNoHandlerFound = false, т.е. вызывается метод sendError(), и как результат контейнер сервлетов переадресует вас на страницу ошибки. Чтобы переопределить эту страницу на собственную, согласно спецификации JEE. вам нужно задать параметр error-page в дескрипторе развертывания web.xml 404 /404 Соответственно, нужно создать и метод контроллера, который будет замапен на URL /404 и вернет нужную страницу. Может случиться так, что вам потребуется выкинуть пользователя на страницу 404 вручную. Глобально это можно сделать через аннотацию @ControllerAdvice, действия перечисленные в классе помеченным данной аннотацией являются дефолтными для всех контроллеров. Создаем собственный класс исключения: public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException() { super(); } } Ловим это исключение глобально для всех контроллеров, и перекидываем на /404 @ControllerAdvice public class ExceptionInterceptor { @ExceptionHandler(ResourceNotFoundException.class) public ModelAndView handleError404(HttpServletRequest request, Exception e) { ModelAndView mv = new ModelAndView("/404"); return mv; } } Соотвественно чтобы выкинуть пользователя на 404 из любого контроллера, просто бросаете это исключение. Передать дополнительный параметры можно просто добавив нужных полей в класс исключения: собственный код ошибки, сообщение и т.д. Теперь когда у нас есть глобальный @ExceptionHandler можно вспомнить про флаг throwExceptionIfNoHandlerFound и сделать так, что 404 будет обрабатываться только в нем. Меняем значение флага: public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override public void customizeRegistration(ServletRegistration.Dynamic registration) { registration.setInitParameter("throwExceptionIfNoHandlerFound", "true"); } } И дописываем в @ExceptionHandler перехват соответствующего класса исключений: @ExceptionHandler(value = {ResourceNotFoundException.class, NoHandlerFoundException.class}) public ModelAndView handleError404(HttpServletRequest request, Exception e) { ModelAndView mv = new ModelAndView("/404"); return mv; } При этом параметры error-page обычно все равно стараются оставлять "на всякий пожарный случай" как fallback, чтобы пользователь вдруг не увидел трекстрейс, который Tomcat показывает в своих сообщениях по дефолту. Полезные ссылки на официальные примеры: error-page Spring error handling

воскресенье, 8 марта 2020 г.

Как передать свой объект из jsp в контроллер

#java #hibernate #jsp #spring_mvc #jstl


У меня есть User, в нем есть ссылка на Role, и например на странице editUser мне
надо собрать объект User в контроллер spring MVC(в параметры контроллера добавляю User
user), такие объекты как
name(String),email(String), age(int) собираются в User нормально, а чтобы добавить
туда Role мне надо редактируемого User'a достать из БД извлечь его роль и засетить
в новый объект User и только потом делать update в hibernate. 

Так вот вопрос можно ли как то spring'ом сразу User собрать вместе с Role из jsp
страницы?

Пробовал указать поле в верстке 

input type="hidden" name="role" value"${user.role}"


но тогда вообще контроллер не находит и редиректит на страницу 400.

P.S User добавляю из get-контроллера в jsp, тоесть переменные User можно достать в jsp
    


Ответы

Ответ 1



Как по мне, на уровне клиента это невозможно. Как вариант, тебе нужно сперва запросом отправить id нужного объекта, а потом используя id получить его. Я бы сделал бы это вот так html ... ... ... ... Внутри метода* .........@ModelAttribute("user") User user, @RequestParam("id") Integer id) { .... Role role = (Role) session.createQuery("from Role role where role.id=:id").setParameter("id", id).list().get(0); user.setRole(role); .... }

Ответ 2



input type="hidden" name="role" value"${user.role} Может вместо этого надо: input type="hidden" name="role" value="${user.role}"

понедельник, 24 февраля 2020 г.

Введение в Spring

#spring #spring_mvc


Здравствуйте. В общем я хочу познакомится с Spring Framework MVC.. У меня есть знания
Java 8, HTML/CSS, XML. Проблема в том, что у меня не такая структура проекта как в
уроках. Я хочу использовать чисто аннотации для конфигурирования, ну конечно XML, там
где без него никак (конфигурирование Maven). Мне сказали скачать Maven, Tomcat. Все
скачал, поставил, переменные прописал и в настройках IntelliJ IDEA 15.1 поставил путь
к Maven. Spring MVC скачал как зависимость, (New -> Project... -> Spring -> Spring
MVC). Создал.. В итоге у меня такая структура проекта: 



Нет pom.xml... Я не пойму почему, ведь все подключено и указано. Ну ладно, это поправимо
через Add Framework Support -> Maven, и добавляется pom.xml . Но содержимое сильно
отличается от того, что в уроках по Spring. 
Пробовал эти уроки: https://www.youtube.com/watch?v=YN66kDMjnYM , вроде без конфигурирования
Spring'a через XML, но для меня там все сложно.. Для меня это как темный лес, человек
уже сразу на готовом проекте показывает, а я его ещё не создал толком даже. Вот уже
два дня сижу и не могу Hello World сделать, везде либо XML, либо у меня что-то в структуре
не так (да, по большей части это проблемы с моими руками, наверное). Что я хочу:


Что с этим Maven'ом ? Где pox.xml автоматически созданный?
Можете кто-то, пожалуйста, скинуть просто Spring MVC веб-приложение
(типичный Hello World) по самым новым стандартам.. Ну так как сейчас
пишут на Spring MVC..
Есть какие-то уроки, даже пусть на английском (туториалы), где все
на аннотациях и, как бы сказать, "по-современному", в том тоне, что
сейчас популярен и востребован. Поделитесь пожалуйста.
У меня минимальные знания ВЕБ'а. Правильно ли я понял: На Spring MVC
мы пишем веб-приложение (сервер), который в свою очередь работает с
помощью Java сервлетов. Потом страница на HTML/CSS с помощью
JavaScript обращается к нашему веб-приложению (серверу). На самом
Spring MVC мы не занимаемся написанием непосредственно страницы
(HTML/CSS) или все же занимаемся, иначе для чего JSP (а этот
разновидность HTML). Пролейте свет сюда.


Вопросы не очень грамотные, местами глупые, сразу прошу извинить. 
    


Ответы

Ответ 1



Его там не будет. Для того, чтобы он там был, надо создавать maven-проект с архетипом spring-mvc (название может быть другим). Первый результат поисковика по spring mvc annotation example Эти уроки называются официальная документация. Но она большая, поэтому можешь попробовать так. Вроде да, но нет. Первый делом браузер идет с GET-запросом к серверу, с просьбой дать ему страницу. Тут можно отдать статическую страницу (HTML+javascript), а потом через javascript запросить у сервера динамические данные. Либо сгенерировать её при помощи того же JSP, и отдать сгенерированный html-контент. Spring как раз таки содержит такую штуку, которая из JSP может сделать HTML. Можно также использовать гибридный подход. Тут дело предпочтения. Изучать spring сразу не стоит точно. Сначала стоит изучить обычные сервлеты.

четверг, 13 февраля 2020 г.

Java web app не видит статические файлы. JS, CSS и т.д. Развертываю на Spring MVC

#maven #spring #spring_mvc #java #tomcat


Я использую Spring mvc+maven+tomcat. Когда я пишу java web app проект в NetBeans,
я его периодически запускаю кнопкой F6 (Run Project). То есть я не произвожу его явное
развертывание на Томкате. Этим я хочу проверить только что внесённые изменения.  И
это стало бо-о-о-ольшой ошибкой для меня. Это аукнулось.
Имеется вот такая структура папок-файлов: 



В form.jsp вызываю свои файлы так:





И конечно же, в конфиге спринга прописал пути, ну чтобы спринг мог находить статические
ресурсы: 





Суть проблемы:
Когда я запускаю кнопкой F6 всё работает хорошо. Все JS, CSS подключаются как надо.
Но когда я делаю mvn package и выкидываю готовый war в папку webapps томката, он
не находит эти статические файлы. Открывается только голая form.jsp безо всяких CSS, JS.
Что делать?

Вторая проблема: очень странно, что при запуске F6 путь URL в браузере не такой,
как при запуске задеплоенного war. Если я запускаю F6  - путь такой http://localhost:8080/spring-chat-test/
 A если запускаю задеплоенный, то http://localhost:8080/spring-chat-test-1.0/
    


Ответы

Ответ 1



Это решилось использованием jstl: "> Они добавляют контекст (путь) откуда установлено веб приложение.

Ответ 2



Попробуйте добавить контекст приложения в URL JS-скриптов и стилей. По поводу второй проблемы. Скорее всего в NetBeans в настройках проекта явно прописан контекст /spring-chat-test, в то время как в томкате контекст == имени папки, в которую задеплоилось приложение (в вашем случае это spring-chat-test-1.0). Самое простое - настроить maven, чтобы тот собирал war с нужным именем. Например так: ... spring-chat-test

четверг, 23 января 2020 г.

Редирект в Spring MVC

#spring_mvc #spring #java #redirect


В последнее время мне никто не отвечает, но я всё равно почему-то спрашиваю... 

Привет, товарищи. По прежнему мусолю официальный пример на оф. сайте Spring, переписываю
их код с помощью аннотаций. Там есть формочка, чтобы увеличить цену, вот такая:



По нажатии кнопки Execute цены на товар увеличиваются на заданный процент, и после
этого пользователя перебрасывает на страницу со списком товаров.
Вот их код, который это обслуживает:

public ModelAndView onSubmit(Object command)
            throws ServletException {

        int increase = ((PriceIncrease) command).getPercentage();
        logger.info("Increasing prices by " + increase + "%.");

        productManager.increasePrice(increase);

        logger.info("returning from PriceIncreaseForm view to " + getSuccessView());

        return new ModelAndView(new RedirectView(getSuccessView()));
    }


Опустим работу с логированием, базой данных, остановимся только на возвращаемом методом
значении. Моя задача - сделать так, чтобы после увеличения цен на странице с товарами
появилось уведомление, мол, "цены успешно повышены", а при обновлении страницы пропало.
Я пробовал так:

@RequestMapping(method = RequestMethod.POST)
public ModelAndView post(Model model) {
    model.addAttribute("hello", "world");
    return new ModelAndView(new RedirectView("/home.htm"));
}


так:

@RequestMapping(method = RequestMethod.POST)
public ModelAndView post(Model model) {
    return new ModelAndView("redirect:/home.htm").addObject("hello", "world");
}


так:

@RequestMapping(method = RequestMethod.POST)
public ModelAndView post(Model model) {
    return new ModelAndView("redirect:/home.htm", "hello", "world");
}


так:

@RequestMapping(method = RequestMethod.POST)
public View post(Model model) {
    model.addAttribute("hello","world");
    return new RedirectView("/home.htm");
}


А ещё тут предлагают вызывать метод, отвечающий за страницу со списком товаров. Все
мои попытки, представленные выше, передают мой hello=world в url, получается 


  http://localhost/home.htm?hello=world


Соответственно если я сделаю пометку таким образом, она никуда не денется после обновления
страницы. Как мне быть? Может, есть какой-то способ передать в модель той страницы
дополнительные атрибуты? На PHP я это делал с помощью $_SESSION, может, и здесь сессии
использовать?

P. S. Спасибо тем, кто доскроллил вопрос до конца, надеюсь получить ответ, палец
вверх и принятый ответ гарантирую.
    


Ответы

Ответ 1



может и здесь сессии использовать? Ну а как еще? Это же по факту состояние, которое надо сбрасывать после первого показа. Ну куки еще разве что. Вордпресс, конечно, живет себе гет-параметрами, но он вордпресс. Вообще эта штука называется flash messages, единственная проблема будет со списками, когда надо по одному ключу забить кучу сообщений https://stackoverflow.com/questions/11881720/multiple-flash-messages-in-spring-mvc В последнее время мне никто не отвечает, но я всё равно почему-то спрашиваю... палец вверх и принятый ответ гарантирую. -_-

Бесконечное извлечение из двух set- ов

#java #spring_mvc


Есть 2 pojo класса: 

public class Component {
    private String ID;
    private Set environmentSet;

public class Environment {
    private String host;
    private String name;
    private Set components;


И контейнер для второго: 

public class EnvironmentContainer {
    private List allEnvironments;


Мне по данному пути нужно вернуть лист из контейнера

@RequestMapping("/environments")
    @ResponseBody
    public List getAllEnvironments() {
        return environmentcontainer.getAllEnvironments();
    }


При выполнении возникает ошибка, скорее всего из за взаимосвязи между pojo, построенной
на set:


  java.lang.IllegalStateException: Cannot call sendError() after the
  response has been committed   at
  org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:466)


Слышал в спринге есть аннотация, которая обрубает бесконечное извлечение, или есть
другие пути как это исправить?
    


Ответы

Ответ 1



Нижеизложенное справедливо если вы используете Jackson для сериализации ваших бинов. В Jackson есть аннотации, позволяющие управлять сериализацией бинов, связанных циклическими ссылками. Это @JsonManagedReference и @JsonBackReference. Первой помечается поле или геттер основного класса (с которого начинается сериализация) со ссылками на зависимые объекты, второй - поле или геттер зависимого класса со ссылкой на основной: public class Environment { @JsonManagedReference private Set components; } public class Component { @JsonBackReference private Set environmentSet; } Теперь Jackson не уйдёт в бесконечную рекурсию при сериализации. Другая аннотация, которая может помочь - @JsonIgnore. Поля, помеченные такой аннотацией, не включаются в сериализованный объект. Если объекты класса Component никогда не будут сериализовываться отдельно от объектов Environment, то можно пометить поле environmentSet аннотацией @JsonIgnore, эффект будет аналогичный. Ещё один вариант - @JsonIdentityInfo. Этой аннотацией помечается весь класс. С её помощью можно подсказать Jackson'у, как определить уникальный ключ объекта (например, по полю ID): @JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "ID") public class Environment { ... } Встретив при сериализации объект с ключом, который он уже обрабатывал, Jackson подставит вместо него его ключ. Также есть возможность управлять сериализацией циклических ссылок через @JsonView (подробнее об этом и других способах читайте в этой статье), но это требует регистрации в Spring'е кастомного сериализатора.

среда, 22 января 2020 г.

Логгирование в разные файлы в Spring Boot

#java #spring #spring_mvc #spring_boot


Нужно вести логи приблизительно таким образом:


Информационные сообщения сохраняются в одном файлу
Сообщения об ошибках сохраняются в другом файле
Может быть, каждый день логи нужно сохранять в новой директории, имя которой создается
из текущей даты в текстовом формате


Как это можно сделать на спринге?
    


Ответы

Ответ 1



1) отключаем встроенный логгер спринга org.springframework.boot spring-boot-starter logback-classic ch.qos.logback log4j-over-slf4j org.slf4j 2) добавляем в зависимости какой хотим, к примеру Log4j 3) конфигурирем его как уже душе угодно %d{HH:mm:ss} [%t] %-5level %c{1} - %msg%n %d %p %c{1.} [%t] - %msg%n %d %p %c{1.} [%t] - %msg%n ]

пятница, 10 января 2020 г.

Как запустить свое веб-приложение на Jetty в IntelliJ Idea

#java #xml #spring_mvc #jetty


Здравствуйте!
Я разрабатываю Spring MVC веб-приложение и мне нужно чтобы оно работало на Jetty-сервере,
не требующем внешнего контейнера.
Я добавил сервер jetty стандартным образом через "edit run configuration".


Он также успешно отображается в Project Structure.



Но, когда я запускаю Jetty то он просто не запускается и выдает мне следущее:

"C:\Program Files\Java\jdk1.8.0_65\bin\java" -DSTOP.PORT=0 -Dcom.sun.management.jmxremote=
-Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false -DOPTIONS=jmx -Didea.launcher.port=7535 "-Didea.launcher.bin.path=C:\Program
Files (x86)\JetBrains\IntelliJ IDEA 15.0.4\bin" -Dfile.encoding=windows-1251 -classpath
"D:\jetty-distribution-9.3.7.v20160115\start.jar;C:\Program Files\Java\jdk1.8.0_65\lib\tools.jar;C:\Program
Files (x86)\JetBrains\IntelliJ IDEA 15.0.4\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain
org.eclipse.jetty.start.Main --module=jmx C:\Windows\Temp\context4config\jetty-contexts.xml
[2016-03-03 07:17:55,511] Artifact DVDExchange:war exploded: Server is not connected.
Deploy is not available.
Detected server http port: 8080
java.nio.file.AccessDeniedException: C:\Windows\Temp\context4config\jetty-contexts.xml
    at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83)
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:90)
    at sun.nio.fs.WindowsLinkSupport.getRealPath(WindowsLinkSupport.java:259)
    at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:836)
    at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:44)
    at org.eclipse.jetty.start.FS.toRealPath(FS.java:165)
    at org.eclipse.jetty.start.StartArgs.addUniqueXmlFile(StartArgs.java:217)
    at org.eclipse.jetty.start.StartArgs.resolveExtraXmls(StartArgs.java:1123)
    at org.eclipse.jetty.start.Main.processCommandLine(Main.java:342)
    at org.eclipse.jetty.start.Main.main(Main.java:74)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Usage: java -jar start.jar [options] [properties] [configs]
       java -jar start.jar --help  # for more information
Disconnected from server

Process finished with exit code -9




Возможно, одна из причин - это пустой файл jetty-web.xml









Как мне все-таки застваить Jetty работать?
Приведите пожалуйста весь список файлов что нужно описать, а еще лучше поделитесь
живым примером (туториалом или исходником), где четко все описано что и зачем. Мне
очень срочно нужно разобраться с этим jetty.


    


Ответы

Ответ 1



Отвечаю на собственный вопрос, дабы поделиться со всеми опытом. Не во всех уроках есть информация о том, что нужно нажать галочку на Use custom context root: Без этой галочки сервер и не запускался, хотя Intellij IDEA пропускала такую конфигурацию и не требовала, чтобы эта галочка была отмечена, т.е. спокойно можно было нажать Apply и без нее. В поле рядом с этим checkbox через слэш пишите что угодно, что хотите, чтобы отображалось в адресной строке браузера после localhost:8080. И вуаля - все работает. Также проверьте настройки версии языка в IDEA и конкретно в проекте - они должны удовлетворять требованиям вашей текущей версии сервера Jetty. Надеюсь вам это тоже поможет если у вас возникла подобная ситуация.

Как мне не искать каждый раз пользователя в Spring?

#java #spring_mvc #spring_security


В общем при каждом обращении к котроллер в Spring мне приходится искать пользователя,
создавать объект и уже работать с ним. Т.е. скажем захожу в /profile, я вытаскиваю
имя авторизованного пользователя, ищу в Базе его, если всё нормально то работаю с ним.
Кликаю по /index, то опять приходиться искать пользователя в базе, создаю объект и
опять выстраиваю страницу под него. Как избежать поиск и создание нового объекта?
    


Ответы

Ответ 1



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

Ответ 2



Так же не плохим вариантом будет работа с cookies, для подтверждения личности пользователя)

среда, 25 декабря 2019 г.

javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread

#java #spring #hibernate #spring_mvc #jpa


GenericDao.java:

@Repository
public abstract class GenericDao implements GeneralDao {

    private Class className;

    public GenericDao(Class className) {
        this.className = className;
    }

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    @Override
    public void add(T object) {
        try {
            getEntityManager().persist(object);
        } catch (HibernateException e) {
            throw new DaoException(ErrorMessage.ADD_ENTITY_FAIL, e);
        }
    }

    @Override
    public void update(T object) {
        try {
            getEntityManager().merge(object);
        } catch (HibernateException e) {
            throw new DaoException(ErrorMessage.UPDATE_ENTITY_FAIL, e);
        }
    }

    @Override
    public void remove(T object) {
        try {
            getEntityManager().remove(object);
        } catch (HibernateException e) {
            throw new DaoException(ErrorMessage.REMOVE_ENTITY_FAIL, e);
        }
    }

    @Override
    public T getById(int id) {
        try {
            return getEntityManager().find(this.className, id);
        } catch (HibernateException e) {
            throw new DaoException(ErrorMessage.GET_BY_ID_ENTITY_FAIL, e);
        }
    }

    public abstract List getAll() throws DaoException;

}


GenericService.java

@Service
public abstract class GenericService implements GeneralService {
    private static Logger logger = Logger.getLogger(GenericService.class);

    @Autowired
    private GenericDao dao;

    @Transactional
    @Override
    public void add(T object) throws ServiceException {
        try {
           dao.add(object);
        } catch (DaoException e) {
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        }
    }

    @Transactional
    @Override
    public void update(T object) throws ServiceException {
        try {
            dao.update(object);
        } catch (DaoException e) {
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        }
    }

    @Transactional
    @Override
    public void remove(T object) throws ServiceException {
        try {
            dao.remove(object);
        } catch (DaoException e) {
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        }
    }

    @Transactional(readOnly = true)
    @Override
    public T getById(int id) throws ServiceException {
        try {
            return dao.getById(id);
        } catch (DaoException e) {
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        }
    }

    @Transactional(readOnly = true)
    @Override
    public List getAll() throws ServiceException {
        try {
            return dao.getAll();
        } catch (DaoException e) {
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        }
    }

}


UserServiceImpl.java:

@Service
public class UserServiceImpl extends GenericService implements UserService {
    private static Logger logger = Logger.getLogger(UserServiceImpl.class);

    @Autowired
    private UserDao userDao;

    @Transactional
    @Override
    public String checkUser(String userLogin, String userPassword) throws ServiceException {
        String namePage = "errorAuthorization";
        List userList;
        try {
           userList = userDao.getByLoginAndPassword(userLogin, userPassword);
        }  catch (DaoException e) {
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        }
        if(userList.size() != 0) {
            return UserRoleChecker.defineUserPage(userList.get(0));
        }
        return namePage;
    }

    @Transactional
    @Override
    public void addUser(String userLogin, String userPassword, String userMail) throws
ServiceException {
        Role role = new Role(0L, RoleType.USER);
        User user = new User(0L, userLogin, userPassword, userMail, role);
        add(user);
    }

}


UserController.java:

@Controller
public class UserController {
    private static String className = UserController.class.getName();
    private static Logger logger = Logger.getLogger(UserController.class.getName());

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/check_user", method = RequestMethod.POST)
    public ModelAndView authorizationUser(HttpServletRequest request, HttpServletResponse
response) {
        ModelAndView modelAndView = new ModelAndView();
        String returnPage;
        try {
            returnPage = userService.checkUser(request.getParameter(RequestParameter.USER_LOGIN),
request.getParameter(RequestParameter.USER_PASSWORD));
        } catch (ServiceException e) {
            logger.debug(e);
            returnPage = ErrorHandler.returnErrorPage(e.getMessage(), className);
        }
        modelAndView.setViewName(returnPage);
        return modelAndView;
    }

    @RequestMapping(value = "/add_user", method = RequestMethod.POST)
    public ModelAndView registrationUser(HttpServletRequest request, HttpServletResponse
response) {
        ModelAndView modelAndView = new ModelAndView();
        String returnPage = Page.SUCCESSFUL_REGISTRATION;
        try {
            userService.addUser(request.getParameter(RequestParameter.USER_LOGIN),
request.getParameter(RequestParameter.USER_PASSWORD), request.getParameter(RequestParameter.USER_MAIL));
        }  catch (ServiceException e) {
            logger.debug(e);
           returnPage = ErrorHandler.returnErrorPage(e.getMessage(), className);
        }
        modelAndView.setViewName(returnPage);
        return modelAndView;
    }

}


root-context.xml:




    

    
    
    

    
        
        
        
        
        
        
    

    
        
        
        
            
                
                
                
            
        
        
            
                org.hibernate.dialect.MySQLDialect
                true
                true
                2
                true
                update
            
        
        
    

    

    

    

    
        
        
        
    




logs:

org.springframework.web.servlet.FrameworkServlet 2017-05-10 22:23:59,107 DEBUG -
Could not complete request
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction
available for current thread - cannot reliably process 'persist' call
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:282)
    at com.sun.proxy.$Proxy27.persist(Unknown Source)
    at by.netcracker.artemyev.dao.GenericDao.add(GenericDao.java:35)
    at by.netcracker.artemyev.service.GenericService.add(GenericService.java:24)
    at by.netcracker.artemyev.service.impl.UserServiceImpl.addUser(UserServiceImpl.java:48)
    at by.netcracker.artemyev.web.UserController.registrationUser(UserController.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:475)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:495)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:767)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1354)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)


Почему возникает эта ошибка и как её исправить?
    


Ответы

Ответ 1



Проблема в том, что spring не строит прокси-объект поверх UserServiceImpl, а внедряет его напрямую в UserController, что видно из твоего исключения. Причины, я полагаю, в том, что твой root-context.xml относится к контексту всего приложения, т.е. инициализируется через ContextLoaderListener. Внутри указанного контекста UserServiceImpl имеет транзакционность. Однако, у тебя также имеется некая конфигурация (тут лучше бы приложить web.xml) относящаяся к DispatcherServlet, в которой инициализируется UserController и как раз нет tx:annotation-driven. Соответственно в UserController попадет чистый бин, а не его прокси. Быстро решить данную проблему можно убрав @Service с самого класса UserServiceImpl и определив его в root-context.xml

Ответ 2



У меня была подобная проблема. Исправил так - в контексте для сервлета в аннотации component-scan указал не корневое пространство имен, а конкретно то, которое хранит Rest-контроллер src/main/webapp/spring/appServlet-context.xml После данных изменений рутовый контекс рулил бинами сервисов и проблем не было.

суббота, 21 декабря 2019 г.

Hibernate many to many бесконечно вложенный json

#java #hibernate #spring_mvc


Есть три таблицы user, group, user2group. Связь много ко многим.

Если прописать мапинг только в классе User, то контроллер выдает json который и нужен.

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user2group", 
    joinColumns = { @JoinColumn(name = "user_id") }, 
    inverseJoinColumns = { @JoinColumn(name = "group_id") })
private Set groups = new HashSet();


Но если мапим связанные данные в классе Group, то при попытке получить список групп
с привязанными к ним клиентами, контроллер начинает выдавать json, но при этом бесконечно
проваливается в связанные сущности.

Мапинг для класса Group сейчас у меня следующий:

@ManyToMany(mappedBy = "groups")
private Set users = new HashSet();


Записи я получаю следующим образом 

Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("FROM User");
List users = query.list();


Также, на случай если это важно. По началу при обращении к соответствующему урлу,
вылетало исключение, которое починилось установкой следующего нагугленного свойства
при инициализации бина LocalSessionFactory:

properties.put("hibernate.enable_lazy_load_no_trans",  true);


Вопрос. Чтобы получать json единой вложенности, нужны какие-то особенные настройки
хибернейта? Или прописывать особый маппинг?

В ходе гугления вопроса мне начало казаться, что особо ни у кого не возникает подобной
сложности, может тут подход нужен иной?

Задача
Возможность получить всех юзеров, где к каждому юзеру будет прикреплен список групп,
в которых он числится.
Пример: 

[
  {id:1, name:"user1", groups:[{id:1, title:"group1"}, {id:5, title:"group5"}]},
  {id:2, name:"user2", groups:[]},
  {id:3, name:"user3", groups:[{id:3, title:"group3"}]},
]


Возможность получить все группы, где к каждой группе будет прикреплен список юзеров,
которые числятся в этой группе
Пример: 

[
  {id:1, title:"group1", users:[{id:1, name:"user1"}, {id:5, name:"user5"}]},
  {id:2, title:"group2", users:[]},
  {id:3, title:"group3", users:[{id:3, name:"user3"}]},
]

    


Ответы

Ответ 1



Не очень хорошая практика - отдавать ваши ORM-сущности непосредственно наружу. Во-первых, вы смешиваете разные задачи в одном классе. Во-вторых, вы отдаете наружу все его состояние, которое, возможно, не стоит отдавать. В третьих, когда-нибудь вам потребуется отдавать для разных задач разный набор полей. Обычно, эта проблема решается паттерном DTO (Data Transfer Object). Вы просто создаете классы UserDto и GroupDto, которые содержат только необходимые поля, геттеры-сеттеры, конструктор по-умолчанию и JSON-аннотации. При отдаче наружу в слое представления (контроллере) перекладываете поля из ORM-объектов в DTO-объекты. Можно это делать как вручную (завести static-метод в DTO), так и использовать какой-нибудь маппер вроде Dozer. Для получения групп с вложенными в нее пользователями, нужно завести отдельную DTO, например, как GroupWithUsersDto в примере ниже. public class GroupDto { @JsonProperty("id") private Long id; @JsonProperty("title") private String title; // сеттеры-геттеры public static GroupDto fromModel(Group group) { GroupDto dto = new GroupDto(); dto.setId(group.getId()); dto.setTitle(group.getTitle()); return dto; } public class UserDto { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; // сеттеры-геттеры public static UserWithGroupsDto fromModel(User user) { UserWithGroupsDto dto = new UserWithGroupsDto(); dto.setId(user.getId()); dto.setName(user.getName()); return dto; } } Для выдачи пользователей с их группами: public class UserWithGroupsDto { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; @JsonProperty("groups") private List groups; // сеттеры-геттеры public static UserWithGroupsDto fromModel(User user) { UserWithGroupsDto dto = new UserWithGroupsDto(); dto.setId(user.getId()); dto.setName(user.getName()); List groupDtos = new ArrayList<>(); for (Group group : user.getGroups()) { groupDtos.add(GroupDto.fromModel(group)); } dto.setGroups(groupDtos); return dto; } } Для выдачи групп с пользователями: public class GroupWithUsersDto{ @JsonProperty("id") private Long id; @JsonProperty("title") private String title; @JsonProperty("users") private List // сеттеры-геттеры public static GroupWithUsersDto fromModel(Group group) { GroupWithUsersDto dto = new GroupWithUsersDto(); dto.setId(group.getId()); dto.setTitle(group.getTitle()); List usersDtos = new ArrayList<>(); for (User user : group.getUsers()) { usersDtos.add(UserDto.fromModel(user)); } dto.setUsers(usersDtos); return dto; }

Ответ 2



Можно повесить в классе Group на поле @ManyToMany(mappedBy = "groups") private Set users = new HashSet(); аннотацию @JsonIgnore Или использовать слой DTO package my.company.model; import javax.persistence.*; import java.util.Date; import java.util.List; import java.util.Set; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; private String surname; private String password; private String email; @Column(name="time_registration") private Date timeRegistration; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_to_roles", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "role_id")) private Set roles; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List albums; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_to_dialogs", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "dialog_id")) private List dialogs; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List messages; @OneToOne private Avatar avatar; public User() { } public User(String name, String surname, String password, String email, Date timeRegistration, Set roles) { this.name = name; this.surname = surname; this.password = password; this.email = email; this.timeRegistration = timeRegistration; this.roles = roles; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Set getRoles() { return roles; } public void setRoles(Set roles) { this.roles = roles; } public List getAlbums() { return albums; } public void setAlbums(List albums) { this.albums = albums; } public Date getTimeRegistration() { return timeRegistration; } public void setTimeRegistration(Date timeRegistration) { this.timeRegistration = timeRegistration; } public List getDialogs() { return dialogs; } public void setDialogs(List dialogs) { this.dialogs = dialogs; } public List getMessages() { return messages; } public void setMessages(List messages) { this.messages = messages; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Avatar getAvatar() { return avatar; } public void setAvatar(Avatar avatar) { this.avatar = avatar; } @Override public String toString() { return id + " " + name + " " + email; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return id == user.id; } @Override public int hashCode() { return (int) (id ^ (id >>> 32)); } } package my.company.DTO; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; import java.util.List; @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY) @JsonInclude(JsonInclude.Include.NON_NULL) public class UserDTO { private Long id; private String name; private String surname; private String email; @JsonProperty("albums") private List albumDTOs; private Date timeRegistration; private Long avatarId; public UserDTO(Long id, String name, String surname, String email, Date timeRegistration, Long avatarId) { this.id = id; this.name = name; this.surname = surname; this.email = email; this.timeRegistration = timeRegistration; this.avatarId = avatarId; } public UserDTO(Long id, String name, String surname, String email, List albumDTOs, Date timeRegistration) { this.id = id; this.name = name; this.surname = surname; this.email = email; this.albumDTOs = albumDTOs; this.timeRegistration = timeRegistration; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List getAlbumDTOs() { return albumDTOs; } public void setAlbumDTOs(List albumDTOs) { this.albumDTOs = albumDTOs; } public Date getTimeRegistration() { return timeRegistration; } public void setTimeRegistration(Date timeRegistration) { this.timeRegistration = timeRegistration; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Long getAvatarId() { return avatarId; } public void setAvatarId(Long avatarId) { this.avatarId = avatarId; } }

среда, 11 декабря 2019 г.

Проблемы с кодировкой в Spring 3 MVC

#java #spring #spring_mvc


Здравствуйте. Пытаюсь сделать вот что:
@RequestMapping(value = "/something")
@ResponseBody
public String helloWorld() {
    return "Русский текст";
}

Страница принимает Примерно это:
??????? ?????

На странице кодировка UTF-8. Но даже если бы она и другой была бы, то были бы в любом
случае не вопросительные знаки. Посоветуйте, пожалуйста, как исправить ситуацию.
Дополню вопрос: Очень странно получилось в такой ситуации:
@RequestMapping(value = "/something")
public ResponseEntity helloWorld() {
    MyCls cls = new MyCls();
    cls.setStr("Русский текст");

    HttpHeaders h = new HttpHeaders();
    return new ResponseEntity(cls, h, HttpStatus.OK);
}

Сейчас сработало так, как я и ожидал:
{"str":"Русский текст"}

У спринга что, на тип String аллергия?    


Ответы

Ответ 1



Похоже, это известная проблема (аналогичный вопрос на SO, баг в джире), решается она несколькими способами: так, как указал @Nofate - использовать ResponseEntity с соответствующими заголовками; добавить конвертер в hw-servlet.xml (код ниже); забабахать свой конвертер с блекдж... (как тут). Сконфигурированный конвертер: В ответе на SO говорится, что второй метод не сработает с mvc:annotation-driven - не знаю, у меня заработало и с ним. UPD. Ага, с mvc:annotation-driven не заработает, если поставите конвертер после этого объявления.

Ответ 2



В web.xml добавить сам фильтр: characterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true characterEncodingFilter /* В параметрах подключения к базе, добавить следующий код: jdbc:mysql://localhost:3306/dataBaseName?characterEncoding=UTF-8

Ответ 3



Проверьте на всякий случай кодировку самого файла с исходником. Знаки вопроса - верный признак попытки интерпретировать cp1251-строку в качестве UTF-строки. -- UPD1. Все, что пока получилось, это: public ResponseEntity preview(HttpServletResponse response) { HttpHeaders h = new HttpHeaders(); h.add("Content-type", "text/html;charset=UTF-8"); return new ResponseEntity("Привет мир",h ,HttpStatus.OK); }

Ответ 4



Добавлю от себя еще. Если вы используете обработку ошибок @ExceptionHandler и вам нужен русский текст, например в ошибках sql от бд на фронте и передаете их например в формате json, то также нужно добавить в ..-servlet.xml: text/plain;charset=UTF-8 text/html;charset=UTF-8 application/json;charset=UTF-8 application/x-www-form-urlencoded;charset=UTF-8 text/plain;charset=UTF-8 text/html;charset=UTF-8 application/json;charset=UTF-8 application/x-www-form-urlencoded;charset=UTF-8 Ну и maven зависимости: com.fasterxml.jackson.core jackson-databind 2.8.3 Подтверждаю, работает с , объявленном после бинов. Может кому пригодится. Мой пост по этой теме

Ответ 5



Если используется Tomcat, то в server.conf (по-моему так файл называется) надо поправить коннектор, по которому вы работаете - добавить URIEncoding="UTF-8".

Ответ 6



В дескрипторе приложения необходимо добавить фильтр: encodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true

суббота, 7 декабря 2019 г.

Архитектура web torrent клиента

#java #spring_mvc #архитектура #torrent


Подскажите как правильно организовать локальную торрент качалку с веб интервейсом.
Сам торрент клиент уже написан. Осталось прикрутить веб. Стоит ли ради "такого" дела
поднимать спринг или же есть альтернативные пути? Если стоит прикрутить спринг, насколько
подходит архитектура mvc?

Торрент клиент на вход получает файл.torrent(или же путь к файлу) и путь для сохранения.
С возможностью выдачи статистических данных. Никакой безопасности и многопользовательности.
    


Ответы

Ответ 1



А в чем сомнения возникли? Вам же по сути надо сделать CRUD для управления торрентами. Любой MVC тут подойдет. Но можно и без него. Архитектурно у вас уже есть разделение на UI и саму качалку. Если у вас нет конкретных требований к аппаратным ресурсам делать можно на чем нравится. Варианты: если хочется клиентскую часть сделать максимально тонкой - Spring Boot, Spring MVC, любой шаблонизатор (JSP, Thymeleaf, Velocity). если хочется попробовать SPA, то какой-нибудь не слишком тяжелый сервер (Jetty, Undertow, Ratpack) и JAX-RS обвязка для REST (Jersey, RESTeasy), или даже вовсе без нее, а UI ваять на JS. если хочется SPA без JavaScript, то можно попробовать GWT (Google Web Toolkit), но это путь в никуда. если все же железо слабое, и хочется чего-то с минимальным footprint, то лучше вообще отказаться от Java, а попробовать Python или даже Lua. Под них тоже достаточно как полноценный MVC, так минималистичных микро-web-фреймворков.

четверг, 11 июля 2019 г.

Редирект в Spring MVC

В последнее время мне никто не отвечает, но я всё равно почему-то спрашиваю...
Привет, товарищи. По прежнему мусолю официальный пример на оф. сайте Spring, переписываю их код с помощью аннотаций. Там есть формочка, чтобы увеличить цену, вот такая:

По нажатии кнопки Execute цены на товар увеличиваются на заданный процент, и после этого пользователя перебрасывает на страницу со списком товаров. Вот их код, который это обслуживает:
public ModelAndView onSubmit(Object command) throws ServletException {
int increase = ((PriceIncrease) command).getPercentage(); logger.info("Increasing prices by " + increase + "%.");
productManager.increasePrice(increase);
logger.info("returning from PriceIncreaseForm view to " + getSuccessView());
return new ModelAndView(new RedirectView(getSuccessView())); }
Опустим работу с логированием, базой данных, остановимся только на возвращаемом методом значении. Моя задача - сделать так, чтобы после увеличения цен на странице с товарами появилось уведомление, мол, "цены успешно повышены", а при обновлении страницы пропало. Я пробовал так:
@RequestMapping(method = RequestMethod.POST) public ModelAndView post(Model model) { model.addAttribute("hello", "world"); return new ModelAndView(new RedirectView("/home.htm")); }
так:
@RequestMapping(method = RequestMethod.POST) public ModelAndView post(Model model) { return new ModelAndView("redirect:/home.htm").addObject("hello", "world"); }
так:
@RequestMapping(method = RequestMethod.POST) public ModelAndView post(Model model) { return new ModelAndView("redirect:/home.htm", "hello", "world"); }
так:
@RequestMapping(method = RequestMethod.POST) public View post(Model model) { model.addAttribute("hello","world"); return new RedirectView("/home.htm"); }
А ещё тут предлагают вызывать метод, отвечающий за страницу со списком товаров. Все мои попытки, представленные выше, передают мой hello=world в url, получается
http://localhost/home.htm?hello=world
Соответственно если я сделаю пометку таким образом, она никуда не денется после обновления страницы. Как мне быть? Может, есть какой-то способ передать в модель той страницы дополнительные атрибуты? На PHP я это делал с помощью $_SESSION, может, и здесь сессии использовать?
P. S. Спасибо тем, кто доскроллил вопрос до конца, надеюсь получить ответ, палец вверх и принятый ответ гарантирую.


Ответ

может и здесь сессии использовать?
Ну а как еще? Это же по факту состояние, которое надо сбрасывать после первого показа. Ну куки еще разве что. Вордпресс, конечно, живет себе гет-параметрами, но он вордпресс. Вообще эта штука называется flash messages, единственная проблема будет со списками, когда надо по одному ключу забить кучу сообщений https://stackoverflow.com/questions/11881720/multiple-flash-messages-in-spring-mvc
В последнее время мне никто не отвечает, но я всё равно почему-то спрашиваю... палец вверх и принятый ответ гарантирую.
-_-

суббота, 6 июля 2019 г.

Зачем нужна решетка в этом запросе и чем первый action отличается от второго?

Не силен в thymeleaf. Не могу понять смысл этой строки:


Ответ

Для правильности написания нужно указывать action атрибут. Значение решетка означает текущий фрагмент URL, который указывает на текущую страницу.
Второй th:action вычисляет выражение указанное в значении и создает/меняет значение action аттрибута формы.
Если по каким либо причинам выражение не вернет URL для акшен атрибута, то форма не будет отправляться, так как есть action аттрибут, который указывает на текущую страницу. При этом значение фрагмента # предохраняет страницу от рефреша. В данном случае абсолютно бесполезный, но возможно есть обработчик на JS.

Spring boot не отдается страница

Начинаю разбираться со спрингом. Не работает самый банальный пример, со статьи на хабре.
IndexController.java
@Controller public class IndexController { @GetMapping("/") public ModelAndView index() { Map model = new HashMap<>(); model.put("name", "Alexey"); return new ModelAndView("index", model); } }
resourses/templates/index.html

Welcome to Spring, {{ name }}


Выдает
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback. Tue Aug 14 15:30:33 MSK 2018 There was an unexpected error (type=Not Found, status=404). No message available
Если использовать @RestController, то всё работает. Есть подозрения на SELinux


Ответ

Надо добавить вью резолвер MustacheViewResolver для того, чтобы можно было использовать шаблоны с расширением .html
@Configuration public class Config implements WebMvcConfigurer {
@Bean public ViewResolver viewResolver() { MustacheViewResolver mustacheViewResolver = new MustacheViewResolver(); mustacheViewResolver.setPrefix("classpath:/templates/"); mustacheViewResolver.setSuffix(".html"); mustacheViewResolver.setCache(false); return mustacheViewResolver; } }

пятница, 21 июня 2019 г.

Как запустить свое веб-приложение на Jetty в IntelliJ Idea

Здравствуйте! Я разрабатываю Spring MVC веб-приложение и мне нужно чтобы оно работало на Jetty-сервере, не требующем внешнего контейнера. Я добавил сервер jetty стандартным образом через "edit run configuration". Он также успешно отображается в Project Structure.

Но, когда я запускаю Jetty то он просто не запускается и выдает мне следущее:
"C:\Program Files\Java\jdk1.8.0_65\bin\java" -DSTOP.PORT=0 -Dcom.sun.management.jmxremote= -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -DOPTIONS=jmx -Didea.launcher.port=7535 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 15.0.4\bin" -Dfile.encoding=windows-1251 -classpath "D:\jetty-distribution-9.3.7.v20160115\start.jar;C:\Program Files\Java\jdk1.8.0_65\lib\tools.jar;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 15.0.4\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain org.eclipse.jetty.start.Main --module=jmx C:\Windows\Temp\context4config\jetty-contexts.xml [2016-03-03 07:17:55,511] Artifact DVDExchange:war exploded: Server is not connected. Deploy is not available. Detected server http port: 8080 java.nio.file.AccessDeniedException: C:\Windows\Temp\context4config\jetty-contexts.xml at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:90) at sun.nio.fs.WindowsLinkSupport.getRealPath(WindowsLinkSupport.java:259) at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:836) at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:44) at org.eclipse.jetty.start.FS.toRealPath(FS.java:165) at org.eclipse.jetty.start.StartArgs.addUniqueXmlFile(StartArgs.java:217) at org.eclipse.jetty.start.StartArgs.resolveExtraXmls(StartArgs.java:1123) at org.eclipse.jetty.start.Main.processCommandLine(Main.java:342) at org.eclipse.jetty.start.Main.main(Main.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Usage: java -jar start.jar [options] [properties] [configs] java -jar start.jar --help # for more information Disconnected from server
Process finished with exit code -9

Возможно, одна из причин - это пустой файл jetty-web.xml



Как мне все-таки застваить Jetty работать? Приведите пожалуйста весь список файлов что нужно описать, а еще лучше поделитесь живым примером (туториалом или исходником), где четко все описано что и зачем. Мне очень срочно нужно разобраться с этим jetty.


Ответ

Отвечаю на собственный вопрос, дабы поделиться со всеми опытом. Не во всех уроках есть информация о том, что нужно нажать галочку на Use custom context root

Без этой галочки сервер и не запускался, хотя Intellij IDEA пропускала такую конфигурацию и не требовала, чтобы эта галочка была отмечена, т.е. спокойно можно было нажать Apply и без нее. В поле рядом с этим checkbox через слэш пишите что угодно, что хотите, чтобы отображалось в адресной строке браузера после localhost:8080. И вуаля - все работает.
Также проверьте настройки версии языка в IDEA и конкретно в проекте - они должны удовлетворять требованиям вашей текущей версии сервера Jetty.
Надеюсь вам это тоже поможет если у вас возникла подобная ситуация.

Spring перехват ошибок (404)

Всем привет. Изучаю Java Web и Spring MVC. Столкнулся с такой проблемой. Нужно сделать свою страницу ошибок. Я не до конца понял как эти ошибки отлавливать. Понятно как отлавливать ошибки в контроллере типа ("page/${id}"). Мы ищем инфу по данному id и если такой нет, выбрасываем исключение или тупо редиректим куда нибудь. Тут всё понятно. Но я хочу, что бы это всё делалось не в каждом контроллере, а где то в одном месте и не важно по какому роуту была эта ошибка. Что то в роде такого. Прописать, что для всех исключений (404), мы резолвим такую jsp и передаём в модель то-то и то-то. И вообще пофиг какой роут вызвал эту ошибку. В общем как то так. Заранее благодарен.


Ответ

Spring Web построен на сервлетах. Чтобы наиболее полно ответить на ваш вопрос, давайте посмотрим под DispatcherServlet, который в Spring отвечает за обработку всех запросов.
Вот код, который вызывается, если Spring не находит метод контроллера, который должен обрабатывать запрос:
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { if (pageNotFoundLogger.isWarnEnabled()) { pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) + "] in DispatcherServlet with name '" + getServletName() + "'"); } if (this.throwExceptionIfNoHandlerFound) { throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), new ServletServerHttpRequest(request).getHeaders()); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND); } }
Из него видно, что:
Пишется сообщение в лог Если установлен флаг throwExceptionIfNoHandlerFound, то бросается исключение NoHandlerFoundException Если флаг throwExceptionIfNoHandlerFound не установлен, то вызывается метод объекта HttpServletResponse sendError()
По умолчанию, флаг сброшен throwExceptionIfNoHandlerFound = false, т.е. вызывается метод sendError(), и как результат контейнер сервлетов переадресует вас на страницу ошибки. Чтобы переопределить эту страницу на собственную, согласно спецификации JEE. вам нужно задать параметр error-page в дескрипторе развертывания web.xml
404 /404
Соответственно, нужно создать и метод контроллера, который будет замапен на URL /404 и вернет нужную страницу.

Может случиться так, что вам потребуется выкинуть пользователя на страницу 404 вручную. Глобально это можно сделать через аннотацию @ControllerAdvice, действия перечисленные в классе помеченным данной аннотацией являются дефолтными для всех контроллеров.
Создаем собственный класс исключения:
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() { super(); } }
Ловим это исключение глобально для всех контроллеров, и перекидываем на /404
@ControllerAdvice public class ExceptionInterceptor {
@ExceptionHandler(ResourceNotFoundException.class) public ModelAndView handleError404(HttpServletRequest request, Exception e) { ModelAndView mv = new ModelAndView("/404"); return mv; } }
Соотвественно чтобы выкинуть пользователя на 404 из любого контроллера, просто бросаете это исключение. Передать дополнительный параметры можно просто добавив нужных полей в класс исключения: собственный код ошибки, сообщение и т.д.

Теперь когда у нас есть глобальный @ExceptionHandler можно вспомнить про флаг throwExceptionIfNoHandlerFound и сделать так, что 404 будет обрабатываться только в нем.
Меняем значение флага:
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override public void customizeRegistration(ServletRegistration.Dynamic registration) { registration.setInitParameter("throwExceptionIfNoHandlerFound", "true"); } }
И дописываем в @ExceptionHandler перехват соответствующего класса исключений:
@ExceptionHandler(value = {ResourceNotFoundException.class, NoHandlerFoundException.class}) public ModelAndView handleError404(HttpServletRequest request, Exception e) { ModelAndView mv = new ModelAndView("/404"); return mv; }
При этом параметры error-page обычно все равно стараются оставлять "на всякий пожарный случай" как fallback, чтобы пользователь вдруг не увидел трекстрейс, который Tomcat показывает в своих сообщениях по дефолту.

Полезные ссылки на официальные примеры:
error-page Spring error handling

четверг, 30 мая 2019 г.

Как передать свой объект из jsp в контроллер

У меня есть User, в нем есть ссылка на Role, и например на странице editUser мне надо собрать объект User в контроллер spring MVC(в параметры контроллера добавляю User user), такие объекты как name(String)email(String), age(int) собираются в User нормально, а чтобы добавить туда Role мне надо редактируемого User'a достать из БД извлечь его роль и засетить в новый объект User и только потом делать update в hibernate.
Так вот вопрос можно ли как то spring'ом сразу User собрать вместе с Role из jsp страницы?
Пробовал указать поле в верстке
input type="hidden" name="role" value"${user.role}"
но тогда вообще контроллер не находит и редиректит на страницу 400.
P.S User добавляю из get-контроллера в jsp, тоесть переменные User можно достать в jsp


Ответ

Как по мне, на уровне клиента это невозможно.
Как вариант, тебе нужно сперва запросом отправить id нужного объекта, а потом используя id получить его.
Я бы сделал бы это вот так
html
... ... ... ...
Внутри метода*
.........@ModelAttribute("user") User user, @RequestParam("id") Integer id) { .... Role role = (Role) session.createQuery("from Role role where role.id=:id").setParameter("id", id).list().get(0); user.setRole(role); .... }