1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.common.xml;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.IOException;
23 import java.text.MessageFormat;
24 import java.util.Locale;
25
26 import javax.xml.transform.OutputKeys;
27 import javax.xml.transform.Transformer;
28 import javax.xml.transform.TransformerException;
29 import javax.xml.transform.TransformerFactory;
30 import javax.xml.transform.stream.StreamResult;
31
32 import org.apache.logging.log4j.LogManager;
33 import org.apache.logging.log4j.Logger;
34 import org.mycore.common.MCRException;
35 import org.mycore.common.MCRSessionMgr;
36 import org.mycore.common.MCRTransactionHelper;
37 import org.mycore.common.config.MCRConfiguration2;
38 import org.mycore.common.content.MCRContent;
39 import org.mycore.common.content.transformer.MCRContentTransformer;
40 import org.mycore.common.content.transformer.MCRParameterizedTransformer;
41 import org.mycore.common.xsl.MCRParameterCollector;
42 import org.xml.sax.SAXException;
43
44 import jakarta.servlet.ServletOutputStream;
45 import jakarta.servlet.http.HttpServletRequest;
46 import jakarta.servlet.http.HttpServletResponse;
47
48
49
50
51
52
53
54
55 public class MCRLayoutService {
56
57 private static final int INITIAL_BUFFER_SIZE = 32 * 1024;
58
59 static final Logger LOGGER = LogManager.getLogger(MCRLayoutService.class);
60
61 private static final MCRLayoutService SINGLETON = new MCRLayoutService();
62
63 private static final String TRANSFORMER_FACTORY_PROPERTY = "MCR.Layout.Transformer.Factory";
64
65 public static MCRLayoutService instance() {
66 return SINGLETON;
67 }
68
69 public void sendXML(HttpServletRequest req, HttpServletResponse res, MCRContent xml) throws IOException {
70 res.setContentType("text/xml; charset=UTF-8");
71 try {
72 Transformer transformer = TransformerFactory.newInstance().newTransformer();
73 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
74 transformer.setOutputProperty(OutputKeys.INDENT, "no");
75 StreamResult result = new StreamResult(res.getOutputStream());
76 transformer.transform(xml.getSource(), result);
77 } catch (TransformerException e) {
78 throw new MCRException(e);
79 }
80 res.flushBuffer();
81 }
82
83 public void doLayout(HttpServletRequest req, HttpServletResponse res, MCRContent source) throws IOException,
84 TransformerException, SAXException {
85 if (res.isCommitted()) {
86 LOGGER.warn("Response already committed: {}:{}", res.getStatus(), res.getContentType());
87 return;
88 }
89 String docType = source.getDocType();
90 try {
91 MCRParameterCollector parameter = new MCRParameterCollector(req);
92 MCRContentTransformer transformer = getContentTransformer(docType, parameter);
93 String filename = getFileName(req, parameter);
94 transform(res, transformer, source, parameter, filename);
95 } catch (IOException | TransformerException | SAXException ex) {
96 throw ex;
97 } catch (MCRException ex) {
98
99
100 if (!"mcr_error".equals(docType)) {
101 throw ex;
102 }
103
104 String msg = "Error while generating error page!";
105 LOGGER.warn(msg, ex);
106 res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
107 } catch (Exception e) {
108 throw new MCRException(e);
109 }
110 }
111
112 public MCRContent getTransformedContent(HttpServletRequest req, HttpServletResponse res, MCRContent source)
113 throws IOException, TransformerException, SAXException {
114 String docType = source.getDocType();
115 try {
116 MCRParameterCollector parameter = new MCRParameterCollector(req);
117 MCRContentTransformer transformer = getContentTransformer(docType, parameter);
118 String filename = getFileName(req, parameter);
119 return transform(transformer, source, parameter, filename);
120 } catch (IOException | TransformerException | SAXException ex) {
121 throw ex;
122 } catch (MCRException ex) {
123
124
125 if (!"mcr_error".equals(docType)) {
126 throw ex;
127 }
128
129 String msg = "Error while generating error page!";
130 LOGGER.warn(msg, ex);
131 res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
132 return null;
133 } catch (Exception e) {
134 throw new MCRException(e);
135 }
136 }
137
138 public static MCRContentTransformer getContentTransformer(String docType, MCRParameterCollector parameter)
139 throws Exception {
140 String transformerId = parameter.getParameter("Transformer", null);
141 if (transformerId == null) {
142 String style = parameter.getParameter("Style", "default");
143 transformerId = new MessageFormat("{0}-{1}", Locale.ROOT).format(new Object[] { docType, style });
144 }
145 MCRLayoutTransformerFactory factory = MCRConfiguration2.<MCRLayoutTransformerFactory>getInstanceOf(
146 TRANSFORMER_FACTORY_PROPERTY)
147 .orElseGet(MCRLayoutTransformerFactory::new);
148 return factory.getTransformer(transformerId);
149 }
150
151 private String getFileName(HttpServletRequest req, MCRParameterCollector parameter) {
152 String filename = parameter.getParameter("FileName", null);
153 if (filename != null) {
154 if (req.getServletPath().contains(filename)) {
155
156 return extractFileName(req.getServletPath());
157 }
158 return filename;
159 }
160 if (req.getPathInfo() != null) {
161 return extractFileName(req.getPathInfo());
162 }
163 return new MessageFormat("{0}-{1}", Locale.ROOT).format(
164 new Object[] { extractFileName(req.getServletPath()), String.valueOf(System.currentTimeMillis())});
165 }
166
167 private String extractFileName(String filename) {
168 int filePosition = filename.lastIndexOf('/') + 1;
169 filename = filename.substring(filePosition);
170 filePosition = filename.lastIndexOf('.');
171 if (filePosition > 0) {
172 filename = filename.substring(0, filePosition);
173 }
174 return filename;
175 }
176
177 private void transform(HttpServletResponse response, MCRContentTransformer transformer, MCRContent source,
178 MCRParameterCollector parameter, String filename) throws IOException, TransformerException, SAXException {
179 try {
180 String fileExtension = transformer.getFileExtension();
181 if (fileExtension != null && fileExtension.length() > 0) {
182 filename += "." + fileExtension;
183 }
184 response.setHeader("Content-Disposition",
185 transformer.getContentDisposition() + ";filename=\"" + filename + "\"");
186 String ct = transformer.getMimeType();
187 String enc = transformer.getEncoding();
188 if (enc != null) {
189 response.setCharacterEncoding(enc);
190 response.setContentType(ct + "; charset=" + enc);
191 } else {
192 response.setContentType(ct);
193 }
194 LOGGER.debug("MCRLayoutService starts to output {}", response.getContentType());
195 ServletOutputStream servletOutputStream = response.getOutputStream();
196 long start = System.currentTimeMillis();
197 try {
198 ByteArrayOutputStream bout = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
199 if (transformer instanceof MCRParameterizedTransformer) {
200 MCRParameterizedTransformer paramTransformer = (MCRParameterizedTransformer) transformer;
201 paramTransformer.transform(source, bout, parameter);
202 } else {
203 transformer.transform(source, bout);
204 }
205 endCurrentTransaction();
206 response.setContentLength(bout.size());
207 bout.writeTo(servletOutputStream);
208 } finally {
209 LOGGER.debug("MCRContent transformation took {} ms.", System.currentTimeMillis() - start);
210 }
211 } catch (TransformerException | IOException | SAXException e) {
212 throw e;
213 } catch (Exception e) {
214 Throwable cause = e.getCause();
215 while (cause != null) {
216 if (cause instanceof TransformerException) {
217 throw (TransformerException) cause;
218 } else if (cause instanceof SAXException) {
219 throw (SAXException) cause;
220 } else if (cause instanceof IOException) {
221 throw (IOException) cause;
222 }
223 cause = cause.getCause();
224 }
225 throw new IOException(e);
226 }
227 }
228
229 private MCRContent transform(MCRContentTransformer transformer, MCRContent source, MCRParameterCollector parameter,
230 String filename) throws IOException, TransformerException, SAXException {
231 LOGGER.debug("MCRLayoutService starts to output {}", getMimeType(transformer));
232 long start = System.currentTimeMillis();
233 try {
234 if (transformer instanceof MCRParameterizedTransformer) {
235 MCRParameterizedTransformer paramTransformer = (MCRParameterizedTransformer) transformer;
236 return paramTransformer.transform(source, parameter);
237 } else {
238 return transformer.transform(source);
239 }
240 } finally {
241 LOGGER.debug("MCRContent transformation took {} ms.", System.currentTimeMillis() - start);
242 }
243 }
244
245 private String getMimeType(MCRContentTransformer transformer) throws IOException, TransformerException,
246 SAXException {
247 try {
248 return transformer.getMimeType();
249 } catch (IOException | TransformerException | SAXException | RuntimeException e) {
250 throw e;
251 } catch (Exception e) {
252 return "application/octet-stream";
253 }
254 }
255
256
257
258
259 private static void endCurrentTransaction() {
260 MCRSessionMgr.getCurrentSession();
261 MCRTransactionHelper.commitTransaction();
262 }
263 }