반응형

@Controller에서 요청을 받아 특정 url로 redirect시켜주는 컨트롤러가 있는데

정상적으로 요청이 전달이 되지 않는 것 같아 로그를 확인해 보니 다음과 같은 에러메세지가 떨어지고 있었다.

java.lang.IllegalArgumentException: Model has no value for key 'e_e_ttt'
at org.springframework.web.servlet.view.RedirectView.replaceUriTemplateVariables(RedirectView.java:387)
at org.springframework.web.servlet.view.RedirectView.createTargetUrl(RedirectView.java:346)
at org.springframework.web.servlet.view.RedirectView.renderMergedOutputModel(RedirectView.java:307)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1370)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1116)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:94

 

문제는 리다이렉트 하려는 url에 e_e_ttt라는 문자열을 '{', '}'가 감싸고 있었던 것이다. 

ex) www.hello.com?name=blog&param={e_e_ttt} 

 

왜 문제가 되느냐???

URL내에서 몇 몇 문자는 특별한 의미로 예약되어 있거나, 어떤 문자는 US-ASCII의 출력 가능한 문자 집합에 포함되어 있지 않고 어떤 문자는 몇몇 인터넷 게이트웨이와 프로토콜에서 혼동되는 것으로 알려져 있어, 사용하면 안되는 것들이 있다.

 

URL에서 사용하면 안되는 문자들

% : 인코딩된 문자에 사용할 이스케이프 토큰으로 선점

/  : 경로 컴포넌트에서 선점

. : 경로 컴포넌트에서 선점

.. : 경로 컴포넌트에서 선점

# : 프래그먼트의 구획 문자로 선점

? : 질의 문자열의 구획 문자로 선점

; : 파라미터의 구획 문자로 선점

: : 스킴, 사용자 이름/비밀번호, 호스트/포트의 구획 문자로 선점

$, + : 선점

@ & = : 특정 스킴에서 특별한 의미가 있기 때문에 선점

{}|\~[]` : 게이트웨이와 같은 여러 전송 에이전트에서 불안전하게 다루기 때문에 제한됨

<> " : 안전하지 않음. 반드시 인코딩되어야함

 

근데 이번에 문제가 된 경우에는 {} 문자열이 있었기 때문이다.

 

항상 안전하지 않은 문자들에 대해서는 인코딩을 하도록 하자!!!!

 

반응형
반응형

JPA Entity에서 복합키(composite key)를 사용할 떄 복합키에 해당하는 컬럼 모두에 @Id

을 달아주어야 한다.

 

그 뿐만 아니라 class에도 @IdClass(클래스명.class)를 달아주어야 한다.

이렇게만 선언해주고 실행을 하게 되면 다음과 같은 에러를 만나게 된다.

Caused by: org.hibernate.MappingException: Composite-id class must implement Serializable:

해당 클래스에 Serializable을 implements해줍니다.

Serializable을 해주어야하는 이유는 stackoverflow의 설명으로 대신하겠습니다. (저도 JPA를 막 학습하고 사용하고 있는 입장은 아니라서..특정 이슈 때마다 검색으로 적용해 나가고 있긴 한 데 한 번 제대로 된 학습이 필요할 것 같긴합니다.)

The session object needs to be serializable hence all objects referenced by it must be serializable as well. The id is used as a key to index loaded objects in the session. In case of CompositeId s the class itself is used as the id.

ref : https://stackoverflow.com/questions/9271835/why-composite-id-class-must-implement-serializable

반응형
반응형

간단하게 메모리위에 데이터를 올려놓고 사용하고 싶은 경우 보통 ehcache를 많이 사용하게 된다.

 

이때 주의해야하는 점이 있다.

 

동일한 클래스 내에서 호출되는 메서드에 @Cacheable을 적용해도 정상적으로 동작하지 않고 무시되어져 버린다. 프록시 생성방식때문...

 

다음처럼 getSpecificRangeContents메서드 내에서 getAllContentsIdAndLocation메서드를 같은 클래스내에서 호출하게되면 정상동작하지 않는 것이다.

간단히 설명하자면 캐시는 프록시기반으로 동작하는데 프록시 클래스는 모든 요청과 응답의 인터셉터에 의해 생성되어지기 때문에 같은 클래스 내부(internal) 콜에 의해서는 캐시기능이 적용되지 않는다. 관련하여 읽어보면 프록시 동작방식과 ehcache를 사용함에 도움이 될 만한 문서를 첨부한다.

Only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual cache interception at runtime even if the invoked method is marked with @Cacheable.

ref : https://stackoverflow.com/questions/16899604/spring-cache-cacheable-not-working-while-calling-from-another-method-of-the-s/40340964

 

[ ehcache관련 읽어보면 좋은 자료 ]

Transactions, Caching and AOP: understanding proxy usage in Spring

https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring

 

ehcache-spring-annotations - UsingCacheable.wiki

https://code.google.com/archive/p/ehcache-spring-annotations/wikis/UsingCacheable.wiki

 

해당문제가 스프링4.3에서는 self-autowiring으로 해결되었다고 한다.

 

잘알고 쓰자...

 

반응형
반응형

JPA 직접 @Query애노테이션을 사용해 원하는 쿼리를 직접 사용할 수 있다.

이 때 주의해야할 점은 from절에 db의 테이블명이 아닌 해당 테이블의 entity로 등록한 모델을 입력해주어야 한다.

 

문제상황

from절의 테이블명(channel)을 작성 

 

다음과 같은 에러 발생

Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: channel is not mapped [select max(id) as id from channel] at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:138) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final] at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final] at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final] at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:729) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final] at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_151] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_151] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_151] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_151]

 

문제해결

entity로 등록한 Java class로 변경

반응형
반응형

스프링부트에서 mysql 연동시 다음과 같은 에러를 본적이 있을 것이다.

java.sql.SQLException: The server time zone value 'KST' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.

 mysql-connector-java 버전 5.1.X 이후 버전부터 KST 타임존을 인식하지 못하는 이슈로

application.properties의 datasource.url부분을 수정해준다.

spring.datasource.url에 'serverTimezone=UTC'를 추가해준다.

이후 어플리케이션 시작시 에러가 깔끔하게 사라진것을 확인할 수 있을 것이다.

반응형
반응형

스프링부트에 actuactor적용하다가 다음과 같은 에러메시지가 발생했다.

해당 프로젝트에서는 스프링부트 버전 2.1.0.REALSE를 사용하고 있다.

org.apache.solr.client.solrj.SolrServerException: Server refused connection at: http://127.0.0.1:8983/solr at org.apache.solr.client.solrj.impl.HttpSolrClient.executeMethod(HttpSolrClient.java:650) at 
.
.
.
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.http.conn.HttpHostConnectException: Connect to 127.0.0.1:8983 [/127.0.0.1] failed: Connection refused (Connection refused) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:159) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:394) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)

해당 프로젝트에서는 spring-boot-starter-data-solr를 사용하고 있었고 spring-boot-starter-actuator dependency를 추가하고

application.properties에 다음과 같이 추가해주었다. actuator사용을 위해서!

management.endpoint.metrics.enabled=true
management.endpoints.web.exposure.include=*
management.endpoint.prometheus.enabled=true
management.metrics.export.prometheus.enabled=true

 

 

문제해결은 다음과 같이 했다.

management.health.solr.enabled=false 한줄을 application.properties에 추가해주어 처리해주었다. 뭔가 actuator내부적으로 solr를 사용하고 있어 포트가 충돌나지 않았나 싶다. 이에 actuator설정에서 저렇게 false로 명시해주니 정상적으로 동작하는 것을 확인하였다.

반응형
반응형

스프링부트의 기본 컨테이너는 톰캣임을 알고 있었는데 이를 제외하고

다른 컨테이너를 사용하기 위한 방법에 대해 간단히 포스팅해본다. 

 

spring-boot-starter-web을 사용하게 되면 내부에 spring-boot-starter-tomcat을 포함하게 된다.

의존성 관계를 보면 알 수 있다.

따라서 다른 컨테이너를 사용하고 싶다면 spring-boot-starter-web의존성에서 tomcat을 exclusion시켜주어야 한다.

참조 : https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-web-servers.html

위와 같이 tomcat을 exclusion시켜주고 jetty의존성을 주입하게 되면 컨테이너로 jetty를 사용할 수 있다.

 

자세한 내용은 spring docs를 참고하자!

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-web-servers.html

반응형
반응형

스프링부트 프로젝트 생성 후 어플리케이션을 실행시켰는데 에러없이 실행되자마자 종료되는 경우가 있다. 

 


Process finished with exit code 0

라고 뜨며 어플리케이션이 바로 종료되어버린다.

스프링부트는 임베디드톰캣 기반으로 실행되는데 임베디드컨테이너의 클래스패스를 못잡아줘서 그런 것 같다.

따라서 메이븐에 다음과 같이 의존성을 추가해준다.

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>

 

반응형
반응형



HashMap을 생성하고 그 안에 put을 해도 HashMap자체의 해쉬코드(hashCode)값은 안변할 줄 알았것만.....


결론은 put을 할 때마다 hadhCode값이 변하게 됩니다.


[ 백문이불여일코딩 ]


[ 결과 hashcode 값 ]

get메서드사용할때는 당연히 안바뀌고 put으로 데이터를 넣을 때 마다 해쉬코드가 변하는 것을 확인!!!!

값은 key값에 데이터를 덮어씌울때도 hashcode가 변한다! 이유는??


[ 이유는??? ]

HashMap의 put메서드가 내부적으로 entry를 새로 만들기 때문!!!!

map에서의 hashcode의 값은 entry의 해시코드의 합으로 정의되는데 내부에서 

put메서드가 호출될때마다 새로운 entry를 생성하기 때문에 값이 달라짐

public int hashCode()
Returns the hash code value for this map. The hash code of a map is defined to be the sum of the hash codes of each entry in the map's entrySet() view. This ensures that m1.equals(m2) implies that m1.hashCode()==m2.hashCode() for any two maps m1 and m2, as required by the general contract of Object.hashCode().

This implementation iterates over entrySet(), calling hashCode() on each element (entry) in the set, and adding up the results.

Specified by:
hashCode in interface Map<K,V>
Overrides:
hashCode in class Object
Returns:
the hash code value for this map
See Also:
Map.Entry.hashCode(), Object.equals(Object), Set.equals(Object)


참조 : https://docs.oracle.com/javase/7/docs/api/java/util/AbstractMap.html#hashCode()


[ HashMap put메서드를 따라가보자! ]

1. put


2. putVal


3. putTreeVal


4. newTreeNode


5. new TreeNode



6. TreeNode의 super


Wow....


상식적으로 생각했을 때 Map이 한 번 생성되면 안에 내용이 변경되더라도 해시코드(hashcode)값은 변하지 않을 줄 알았는데....


변하는 것을 보고 한 번 끝까지 따라가 보았다.


뭔가 속이 후련한???


앞으로도 궁금한점이 생긴다면 대충 알고 넘어가는 것보다 한 번 끝까지 따라가 제대로 된 이유를 알아보는것을 습관화하도록 하자!






반응형

+ Recent posts