Source code: com/flexstor/flexdbserver/services/asset/ImageInfoService.java
1 /*
2 * ImageInfoService.java
3 *
4 * Copyright $Date: 2003/08/11 02:22:28 $ FLEXSTOR.net Inc.
5 *
6 * This work is licensed for use and distribution under license terms found at
7 * http://www.flexstor.org/license.html
8 *
9 */
10
11 package com.flexstor.flexdbserver.services.asset;
12
13 import java.util.Enumeration;
14 import java.util.Vector;
15
16 import com.flexstor.common.data.ActionData;
17 import com.flexstor.common.data.ActionResult;
18 import com.flexstor.common.data.ejb.disguiserecord.AssetRoleData;
19 import com.flexstor.common.data.ejb.disguiserecord.DisguiseAssetRecordData;
20 import com.flexstor.common.data.ejb.disguiserecord.DisguiseRecordData;
21 import com.flexstor.common.data.ejb.disguiserecord.ImageRoleData;
22 import com.flexstor.common.errorlogger.FlexError;
23 import com.flexstor.common.importprocessor.ImportCtlData;
24 import com.flexstor.common.importprocessor.ImportData;
25 import com.flexstor.common.importprocessor.ImportResult;
26 import com.flexstor.common.resources.Resources;
27 import com.flexstor.common.services.ServiceArgumentsI;
28 import com.flexstor.common.settings.Settings;
29 import com.flexstor.common.util.Diagnostic;
30 import com.flexstor.common.util.FlexDbServerHost;
31 import com.flexstor.common.util.ServerList;
32 import com.flexstor.flexdbserver.importprocessor.ConversionOptions;
33 import com.flexstor.flexdbserver.services.Service;
34 import com.flexstor.flexdbserver.services.ServiceContext;
35 import com.flexstor.flexdbserver.services.io.ImportConvert;
36
37 /**
38 * <P>
39 * AlchemyInfoService <BR>
40 * <BLOCKQUOTE>
41 * Calls the Alchemy utility to obtain display information about an asset.
42 *
43 * Different image convert applications can be plugged into this service. <BR>
44 * The service requires the following key/value pair lines as output of the command line
45 * application being invoked (this is an example): <BR>
46 * <BLOCKQUOTE>
47 * Type: JPEG <BR>
48 * Mode: Truecolour <BR>
49 * Width: 640 <BR>
50 * Height: 430 <BR>
51 * BitsPerPixel: 24 <BR>
52 * DPIX: 72 <BR>
53 * DPIY: 72 <BR>
54 * FileSize: 203189 <BR>
55 * </BLOCKQUOTE>
56 * The result must be sent to standard output. <BR>
57 * The keys do not required to be with the capitalization shown. <BR>
58 * Make sure a colon is used, not an equal sign. <BR>
59 * Note that DPIX and DPIY can be substituted with DPCX and DPCY if this values are given in
60 * centimeters rather than inches. <BR>
61 *
62 * </BLOCKQUOTE>
63 * </P>
64 *
65 * Configurable Properties in roletype_services.config <BR>
66 *
67 * <TABLE BORDER="1" CELLPADDING="3" CELLSPACING="3">
68 * <CAPTION ALIGN=TOP>
69 * <B> In/Out Properties for Assets </B>
70 * </CAPTION>
71 * <TR>
72 * <FONT SIZE=+1><B>
73 * <TH WIDTH="120">Attribute</TH>
74 * <TH WIDTH="30">IN</TH> <TH WIDTH="30">OUT</TH> <TH WIDTH="30">Default IN</TH> <TH WIDTH="30">Default OUT</TH>
75 * </B></FONT>
76 * </TR>
77 * <TR>
78 * <TH ALIGN=LEFT><FONT SIZE=+1><B>ROLE</B></FONT></TH>
79 * <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER> ALL </TD> <TD ALIGN=CENTER>   </TD>
80 * </TR>
81 * <TR><TH> Highres </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
82 * <TR><TH> Lowres </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
83 * <TR><TH> Thumbnail </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
84 * <TR><TH> Layout </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
85 * <TR><TH> Video </TH> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
86 * <TR><TH> Audio </TH> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
87 * <TR>
88 * <TH ALIGN=LEFT><FONT SIZE=+1><B>TYPE</B></FONT></TH>
89 * <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER> ALL </TD> <TD ALIGN=CENTER>   </TD></TR>
90 * </TR>
91 * <TR>
92 * <TH ALIGN=LEFT><FONT SIZE=+1><B>FLAG</B></FONT></TH>
93 * </TR>
94 * <TR><TH> PARENT </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD></TR>
95 * <TR><TH> CHLDREN </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
96 * <TR><TH> ALL </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
97 * <TR><TH> TEMP_PARENT </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
98 * <TR><TH> TEMP_CHILDREN </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
99 * <TR><TH> TEMP_ALL </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
100 * </TABLE>
101 *
102 * <P>
103 * configfile: Path to configuration file holding alchemy related parameters (optional; defaults
104 * to the conversion.cfg file for the hot directory). <BR>
105 * Legal values: Full path to configuration file
106 * </P>
107 *
108 * <P>
109 * Input Data Object <BR>
110 * <BLOCKQUOTE>
111 * com.flexstor.common.importprocessor.ImportData
112 * </BLOCKQUOTE>
113 * </P>
114 *
115 * <P>
116 * Output Data Object <BR>
117 * <BLOCKQUOTE>
118 * com.flexstor.common.importprocessor.ImportResult
119 * </BLOCKQUOTE>
120 * </P>
121 * Programmable Properties (passed inside data object) <BR>
122 * <BLOCKQUOTE>
123 * Global Properties (apply to all assets) <BR>
124 * <BLOCKQUOTE>
125 * <P>
126 * "intervaltimer_timeout" :
127 * A user-specified property in roletype_services.config that defines the timeout period for
128 * the watch-dog timer that interrupts the Alchemy process if it hangs.
129 * Data type: int <BR>
130 * Legal values: time (seconds) <BR>
131 * </P>
132 * </BLOCKQUOTE>
133 * </BLOCKQUOTE>
134 */
135
136 public class ImageInfoService
137 implements Service
138 {
139 // To get the version number from MKS
140 public final static String IDENTIFIER="$Id: ImageInfoService.java,v 1.6 2003/08/11 02:22:28 aleric Exp $";
141
142 protected ServiceContext context;
143 protected String fileSeparator;
144 protected int id;
145
146 private String sThisService = "";
147
148 // Image File Attributes
149 private String sPrimaryFormat = " ";
150 private String sPrimaryDPIX = " ";
151 private String sPrimaryDPIY = " ";
152 private String sPrimaryWidth = " ";
153 private String sPrimaryHeight = " ";
154 private String sPrimaryColorDepth = " ";
155 private String sPrimaryColorSpace = " ";
156 private String sPrimaryFileSize = " ";
157
158 private ConversionOptions refConversionOptions = null;
159
160 // "1" = UNIX version, "0" = Windows version
161 private int nOemAlchemy = 1; // default to UNIX
162 private String sTranslator = "";
163 private String sConfigFile = "";
164
165 protected boolean successful = true;
166
167 protected ImportCtlData refCtlData = null;
168 protected ImportData refImportData = null;
169
170
171 /**
172 * Calls before the service is initialized (before initData is called) to
173 * pass information about the environment in which the service is running.
174 * This environment consists of information about the properties set for the
175 * service in one of these files (services.config, roletype_services.config,
176 * or *.ctl), plus methods to access other information such as an instance
177 * of the service broker to invoke other services, the transaction id for
178 * the service, file separator character and local path for the installation
179 * directory and configuration directory.
180 *
181 * @param context Holds information about the environment in which the service
182 * is running.
183 */
184 public void setServiceContext( ServiceContext context )
185 {
186 this.context = context;
187 fileSeparator = context.getFileSeparator();
188 id = context.getTransactionId();
189 }
190
191 /**
192 * A data initialization method called at the beginning of the service.
193 * The input argument, ActionData must be cast into its subclass, ImportData
194 * in order to extract the service specific data from it.
195 */
196 public void initData(ActionData actionData)
197 {
198 if ( (Settings.getString( Settings.SYSTEM_OS )).equals("UNIX") )
199 nOemAlchemy = 1; // tell Convert to use unixExecute()
200 else
201 nOemAlchemy = 0; // tell Convert to use dosExecute()
202
203
204 // cast the data object to a convert data wrapper
205 refImportData = (ImportData) actionData;
206
207
208 // Get the reference to the .CTL data object
209 refCtlData = refImportData.getCtlDataRef();
210
211
212 // Get the config file name defined in the services.config file; if one is not present
213 // use the one listed in the CTL data.
214 sConfigFile = context.getProperty( "configfile" );
215 if ( sConfigFile == null || sConfigFile.equals("") )
216 sConfigFile = refCtlData.getValuePerKey("CONTROLFILESPATH") + refCtlData.getValuePerKey("CONFIGFILE");
217
218 //6201=Alchemy Info Service
219 sThisService = Resources.get(6201) + " (" + id + ")";
220 } // initData
221
222 /**
223 * The start of the Convert Service.
224 */
225 public ActionResult go()
226 {
227 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "AlchemyInfoStarting");
228 // Need to read the .cfg conversion file to get all of the parameters
229 // that Alchemy needs to do the conversions
230 // If we could not read the config file return from the convert service.
231 // We already sent the response object back if it was necessary
232 if (!readCfgFile())
233 {
234 ImportResult response = new ImportResult(false);
235 //response.setText(CONVERT);
236 response.setImportData(refImportData);
237
238 return response;
239 }
240
241
242 // Now that we have read in the .cfg file successfully, prepare to
243 // read in the input files and process them with Alchemy.
244 // If successful == false at least one file didn't convert correctly.
245 successful = true;
246
247 // Get a reference to the DisguiseRecordData so we can get the assets
248 // Also get Role, Type and Flag values from property list (AssetService)
249 DisguiseRecordData refDisguiseRecordData = refImportData.getDisguiseRecordRef();
250 String sRole = context.getProperty(ServiceArgumentsI.ROLE_DATA_SOURCE);
251 String sType = context.getProperty(ServiceArgumentsI.TYPE_DATA_SOURCE);
252 String sFlag = context.getProperty(ServiceArgumentsI.FLAG_DATA_SOURCE);
253 Vector assetRecords = null;
254
255 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Role = " + sRole);
256 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Type = " + sType);
257 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Flag = " + sFlag);
258
259 if(refDisguiseRecordData != null)
260 {
261 if(sRole == null || sType == null || sFlag == null)
262 {
263 successful = false;
264 }
265 else
266 { // Get the assets
267 assetRecords = refDisguiseRecordData.getAssets(sRole, sType, sFlag);
268 if(assetRecords != null)
269 {
270 Enumeration assets = assetRecords.elements();
271
272 // Processs each asset in each element in ImportData as required
273 while(assets.hasMoreElements())
274 {
275 DisguiseAssetRecordData anAsset = (DisguiseAssetRecordData)assets.nextElement();
276
277 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "####### Calling GET INFO for " + sRole);
278 // Get Info only
279 if (getInfo(anAsset) == false)
280 {
281 // Hard error, quit now
282 successful = false;
283 break;
284 }
285 }
286 }
287 else
288 successful = false;
289 }
290 }
291 else
292 successful = false;
293
294 // pass back the ImportData
295 ImportResult response = new ImportResult(successful); // turn off default result
296 //response.setText(CONVERT); // let the caller know what service for email
297 response.setImportData(refImportData);
298
299 return response;
300 } // go()
301
302
303 /**
304 * Get Info
305 */
306 protected boolean getInfo(DisguiseAssetRecordData refSourceAsset)
307 {
308 if(refSourceAsset == null)
309 return false;
310
311 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "ImageConvert: Get Info for: " + refSourceAsset.getFileName());
312
313 String server = ServerList.getDNSName( refSourceAsset.getServer() );
314
315 // Get the paths
316 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Creating the sInputPath... The location and filename are: " +
317 refSourceAsset.getLocation() + ", " + refSourceAsset.getFileName());
318 String sInputPath = refSourceAsset.getLocation() + refSourceAsset.getFileName();
319 if ( sInputPath.startsWith(fileSeparator) == false )
320 sInputPath = fileSeparator + sInputPath;
321
322 // Get The Role Data to get the AssetType
323 AssetRoleData roleData = (AssetRoleData) refSourceAsset.getAssetRole();
324 // If the servername does not match the host name, it means that the file is on another
325 // filesystem and we shouldn't convert it.
326 if ( !FlexDbServerHost.isLocalHost( server ) )
327 {
328 successful = false;
329 //6202=Unable to get info for file: %%1. Server specified in asset record (%%2) does not match FlexDBServer host (%%3)
330 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 6202, (new String[] { sInputPath, server, FlexDbServerHost.getLocalHostName() })) );
331 return false;
332 }
333 else
334 {
335 String sAssetType = null;
336 if(roleData != null)
337 {
338 sAssetType = roleData.getAssetFileType();
339 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "sAssetType is: " + sAssetType);
340 }
341 if (sInputPath != null)
342 {
343 // Set up the conversion options data structure
344 // Setup and process the element
345 if (processElement(sInputPath) == false)
346 {
347 //5519=Unable to convert input file: %%1
348 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5519, (new String[] { sInputPath} )) );
349 }
350 } // if (sInputPath != null)
351 else
352 {
353 successful = false; // at least one file did not convert right
354 //5505=Null input file.
355 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5505) );
356 }
357 } // ServerPath else
358
359 // Set the file size if it isn't already set
360 if ( refSourceAsset.getFileSize() < 0 )
361 refSourceAsset.setFileSize( stringToLong(sPrimaryFileSize) );
362
363 // Create the thumbnail asset and role if necessary
364 if ( roleData instanceof ImageRoleData )
365 {
366 ImageRoleData imageRoleData = (ImageRoleData) roleData;
367 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Setting values for " + roleData.getAssetFileType());
368 imageRoleData.setXRes( stringToFloat(sPrimaryDPIX) );
369 imageRoleData.setYRes( stringToFloat(sPrimaryDPIY) );
370 imageRoleData.setWidth( stringToFloat(sPrimaryWidth) );
371 imageRoleData.setHeight( stringToFloat(sPrimaryHeight) );
372 imageRoleData.setColorDepth( stringToFloat(sPrimaryColorDepth) );
373 imageRoleData.setColorSpace( sPrimaryColorSpace );
374
375 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "----> " + imageRoleData.getXRes() + ", " + imageRoleData.getYRes() + ", " + imageRoleData.getWidth()
376 + ", " + imageRoleData.getHeight() + ", " + imageRoleData.getColorDepth() + ", " + imageRoleData.getColorSpace());
377 }
378 return true;
379 } // getInfo
380
381 private boolean readCfgFile()
382 {
383 // Need to read the .cfg conversion file to get all of the parameters
384 // that Alchemy needs to do the conversions
385 refConversionOptions = new ConversionOptions(sConfigFile);
386 if ( refConversionOptions.processSection(false) )
387 return true; // we read the file OK so return true
388 else
389 {
390 //5521=Unable to read configuration input file: %%1
391 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5521, (new String[] { sConfigFile })) );
392 return false;
393 }
394 } // readCfgFile()
395
396
397 /**
398 *
399 *
400 */
401 public boolean processElement(String sSourcePath)
402 {
403 // Setup translator path first, so if not found can quit now
404 sTranslator = refConversionOptions.getConversionItem("TRANSLATOR", "PATH");
405 if (sTranslator.equals("") == true)
406 return false;
407
408 String sOptions = "";
409 // Get user-provided conversion options
410 String sUserOptions = refConversionOptions.getConversionItem("info", "OPTION");
411
412 if ( sUserOptions != null )
413 sOptions = sUserOptions;
414
415 // Info only
416 String[] saProcess = ImportConvert.createCommand( sTranslator, sSourcePath, null, sOptions );
417 if( getImageInfo(saProcess) == false )
418 return false;
419 return true;
420 } // processElement
421
422 /**
423 *
424 */
425 protected boolean getImageInfo(String[] saCommandLine)
426 {
427 boolean bResult = true;
428
429 // Get the user specified value for the watchdog timer interval
430 // Timeout value is specified in seconds
431 String sTimeOut = context.getProperty("intervaltimer_timeout");
432 // If the intervaltimer_timeout property wasn't set, check for the conversion_timeout
433 // property; this is left here for backward compatibility with release 3.0.6.0
434 if ( sTimeOut == null )
435 sTimeOut = context.getProperty("conversion_timeout");
436
437 ImportConvert refConvert = new ImportConvert();
438
439 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Image Info: " + refConvert.toString(saCommandLine));
440 bResult = refConvert.executeConversion(saCommandLine, nOemAlchemy, sTimeOut);
441 if (bResult == true)
442 {
443 sPrimaryFormat = refConvert.getFormat();
444 sPrimaryDPIX = refConvert.getDPIX();
445 sPrimaryDPIY = refConvert.getDPIY();
446 sPrimaryWidth = refConvert.getWidth();
447 sPrimaryHeight = refConvert.getHeight();
448 sPrimaryColorDepth = refConvert.getColorDepth();
449 sPrimaryColorSpace = refConvert.getColorMode();
450 sPrimaryFileSize = refConvert.getFileSize();
451 }
452 return bResult;
453 } // getImageInfo
454
455 private long stringToLong(String theString)
456 {
457 long returnVal = 0;
458 try{ returnVal = Long.valueOf(theString).longValue(); } catch(NumberFormatException e){};
459 return returnVal;
460 }
461
462 private float stringToFloat(String theString)
463 {
464 float returnVal = 0;
465 try{ returnVal = Float.valueOf(theString).floatValue();} catch(NumberFormatException e){};
466 return returnVal;
467 }
468
469 public String getPrimaryFormat() { return sPrimaryFormat; }
470 public String getPrimaryWidth() { return sPrimaryWidth; }
471 public String getPrimaryHeight() { return sPrimaryHeight; }
472 public String getPrimaryDPIX() { return sPrimaryDPIX; }
473 public String getPrimaryDPIY() { return sPrimaryDPIY; }
474 public String getPrimaryColorDepth() { return sPrimaryColorDepth; }
475 public String getPrimaryColorSpace() { return sPrimaryColorSpace; }
476 public String getPrimaryFileSize() { return sPrimaryFileSize; }
477 } // ImageInfoService