728x90

Spring boot 3.x 버전 jstl 에러

 

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Wed Nov 08 13:23:16 KST 2023
There was an unexpected error (type=Internal Server Error, status=500).
The absolute uri: [http://java.sun.com/jsp/jstl/core] cannot be resolved in either web.xml or the jar files deployed with this application
org.apache.jasper.JasperException: The absolute uri: [http://java.sun.com/jsp/jstl/core] cannot be resolved in either web.xml or the jar files deployed with this application
	at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:54)
	at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:294)
	at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:81)
	at org.apache.jasper.compiler.TagLibraryInfoImpl.generateTldResourcePath(TagLibraryInfoImpl.java:251)
	at org.apache.jasper.compiler.TagLibraryInfoImpl.<init>(TagLibraryInfoImpl.java:122)
	at org.apache.jasper.compiler.Parser.parseTaglibDirective(Parser.java:429)
	at org.apache.jasper.compiler.Parser.parseDirective(Parser.java:487)
	at org.apache.jasper.compiler.Parser.parseElements(Parser.java:1444)
	at org.apache.jasper.compiler.Parser.parse(Parser.java:138)
	at org.apache.jasper.compiler.ParserController.doParse(ParserController.java:245)
	at org.apache.jasper.compiler.ParserController.parse(ParserController.java:106)
	at org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:211)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:396)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:372)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:356)
	at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:603)
	at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:396)
	at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:380)
	at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:328)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)
    ...

 

spring boot의 내장 톰켓의 버전이 9.x에서 10.x로 변경되어서 의존성 라이브러리 변경이 필요하다.

spring boot 시작시 로그에서 확인가능

2023-11-08T13:25:59.432+09:00  INFO 404506 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-11-08T13:25:59.442+09:00  INFO 404506 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-11-08T13:25:59.443+09:00  INFO 404506 --- [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.5]

 

gradle에서 라이브러리는 변경한다.

//기존
//implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
//implementation 'org.springframework.security:spring-security-taglibs'
//변경
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'

 

728x90
728x90

net.sf.json-lib를 maven pom.xml에 등록시

Missing artifact net.sf.json-lib:json-lib:2.4 에러 발생시

 

	<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
	<dependency>
	    <groupId>net.sf.json-lib</groupId>
	    <artifactId>json-lib</artifactId>
	    <version>2.4</version>
	</dependency>

<classifier>jdk15</classifier>를 추가해줌

	<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
	<dependency>
	    <groupId>net.sf.json-lib</groupId>
	    <artifactId>json-lib</artifactId>
	    <version>2.4</version>
        <classifier>jdk15</classifier> <!-- 추가 -->
	</dependency>
728x90
728x90

spring과 mybatis로 개발시에 공통 부분의 sql log를 숨기고 싶을때가 있다.

 

해당 sql 로그를 숨기려면 org.codehaus.janino 라이브러리가 필요하다.

 

pom.xml

<!-- 추가(logback)-->
	<dependency>
		<groupId>ch.qos.logback</groupId>
		<artifactId>logback-core</artifactId>
		<version>1.3.0</version>
	</dependency>
	<dependency>
		<groupId>ch.qos.logback</groupId>
		<artifactId>logback-classic</artifactId>
		<version>1.3.0</version>
	</dependency>
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>jul-to-slf4j</artifactId>
		<version>2.0.0</version>
	</dependency>
	<!-- //logback(logback)-->

    <dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
    <version>3.0.6</version>
    </dependency>

logback.xml에 아래부분을 추가해 준다.

<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
    <evaluator>
        <expression>message.contains("NOT_SQL_LOG")</expression>
    </evaluator>
    <OnMismatch>NEUTRAL</OnMismatch>
    <OnMatch>DENY</OnMatch>
</filter>

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

	<property name="LOG_PATH" value="logs" />
	<property name="LOG_FILE_NAME" value="log" />

	<appender name="FILE"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
		<encoder>
			<!-- <charset>UTF-8</charset> <pattern>%d{[yyyy.MM.dd HH:mm:ss]} [%-35.35c{1}:%line] 
				%-5p %m%n</pattern> -->
			<pattern>
				%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
			</pattern>
		</encoder>
		<rollingPolicy
			class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}.%i.log
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>50MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			<maxHistory>7</maxHistory>
			<totalSizeCap>1GB</totalSizeCap>
		</rollingPolicy>
	</appender>

	<!-- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
		<layout class="ch.qos.logback.classic.PatternLayout"> <charset>UTF-8</charset> 
		<Pattern>%d{[yyyy.MM.dd HH:mm:ss]} [%-35.35c{1}:%line] %-5p %m%n</Pattern> 
		</layout> </appender> -->

	<appender name="STDOUT"
		class="ch.qos.logback.core.ConsoleAppender">
		<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
			<evaluator>
				<expression>message.contains("NOT_SQL_LOG")</expression>
			</evaluator>
			<OnMismatch>NEUTRAL</OnMismatch>
			<OnMatch>DENY</OnMatch>
		</filter>
		<encoder>
			<pattern>%d{HH:mm:ss.SSS}[%thread] %-5level %logger{36} - %msg%n
			</pattern>
		</encoder>
	</appender>

	<logger name="jdbc.sqltiming" level="INFO" />
	<logger name="jdbc.audit" level="WARN" />
	<logger name="jdbc.resultset" level="WARN" />
	<logger name="jdbc.resultsettable" level="INFO" />
	<logger name="jdbc.sqlonly" level="WARN" />
	<logger name="jdbc.connection" level="WARN" />

	<root level="DEBUG">
		<appender-ref ref="FILE" />
		<appender-ref ref="STDOUT" />
	</root>

	<typeAliases></typeAliases>
	
</configuration>

mybatis sql xml에 위의 --NOT_SQL_LOG를 추가하면 콘솔에 로그가 출력되지 않는다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="egovframework.basic.log.service.impl.WebLogMapper">

	<!-- 로그 출력 안됨 -->
	<insert id="test1" parameterType="Map">
		-- NOT_SQL_LOG
		INSERT INTO ${schemaNm}.log
		(
			session_id
		  , ip
		)
		VALUES
		(
			#{sessionId}
		  , #{ip}
		)
	</insert>
	
    	<!-- 로그 출력 -->
	<insert id="test2" parameterType="Map">
		INSERT INTO ${schemaNm}.log
		(
			session_id
		  , ip
		)
		VALUES
		(
			#{sessionId}
		  , #{ip}
		)
	</insert>
	
</mapper>
728x90
728x90

전자정부 프레임으로 InterceptorAdapter 구현 후 로그를 보니 한페이지를 호출하는데 여러번 호출되는 현상을 확인했다.

Interceptor구성으로는 문제없는데 왜 여러번 호출되나 debug 결과 jstl의 url import 때문인것을 확인함.

 

localhost/a.do로 호출시 a.jsp에 url import도 Interceptor로 호출됨.

 

request.getRequestURI()로 확인해보면

첫번째 requestURI => /a.do

두번째 requestURI => /WEB-INF/jsp/a.jsp

 

원인이 되었던 부분

a.jsp

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:import url="/topNavi.do" />

해당 url을 exclude-mapping으로 등록한다.

<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/**/*.do" /> <!-- interceptor 적용 -->
            <mvc:exclude-mapping path="/topNavi.do" /> <!-- interceptor 예외 -->
			<bean id="egovWebLogInterceptor" class="egovframework.basic.log.web.EgovWebLogInterceptor"/>
		</mvc:interceptor>
	</mvc:interceptors>

 

728x90
728x90

전자정부 4.1.0

톰캣 9.0

JDK 20.0.1

logback 1.2.9

 

환경으로 프로젝트를 셋팅을 하였는데 톰캣 로그가 한글이 깨지는 현상을 확인하였다.

해당문제는 톰캣 8버전에서는 문제 없으나 9.0에서는 톰캣 설정부분에 8번전에는 볼수 없는 여러 언어설정이 UTF-8로 더 추가가 되어지는게 확인되나 정확한 원인은 파악하지 못했다.

 

이클립스에서 톰캣 언어설정을 MS949 윈도우 인코딩으로 변경해서 정상으로 출력이 되는 모습을 확인 했다.

Encoding에 Other에 MS949가 있으면 선택하고 없으면 입력해주면 된다.

728x90
728x90

spring 전자 정부 파일이미지

package egovframework.com.cmm.web;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

import egovframework.com.cmm.SessionVO;
import egovframework.com.cmm.service.EgovFileMngService;
import egovframework.com.cmm.service.FileVO;

/**
 * @Class Name : EgovImageProcessController.java
 * @Description : 
 * @Modification Information
 *
 *    수정일       수정자         수정내용
 *    -------        -------     -------------------
 *    2009. 4. 2.     
 *    2011.08.31.             경량환경 템플릿 커스터마이징버전 생성
 *
 * @author 공통 서비스 개발팀 이삼섭
 * @since 2009. 4. 2.
 * @version
 * @see
 *
 */
@SuppressWarnings("serial")
@Controller
public class EgovImageProcessController extends HttpServlet
{

	@Resource(name = "EgovFileMngService")
	private EgovFileMngService fileService;

	Logger log = LoggerFactory.getLogger(EgovImageProcessController.class);

	/**
	 * 첨부된 이미지에 대한 미리보기 기능을 제공한다.
	 * 
	 * @param atchFileId
	 * @param fileSn
	 * @param sessionVO
	 * @param model
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping("/cmm/fms/getImage.do")
	public void getImageInf(SessionVO sessionVO, ModelMap model, Map<String, Object> commandMap, HttpServletResponse response) throws Exception
	{

		//@RequestParam("atchFileId") String atchFileId,
		//@RequestParam("fileSn") String fileSn,
		String atchFileId = (String) commandMap.get("atchFileId");
		String fileSn = (String) commandMap.get("fileSn");

		FileVO vo = new FileVO();

		vo.setAtchFileId(atchFileId);
		vo.setFileSn(fileSn);

		FileVO fvo = fileService.selectFileInf(vo);

		//String fileLoaction = fvo.getFileStreCours() + fvo.getStreFileNm();

		File file = new File(fvo.getFileStreCours(), fvo.getStreFileNm());
		FileInputStream fis = null;
		new FileInputStream(file);

		BufferedInputStream in = null;
		ByteArrayOutputStream bStream = null;
		try
		{
			fis = new FileInputStream(file);
			in = new BufferedInputStream(fis);
			bStream = new ByteArrayOutputStream();
			int imgByte;
			while((imgByte = in.read()) != -1)
			{
				bStream.write(imgByte);
			}

			String type = "";

			if(fvo.getFileExtsn() != null && !"".equals(fvo.getFileExtsn()))
			{
				if("jpg".equals(fvo.getFileExtsn().toLowerCase()))
				{
					type = "image/jpeg";
				}
				else
				{
					type = "image/" + fvo.getFileExtsn().toLowerCase();
				}
				type = "image/" + fvo.getFileExtsn().toLowerCase();

			}
			else
			{
				log.debug("Image fileType is null.");
			}

			response.setHeader("Content-Type", type);
			response.setContentLength(bStream.size());

			bStream.writeTo(response.getOutputStream());

			response.getOutputStream().flush();
			response.getOutputStream().close();

		}
		catch(Exception e)
		{
			log.debug("ERROR", e);//e.printStackTrace();
		}
		finally
		{
			if(bStream != null)
			{
				try
				{
					bStream.close();
				}
				catch(Exception est)
				{
					log.debug("IGNORED: " + est.getMessage());
				}
			}
			if(in != null)
			{
				try
				{
					in.close();
				}
				catch(Exception ei)
				{
					log.debug("IGNORED: " + ei.getMessage());
				}
			}
			if(fis != null)
			{
				try
				{
					fis.close();
				}
				catch(Exception efis)
				{
					log.debug("IGNORED: " + efis.getMessage());
				}
			}
		}
	}
}
<img id="img_file" alt="파일보기링크" src="<%=request.getContextPath() %>/cmm/fms/getImage.do?atchFileId=${fileInfo.fileId}">
728x90
728x90

Spring 서버 to 서버 파일 전송

 

A 서버에서 B서버로 파일 전송

[보내는 서버]

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public ResponseEntity<?> uploadImages(@RequestPart("images") final MultipartFile[] files) throws IOException {
    LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
    String response;
    HttpStatus httpStatus = HttpStatus.CREATED;

    try {
        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                map.add("images", new MultipartInputStreamFileResource(file.getInputStream(), file.getOriginalFilename()));
            }
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        String url = "http://example.com/upload";

        HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
        response = restTemplate.postForObject(url, requestEntity, String.class);

    } catch (HttpStatusCodeException e) {
        httpStatus = HttpStatus.valueOf(e.getStatusCode().value());
        response = e.getResponseBodyAsString();
    } catch (Exception e) {
        httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        response = e.getMessage();
    }

    return new ResponseEntity<>(response, httpStatus);
}

class MultipartInputStreamFileResource extends InputStreamResource {

    private final String filename;

    MultipartInputStreamFileResource(InputStream inputStream, String filename) {
        super(inputStream);
        this.filename = filename;
    }

    @Override
    public String getFilename() {
        return this.filename;
    }

    @Override
    public long contentLength() throws IOException {
        return -1; // we do not want to generally read the whole stream into memory ...
    }
}

[받는 서버]

@RequestMapping("/upload")
@ResponseBody
public String upload(List<MultipartFile> files) {
    files.forEach(file -> {
        System.out.println(file.getContentType());
        System.out.println(file.getOriginalFilename());
    });
    HashMap<String, String> resultMap = new HashMap<>();
    resultMap.put("result", "success");
    return ResponseEntity.ok(resultMap);
}

[참고]

https://velog.io/@dailylifecoding/Spring-MVC-multipartFile-sending-technique

 

[Spring MVC] MultipartFile을 서버 또는 Java 코드에서 전송하는 방법

Java 혹은 Spring MVC + Servlet 기반의 서버에서 MultipartForm 을 전송하는 방식에 대해서 알아본다.

velog.io

https://stackoverflow.com/questions/28408271/how-to-send-multipart-form-data-with-resttemplate-spring-mvc

 

How to send Multipart form data with restTemplate Spring-mvc

I am trying to upload a file with RestTemplate to Raspberry Pi with Jetty. On Pi there is a servlet running: protected void doPost(HttpServletReq...

stackoverflow.com

 

728x90
728x90

Spring RestTemplate으로 양식 데이터를 POST하는 방법

 

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("email", "first.last@example.com");

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);

ResponseEntity<String> response = restTemplate.postForEntity( url, request , String.class );

 

https://stackoverflow.com/questions/38372422/how-to-post-form-data-with-spring-resttemplate

 

How to POST form data with Spring RestTemplate?

I want to convert the following (working) curl snippet to a RestTemplate call: curl -i -X POST -d "email=first.last@example.com" https://app.example.com/hr/email How do I pass the email parameter

stackoverflow.com

 

728x90

+ Recent posts