View Javadoc

1   package pl.psnc.dl.ege;
2   
3   import java.io.File;
4   import java.io.FileInputStream;
5   import java.io.FileOutputStream;
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.io.OutputStream;
9   import java.net.URI;
10  import java.net.URL;
11  import java.util.ArrayList;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.UUID;
15  import java.util.regex.Pattern;
16  import java.util.zip.ZipException;
17  
18  import javax.xml.transform.stream.StreamSource;
19  
20  import net.sf.saxon.s9api.DocumentBuilder;
21  import net.sf.saxon.s9api.Processor;
22  import net.sf.saxon.s9api.SaxonApiException;
23  import net.sf.saxon.s9api.Serializer;
24  import net.sf.saxon.s9api.XsltCompiler;
25  import net.sf.saxon.s9api.XsltExecutable;
26  import net.sf.saxon.s9api.XsltTransformer;
27  
28  import org.xml.sax.SAXNotRecognizedException;
29  import org.xml.sax.SAXNotSupportedException;
30  
31  import pl.psnc.dl.ege.utils.EGEIOUtils;
32  import pl.psnc.dl.ege.MultiXslOutputResolver;
33  import pl.psnc.dl.ege.component.ConfigurableConverter;
34  import pl.psnc.dl.ege.configuration.EGEConfigurationManager;
35  import pl.psnc.dl.ege.configuration.EGEConstants;
36  import pl.psnc.dl.ege.exception.ConverterException;
37  import pl.psnc.dl.ege.exception.EGEException;
38  import pl.psnc.dl.ege.types.ConversionActionArguments;
39  import pl.psnc.dl.ege.types.DataType;
40  import pl.psnc.dl.ege.utils.IOResolver;
41  
42  /**
43   * <p>
44   * Converter based on xsl transformations.
45   * </p>
46   * Each extension instance provides one transformation possibility.<br>
47   * <b>Important : </b> the converter expects only compressed data. Data is
48   * compressed with standard EGE IOResolver received from
49   * EGEConfigurationManager.
50   * 
51   * @author mariuszs
52   */
53  public class MultiXslConverter implements ConfigurableConverter {
54  
55  	private static final String EAD = "EAD";
56  	
57  	private static final String MASTER = "MASTER";
58  	
59  	/*
60  	 * URI of resource - xsl transformation scheme.
61  	 */
62  	private URI xslUri;
63  
64  	private URI defaultUri = null;
65  
66  	private final IOResolver ior = EGEConfigurationManager.getInstance()
67  			.getStandardIOResolver();
68  
69  	/*
70  	 * List of possible conversions.
71  	 */
72  	private List<ConversionActionArguments> possibleConversions = new ArrayList<ConversionActionArguments>();
73  
74  	public void convert(InputStream inputStream, OutputStream outputStream,
75  			ConversionActionArguments conversionDataTypes)
76  			throws ConverterException, IOException {
77  		if (!isSupported(conversionDataTypes)) {
78  			throw new ConverterException(
79  					ConverterException.UNSUPPORTED_CONVERSION_TYPES);
80  		}
81  		File inTempDir = prepareTempDir();
82  		File outTempDir = null;
83  		try {
84  			if (conversionDataTypes.getInputType().getFormat().equals(EAD)) {
85  				outTempDir = performEADTransformation(prepareInputData(
86  						inputStream, inTempDir));
87  			} else {
88  				outTempDir = performStandardTransformation(prepareInputData(
89  						inputStream, inTempDir));
90  			}
91  			ior.compressData(outTempDir, outputStream);
92  		} catch (ZipException ex) {
93  			throw new ConverterException(
94  					"Error during EAD conversion : probably wrong input data.");
95  		} catch (SaxonApiException ex) {
96  			ex.printStackTrace();
97  			throw new ConverterException(ex.getMessage());
98  		} catch (Exception ex) {
99  			ex.printStackTrace();
100 			throw new ConverterException(ex.getMessage());
101 		} finally {
102 			if (inTempDir != null && inTempDir.exists())
103 				EGEIOUtils.deleteDirectory(inTempDir);
104 			if (outTempDir != null && outTempDir.exists())
105 				EGEIOUtils.deleteDirectory(outTempDir);
106 		}
107 	}
108 
109 	/*
110 	 * Creates temporary directory with UUID random name.
111 	 */
112 	private File prepareTempDir() {
113 		File inTempDir = null;
114 		String uid = UUID.randomUUID().toString();
115 		inTempDir = new File(EGEConstants.TEMP_PATH + File.separator + uid
116 				+ File.separator);
117 		inTempDir.mkdir();
118 		return inTempDir;
119 	}
120 
121 	/*
122 	 * prepares received data - decompress and open file stream.
123 	 */
124 	private InputStream prepareInputData(InputStream inputStream, File inTempDir)
125 			throws IOException, ConverterException {
126 		ior.decompressStream(inputStream, inTempDir);
127 		// perform transform
128 		File sFile = searchForData(inTempDir, "^.*\\.((?i)xml)$");
129 		if (sFile == null) {
130 			// search for any file
131 			sFile = searchForData(inTempDir, "^.*");
132 			if (sFile == null) {
133 				throw new ConverterException(
134 						"No file data was found for conversion!");
135 			}
136 		}
137 		FileInputStream fis = new FileInputStream(sFile);
138 		return fis;
139 	}
140 
141 	private File searchForData(File dir, String regex) {
142 		for (File f : dir.listFiles()) {
143 			if (!f.isDirectory() && Pattern.matches(regex, f.getName())) {
144 				return f;
145 			} else if (f.isDirectory()) {
146 				File sf = searchForData(f, regex);
147 				if (sf != null) {
148 					return sf;
149 				}
150 			}
151 		}
152 		return null;
153 	}
154 
155 	/*
156 	 * Performs EAD conversions - result contains many files packed to .zip
157 	 * archive
158 	 */
159 	private File performEADTransformation(InputStream inputStream)
160 			throws SaxonApiException, IOException, ConverterException {
161 		InputStream is = null;
162 		try {
163 			Processor proc = new Processor(false);
164 			XsltCompiler comp = proc.newXsltCompiler();
165 			URL xslURL = xslUri.toURL();
166 			try{
167 				is = xslURL.openStream();
168 			}catch(IOException ex){
169 				if(defaultUri != null){
170 					xslURL = defaultUri.toURL();
171 					is = xslURL.openStream();
172 				}else{
173 					throw ex;
174 				}
175 			}
176 			// create temporary files directory
177 			String uid = UUID.randomUUID().toString();
178 			File tempDir = new File(System.getProperty("java.io.tmpdir")
179 					+ File.separator + uid + File.separator);
180 			tempDir.mkdir();
181 
182 			// setup xslt processor
183 			proc.getUnderlyingConfiguration().setOutputURIResolver(
184 					new MultiXslOutputResolver(uid));
185 			XsltExecutable exec = comp.compile(new StreamSource(is));
186 			XsltTransformer transformer = exec.load();
187 			transformer.setInitialContextNode(proc.newDocumentBuilder().build(
188 					new StreamSource(inputStream)));
189 			Serializer result = new Serializer();
190 
191 			// create dummy result file - result file is empty
192 			File dummyResult = new File(tempDir.getPath() + ".xml");
193 			FileOutputStream dummyOs = new FileOutputStream(dummyResult);
194 
195 			result.setOutputStream(dummyOs);
196 			transformer.setDestination(result);
197 			transformer.transform();
198 			dummyOs.close();
199 			dummyResult.delete();
200 
201 			return tempDir;
202 
203 		} finally {
204 			if (is != null) {
205 				try {
206 					is.close();
207 				} catch (Exception ex) {
208 					// do nothing
209 				}
210 			}
211 			try {
212 				inputStream.close();
213 			} catch (Exception ex) {
214 				// do nothing
215 			}
216 		}
217 	}
218 
219 	/*
220 	 * Performs standard xslt conversion.
221 	 */
222 	private File performStandardTransformation(InputStream inputStream)
223 			throws IOException, SaxonApiException, SAXNotRecognizedException,
224 			SAXNotSupportedException {
225 		InputStream is = null;
226 		File tempDir = null;
227 		FileOutputStream fos = null;
228 		try {
229 			String uid = UUID.randomUUID().toString();
230 			tempDir = new File(EGEConstants.TEMP_PATH + File.separator + uid
231 					+ File.separator);
232 			tempDir.mkdir();
233 			fos = new FileOutputStream(new File(tempDir + File.separator
234 					+ "stand.xml"));
235 			Processor proc = new Processor(false);
236 			proc.getUnderlyingConfiguration().setValidation(false);
237 			proc.getUnderlyingConfiguration().getSourceParser()
238 					.setErrorHandler(null);
239 			XsltCompiler comp = proc.newXsltCompiler();
240 			URL xslURL = xslUri.toURL();
241 			try{
242 				is = xslURL.openStream();
243 			}catch(IOException ex){
244 				if(defaultUri != null){
245 					xslURL = defaultUri.toURL();
246 					is = xslURL.openStream();
247 				}else{
248 					throw ex;
249 				}
250 			}
251 			XsltExecutable exec = comp.compile(new StreamSource(is));
252 			XsltTransformer transformer = exec.load();
253 			DocumentBuilder documentBuilder = proc.newDocumentBuilder();
254 			documentBuilder.setDTDValidation(false);
255 			transformer.setInitialContextNode(documentBuilder
256 					.build(new StreamSource(inputStream)));
257 			Serializer result = new Serializer();
258 			result.setOutputStream(fos);
259 			transformer.setDestination(result);
260 			transformer.transform();
261 			return tempDir;
262 		} finally {
263 			if (fos != null) {
264 				fos.close();
265 			}
266 			if (is != null) {
267 				is.close();
268 			}
269 			inputStream.close();
270 		}
271 	}
272 
273 	public List<ConversionActionArguments> getPossibleConversions() {
274 		return possibleConversions;
275 	}
276 
277 	private boolean isSupported(ConversionActionArguments cadtCheck) {
278 		for (ConversionActionArguments cadt : possibleConversions) {
279 			if (cadt.equals(cadtCheck)) {
280 				return true;
281 			}
282 		}
283 		return false;
284 	}
285 
286 	public void configure(Map<String, String> params) throws EGEException {
287 		try {
288 			xslUri = new URI(params.get("xsluri"));
289 			String iFormat = params.get("iFormat");
290 			StringBuffer sb = new StringBuffer();
291 			sb.append("jar:file:");
292 			sb.append(getClass().getProtectionDomain().getCodeSource()
293 					.getLocation().getFile());
294 			// default xsl
295 			if (EAD.equals(iFormat)) {
296 				sb.append("!/ead2enrich/ead2enrich.xsl");
297 				defaultUri = new URI(sb.toString());
298 			} else if (MASTER.equals(iFormat)) {
299 				sb.append("!/master2enrich/master2enrich.xsl");
300 				defaultUri = new URI(sb.toString());
301 			}
302 			String iMIME = params.get("iMimeType");
303 			String oFormat = params.get("oFormat");
304 			String oMIME = params.get("oMimeType");
305 			ConversionActionArguments cadt = new ConversionActionArguments(
306 					new DataType(iFormat, iMIME), new DataType(oFormat, oMIME));
307 			possibleConversions.add(cadt);
308 		} catch (NullPointerException ex) {
309 			throw new EGEException(EGEException.WRONG_CONFIGURATION
310 					+ " 'null' value of parameter.");
311 		} catch (Exception ex) {
312 			throw new EGEException(EGEException.WRONG_CONFIGURATION + " "
313 					+ ex.getMessage());
314 		}
315 	}
316 
317 }