DocFlavor flavor = DocFlavor.INPUT_STREAM.PDF;
FileInputStream fis = null;
try {
	fis = new FileInputStream("sample.pdf");
} catch (FileNotFoundException e) {
	e.printStackTrace();
}
	Doc doc = new SimpleDoc(fis, flavor, null);
	pdfService.createPdfLabel();
try {
	docPrintJob.print(doc, printRequestAttributeSet);
} catch (PrintException e) {
	e.printStackTrace();
}

생산 현장 프로젝트 중에 제품 생산시 바코드를 프린터로 출력해야하는 시나리오를 구현하기 위해 javax.print의 API를 개발했는데 아래와 같은 에러가 발생 하였다.

javax.print.PrintException: already printing
	at java.desktop/sun.print.UnixPrintJob.print(UnixPrintJob.java:317)
	at com.dhptec.waco.service.impl.PrintServiceImpl.printLabel(PrintServiceImpl.java:122)
	at com.dhptec.waco.controller.PrintController.print(PrintController.java:34)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)

정확하게는 처음 시작하고 한 번은 잘 동작하고 두 번째부터 위와 같은 에러가 발생한다. 그런데 구글링을 해도 딱히 정확한게 없어서 API부터 다시 봤다.

javax.print.DocPrintJob public abstract void print(javax.print.Doc doc,
                           javax.print.attribute.PrintRequestAttributeSet attributes)
throws javax.print.PrintException
Prints a document with the specified job attributes. This method should only be called once for a given print job. Calling it again will not result in a new job being spooled to the printer. The service implementation will define policy for service interruption and recovery. When the print method returns, printing may not yet have completed as printing may happen asynchronously, perhaps in a different thread. Application clients which want to monitor the success or failure should register a PrintJobListener.
Print service implementors should close any print data streams (ie Reader or InputStream implementations) that they obtain from the client doc. Robust clients may still wish to verify this. An exception is always generated if a DocFlavor cannot be printed.

Params:
doc – the document to be printed. It must be a flavor supported by this PrintJob.
attributes – the job attributes to be applied to this print job. If this parameter is null then the default attributes are used.
Throws:
javax.print.PrintException – the exception additionally may implement an interface that more precisely describes the cause of the exception
FlavorException. If the document has a flavor not supported by this print job.
AttributeException. If one or more of the attributes are not valid for this print job.

내용인 즉, 이 메서드는 주어진 인쇄작업에 대해 한 번만 호출해야합니다. 다시 호출해도 새 작업이 프린터로 스풀링되지 않습니다. 그렇다면 DocPrintJob을 프린트 할 때마다 새로 생성해야된다는 뜻으로 풀이되어 아래 코드로 변경했다.

@Configuration
public class PrintConfig {

    @Autowired
    private LabelPrintJobAdapter labelPrintJobAdapter;

    @Bean
    public PrintService defaultPrintService(){
        return PrintServiceLookup.lookupDefaultPrintService();
    }

    @Bean
    @RequestScope // 추가한 내용
    public DocPrintJob docPrintJob() {
        PrintService defaultPrintService = defaultPrintService();
        DocPrintJob printJob = defaultPrintService.createPrintJob();
        printJob.addPrintJobListener(labelPrintJobAdapter);
        return printJob;
    }
}

PrintConfig.java에서 DocPrintJob을 singleton으로 주입받았는데 request scope으로 바꾸니 문제가 해결되었다.

역시 구글링이 대부분의 문제들은 해결되지만 안되면 처음부터 차근차근 해법을 찾아가는 것도 중요하다.

Spring Boot Auto Configuration 예제를 보다가 maven은 많은데 나한테 익숙한 gradle의 예제는 몇 개 안되어서 gradle로 한 번 만들어 보았다.

  1. contact-spring-boot-starter 프로젝트를 clone후에 build 한다.
    • 만든 jar파일을 어떻게 dependency를 추가해야 할까 잠시 고민했는데 maven local repository를 사용하면 간단했다.
  2. gradle install로 jar파일을 local repository에 올린다.
    • maven plugin을 추가하면 gradle install로 local repository에 올라간다.
    • maven local은 내 계정 폴더에 .m2 폴더이다.
    • 같은 버전으로 install을 계속하다 보면 갱신이 안될 수도 있다. 그럴 때는 .m2폴더를 지워야 할 때도 있었다.
  3. AutoConfigurationTest 프로젝트를 clone후에 build 한다.
  4. gradle bootrun으로 실행한다.
  5. AppConfig에 MyContact Bean을 생성한 것과 안 한 것과의 차이를 확인해 본다.
    • @ConditionalOnMissingBean에 의해서 MyContact를 만들면 만들어진 bean의 내용이 나오고 MyContact bean이 없다면 properties의 내용이 나온다.

'Programming > Spring Framework' 카테고리의 다른 글

2way ssl 인증을 위한 JKS 만들기  (0) 2020.03.10
코딩 시험과 TDD  (0) 2019.02.14
Annotation-based Controller  (0) 2017.02.01

+ Recent posts