Source code: com/flexstor/flexdbserver/services/asset/ImageConvertService.java
1 /*
2 * ImageConvertService.java
3 *
4 * Copyright $Date: 2003/08/11 02:22:29 $ 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.Vector;
14
15 import com.flexstor.common.constants.AssetRolesI;
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.AudioRoleData;
20 import com.flexstor.common.data.ejb.disguiserecord.DisguiseAssetRecordData;
21 import com.flexstor.common.data.ejb.disguiserecord.DisguiseRecordData;
22 import com.flexstor.common.data.ejb.disguiserecord.ImageRoleData;
23 import com.flexstor.common.data.ejb.disguiserecord.LayoutRoleData;
24 import com.flexstor.common.data.ejb.disguiserecord.VideoRoleData;
25 import com.flexstor.common.errorlogger.FlexError;
26 import com.flexstor.common.importprocessor.ImportCtlData;
27 import com.flexstor.common.importprocessor.ImportData;
28 import com.flexstor.common.importprocessor.ImportResult;
29 import com.flexstor.common.io.xfile.FlexXFile;
30 import com.flexstor.common.resources.Resources;
31 import com.flexstor.common.services.ServiceArgumentsI;
32 import com.flexstor.common.settings.Settings;
33 import com.flexstor.common.util.Diagnostic;
34 import com.flexstor.common.util.FlexDbServerHost;
35 import com.flexstor.common.util.ServerList;
36 import com.flexstor.flexdbserver.importprocessor.ConversionOptions;
37 import com.flexstor.flexdbserver.services.Service;
38 import com.flexstor.flexdbserver.services.ServiceContext;
39 import com.flexstor.flexdbserver.services.io.ImportConvert;
40 import com.flexstor.flexdbserver.util.PathBuilder;
41
42 /**
43 * <P>
44 * AlchemyConvertService <BR>
45 * <BLOCKQUOTE>
46 * Invokes the Alchemy utility for the files specified and creates thumbnail images for each of them.
47 * Also obtains display information about the input file. <BR>
48 *
49 * Different image convert applications can be plugged into this service. <BR>
50 * The service requires the following key/value pair lines as output of the command line
51 * application being invoked (this is an example): <BR>
52 * <BLOCKQUOTE>
53 * Type: JPEG <BR>
54 * Mode: Truecolour <BR>
55 * Width: 640 <BR>
56 * Height: 430 <BR>
57 * BitsPerPixel: 24 <BR>
58 * DPIX: 72 <BR>
59 * DPIY: 72 <BR>
60 * FileSize: 203189 <BR>
61 * </BLOCKQUOTE>
62 * The result must be sent to standard output. <BR>
63 * The keys do not required to be with the capitalization shown. <BR>
64 * Make sure a colon is used, not an equal sign. <BR>
65 * Note that DPIX and DPIY can be substituted with DPCX and DPCY if this values are given in
66 * centimeters rather than inches. <BR>
67 *
68 * </BLOCKQUOTE>
69 * </P>
70 *
71 * Configurable Properties in roletype_services.config <BR>
72 *
73 * <TABLE BORDER="1" CELLPADDING="3" CELLSPACING="3">
74 * <CAPTION ALIGN=TOP>
75 * <B> In/Out Properties for Assets </B>
76 * </CAPTION>
77 * <TR>
78 * <FONT SIZE=+1><B>
79 * <TH WIDTH="120">Attribute</TH>
80 * <TH WIDTH="30">IN</TH> <TH WIDTH="30">OUT</TH> <TH WIDTH="30">Default IN</TH> <TH WIDTH="30">Default OUT</TH>
81 * </B></FONT>
82 * </TR>
83 * <TR>
84 * <TH ALIGN=LEFT><FONT SIZE=+1><B>ROLE</B></FONT></TH>
85 * <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER> ALL </TD> <TD ALIGN=CENTER>   </TD>
86 * </TR>
87 * <TR><TH> Highres </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
88 * <TR><TH> Lowres </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
89 * <TR><TH> Thumbnail </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER> X </TD></TR>
90 * <TR><TH> Layout </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
91 * <TR><TH> Video </TH> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
92 * <TR><TH> Audio </TH> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
93 * <TR>
94 * <TH ALIGN=LEFT><FONT SIZE=+1><B>TYPE</B></FONT></TH>
95 * <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> ALL </TD> <TD ALIGN=CENTER>   </TD></TR>
96 * </TR>
97 * <TR>
98 * <TH ALIGN=LEFT><FONT SIZE=+1><B>FLAG</B></FONT></TH>
99 * </TR>
100 * <TR><TH> PARENT </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
101 * <TR><TH> CHLDREN </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER> X </TD></TR>
102 * <TR><TH> ALL </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
103 * <TR><TH> TEMP_PARENT </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
104 * <TR><TH> TEMP_CHILDREN </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
105 * <TR><TH> TEMP_ALL </TH> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER> X </TD> <TD ALIGN=CENTER>   </TD> <TD ALIGN=CENTER>   </TD></TR>
106 * </TABLE>
107 *
108 * <P>
109 * <BLOCKQUOTE>
110 * configfile: Path to configuration file holding Alchemy related parameters
111 * (optional; defaults to conversion.cfg file for the hot directory). <BR>
112 * Legal values: Full path to configuration file
113 * </P>
114 *
115 * <P>
116 * destination: Path for output thumbnail file (optional; defaults to thumbtarget
117 * property in the input control [*.ctl] file). <BR>
118 * Legal values: Full path to output directory
119 * </BLOCKQUOTE>
120 * </P>
121 *
122 * <P>
123 * Input Data Object <BR>
124 * <BLOCKQUOTE>
125 * com.flexstor.common.importprocessor.ImportData
126 * </BLOCKQUOTE>
127 * </P>
128 *
129 * <P>
130 * Output Data Object <BR>
131 * <BLOCKQUOTE>
132 * com.flexstor.common.importprocessor.ImportResult
133 * </BLOCKQUOTE>
134 * </P>
135 * Programmable Properties (passed inside data object) <BR>
136 * <BLOCKQUOTE>
137 * Global Properties (apply to all assets) <BR>
138 * <BLOCKQUOTE>
139 * <P>
140 * "intervaltimer_timeout" :
141 * A user-specified property in roletype_services.config that defines the timeout period for
142 * the watch-dog timer that interrupts the Alchemy process if it hangs.
143 * Data type: int <BR>
144 * Legal values: time (seconds) <BR>
145 * </P>
146 * </BLOCKQUOTE>
147 * </BLOCKQUOTE>
148 */
149
150 public class ImageConvertService
151 implements Service
152 {
153 // To get the version number from MKS
154 public final static String IDENTIFIER="$Id: ImageConvertService.java,v 1.5 2003/08/11 02:22:29 aleric Exp $";
155
156 public static final String CONVERT = "Image Conversion";
157 public static String sVersion = " ImageConvertService - 02-24-99(1)";
158
159 protected ServiceContext context;
160 protected String fileSeparator;
161 protected int id;
162
163 private String sThisService = "";
164
165 private String sFormat = " ";
166 private String sThumbLocation = " ";
167 private String sThumbServer = " ";
168 private String sThumbName = " ";
169 private String sThumbSize = " ";
170 private String sThumbPath = " ";
171
172 // Image File Attributes
173 private String sPrimaryFormat = " ";
174 private String sPrimaryDPIX = " ";
175 private String sPrimaryDPIY = " ";
176 private String sPrimaryWidth = " ";
177 private String sPrimaryHeight = " ";
178 private String sPrimaryColorDepth = " ";
179 private String sPrimaryColorSpace = " ";
180 private String sPrimaryFileSize = " ";
181
182 private ConversionOptions refConversionOptions = null;
183
184 // "1" = UNIX version, "0" = Windows version
185 private int nOemAlchemy = 1; // default to UNIX
186 private String sTranslator = "";
187 private String sTargetPath = "";
188 private String sConfigFile = "";
189
190 protected boolean successful = true;
191 protected boolean bUpdateTargetPath = true;
192 protected String sExtType = "";
193
194 protected ImportCtlData refCtlData = null;
195 protected ImportData refImportData = null;
196
197
198 /**
199 * Calls before the service is initialized (before initData is called) to
200 * pass information about the environment in which the service is running.
201 * This environment consists of information about the properties set for the
202 * service in one of these files (services.config, roletype_services.config,
203 * or *.ctl), plus methods to access other information such as an instance
204 * of the service broker to invoke other services, the transaction id for
205 * the service, file separator character and local path for the installation
206 * directory and configuration directory.
207 *
208 * @param context Holds information about the environment in which the service
209 * is running.
210 */
211 public void setServiceContext( ServiceContext context )
212 {
213 this.context = context;
214 fileSeparator = context.getFileSeparator();
215 id = context.getTransactionId();
216 }
217
218 /**
219 * A data initialization method called at the beginning of the service.
220 * The input argument, ActionData must be cast into its subclass, ConvertData
221 * in order to extract the ConvertService specific data from it.
222 */
223 public void initData(ActionData actionData)
224 {
225 if ( (Settings.getString( Settings.SYSTEM_OS )).equals("UNIX") )
226 nOemAlchemy = 1; // tell Convert to use unixExecute()
227 else
228 nOemAlchemy = 0; // tell Convert to use dosExecute()
229
230 // cast the data object to a convert data wrapper
231 refImportData = (ImportData) actionData;
232
233 // Get the reference to the .CTL data object
234 refCtlData = refImportData.getCtlDataRef();
235
236 // Get the config file name defined in the services.config file; if one is not present
237 // use the one listed in the CTL data.
238 sConfigFile = context.getProperty( "configfile" );
239 if ( sConfigFile == null || sConfigFile.equals("") )
240 sConfigFile = refCtlData.getValuePerKey("CONTROLFILESPATH") + refCtlData.getValuePerKey("CONFIGFILE");
241
242 //5882=Alchemy Convert Service
243 sThisService = Resources.get(5882) + " (" + id + ")";
244
245 } // initData
246
247 /**
248 * The start of the Convert Service.
249 */
250 public ActionResult go()
251 {
252 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "AlchemyConvertStarting");
253 // Need to read the .cfg conversion file to get all of the parameters
254 // that Alchemy needs to do the conversions
255 // If we could not read the config file return from the convert service.
256 // We already sent the response object back if it was necessary
257 if (!readCfgFile())
258 {
259 ImportResult response = new ImportResult(false);
260 response.setImportData(refImportData);
261
262 return response;
263 }
264
265 // Now that we have read in the .cfg file successfully, prepare to
266 // read in the input files and process them with Alchemy.
267 // If successful == false at least one file didn't convert correctly.
268 successful = true;
269
270 // Get a reference to the DisguiseRecordData so we can get the assets
271 // Also get Role, Type and Flag values from property list (AssetService)
272 DisguiseRecordData refDisguiseRecordData = refImportData.getDisguiseRecordRef();
273 String sRoleIn = context.getProperty(ServiceArgumentsI.ROLE_DATA_SOURCE);
274 String sTypeIn = context.getProperty(ServiceArgumentsI.TYPE_DATA_SOURCE);
275 String sFlagIn = context.getProperty(ServiceArgumentsI.FLAG_DATA_SOURCE);
276 String sRoleOut = context.getProperty(ServiceArgumentsI.ROLE_DATA_DESTINATION);
277 String sTypeOut = context.getProperty(ServiceArgumentsI.TYPE_DATA_DESTINATION);
278 String sFlagOut = context.getProperty(ServiceArgumentsI.FLAG_DATA_DESTINATION);
279 Vector vAssetsIn = null;
280 Vector vAssetsOut = null;
281
282 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "RoleIn = " + sRoleIn);
283 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "TypeIn = " + sTypeIn);
284 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "FlagIn = " + sFlagIn);
285 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "RoleOut = " + sRoleOut);
286 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "TypeOut = " + sTypeOut);
287 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "FlagOut = " + sFlagOut);
288
289 if(refDisguiseRecordData != null)
290 {
291 if(sRoleIn == null || sTypeIn == null || sFlagIn == null)
292 {
293 successful = false;
294 }
295 else
296 { // Get the assets
297 vAssetsIn = refDisguiseRecordData.getAssets(sRoleIn, sTypeIn, sFlagIn);
298 vAssetsOut = refDisguiseRecordData.getAssets(sRoleOut, sTypeOut, sFlagOut);
299 if(vAssetsIn != null)
300 {
301 DisguiseAssetRecordData targetAsset = null;
302
303 // Try to get the thumbnail target set in the service properties (configuration files)
304 // as the "destination" property; if not found in services.config use the value in the ctl file.
305 String sThumbtarget = context.getProperty("destination");
306 if ( sThumbtarget == null || sThumbtarget.equals("") )
307 sThumbtarget = refCtlData.getValuePerKey("thumbtarget");
308
309 if ( sThumbtarget.endsWith( fileSeparator ) )
310 sThumbtarget = sThumbtarget.substring( 0, sThumbtarget.length() - 1 );
311
312 String sPrimaryFileListBase = refCtlData.getValuePerKey("highfilelistbase");
313 if ( sPrimaryFileListBase.endsWith( fileSeparator ) )
314 sPrimaryFileListBase = sPrimaryFileListBase.substring( 0, sPrimaryFileListBase.length() - 1 );
315
316 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "ThumbTarget = " + sThumbtarget);
317 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "HighFileListBase = " + sPrimaryFileListBase);
318
319 // Processs each asset in each element in ImportData as required
320 for ( int i = 0; i < vAssetsIn.size(); i++ )
321 {
322 DisguiseAssetRecordData assetIn = (DisguiseAssetRecordData)vAssetsIn.elementAt(i);
323
324 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "AlchemyConvert: Getting paths");
325 String sLocation = assetIn.getLocation();
326 if ( sLocation.startsWith(fileSeparator) == false )
327 sLocation = fileSeparator + sLocation;
328
329 DisguiseAssetRecordData assetOut = null;
330 try
331 {
332 assetOut = (DisguiseAssetRecordData) vAssetsOut.elementAt(i);
333 // If the asset out is a PARENT, then it children will be the target, otherwise
334 // itself will be the target.
335 if ( sFlagOut.equals( "PARENT" ) )
336 {
337 targetAsset = assetOut.getChildAssetNoTemp( AssetRolesI.THUMBNAIL );
338 if ( targetAsset == null )
339 {
340 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "AlchemyConvert creating new Asset");
341 targetAsset = new DisguiseAssetRecordData();
342 targetAsset.setBucketStructId( assetIn.getBucketStructId() );
343 assetOut.addChildAsset( targetAsset );
344 }
345 else
346 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "AlchemyConvert updating Child Asset");
347
348 // Set this THUMBNAIL as the default view asset
349 targetAsset.setDefaultView(true);
350 }
351 else
352 {
353 targetAsset = assetOut;
354 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "AlchemyConvert updating Asset");
355 }
356 }
357 catch ( ArrayIndexOutOfBoundsException aioobe )
358 {
359 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Creating new assetOut" );
360 // If role and type are default, use the ones in the assetIn
361 int nRoleOutId = AssetRolesI.THUMBNAIL;
362 if ( sRoleOut.equals( ServiceArgumentsI.DEFAULT_ROLE ) == false )
363 nRoleOutId = getRoleId( sRoleOut );
364
365 if ( nRoleOutId == AssetRolesI.THUMBNAIL )
366 // Set this THUMBNAIL as the default view asset
367 targetAsset.setDefaultView(true);
368
369 if ( sTypeOut.equals( ServiceArgumentsI.DEFAULT_TYPE ) )
370 sTypeOut = assetIn.getAssetFileType();
371
372 assetOut = new DisguiseAssetRecordData();
373 assetOut.setBucketStructId(assetIn.getBucketStructId());
374 AssetRoleData role = AssetRoleData.getAssetRoleData(nRoleOutId);
375 role.setAssetFileType( sTypeOut );
376 if ( sFlagOut.equals( "TEMP" ) )
377 role.setTempRole( true );
378 assetOut.setAssetRole( role );
379 assetIn.addChildAsset( assetOut );
380 targetAsset = assetOut;
381 }
382
383 if ( targetAsset.getServer() != null && targetAsset.getLocation() != null && targetAsset.getFileName() != null )
384 {
385 sTargetPath = PathBuilder.constructPath( "", targetAsset.getLocation(), true );
386 sThumbPath = PathBuilder.constructFilePath( "", "", targetAsset.getLocation(), targetAsset.getFileName() );
387 bUpdateTargetPath = false;
388 }
389 else
390 {
391 sTargetPath = buildCompositeDir(sLocation, sThumbtarget, sPrimaryFileListBase);
392 bUpdateTargetPath = true;
393 }
394
395 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Location = " + sLocation);
396 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "TargetPath = " + sTargetPath);
397
398 // Create the Thumbnail
399 if (convertAsset(assetIn, targetAsset) == false)
400 {
401 // Hard error, quit now
402 successful = false;
403 break;
404 }
405 }
406 }
407 else
408 successful = false;
409 }
410 }
411 else
412 successful = false;
413
414 // pass back the ImportData
415 ImportResult response = new ImportResult(successful); // turn off default result
416 response.setImportData(refImportData);
417
418 return response;
419 } // go()
420
421 /**
422 * Convert & Get Info
423 */
424 protected boolean convertAsset(DisguiseAssetRecordData refSourceAsset, DisguiseAssetRecordData targetAsset)
425 {
426 if(refSourceAsset == null)
427 return false;
428
429 String server = ServerList.getDNSName( refSourceAsset.getServer() );
430 // Get the paths
431 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Creating the sInputPath... The location and filename are: " +
432 refSourceAsset.getLocation() + ", " + refSourceAsset.getFileName());
433 String sInputPath = refSourceAsset.getLocation() + refSourceAsset.getFileName();
434 if ( sInputPath.startsWith(fileSeparator) == false )
435 sInputPath = fileSeparator + sInputPath;
436
437 // Get The Role Data to get the AssetType
438 AssetRoleData roleData = (AssetRoleData) refSourceAsset.getAssetRole();
439 // If the host or inputpath is null, fail. Also if the servername does not match
440 // the FlexDBServer host name, it means that the file is on another filesystem
441 // and we shouldn't convert it.
442 if ( !FlexDbServerHost.isLocalHost( server ) )
443 {
444 successful = false; // at least one file did not convert right
445 //6805=Unable to convert file: %%1. Server specified in asset record (%%2) does not match FlexDBServer host (%%3)
446 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 6805, (new String[] { sInputPath, server, FlexDbServerHost.getLocalHostName() })) );
447 return false;
448 }
449 else
450 {
451 String sAssetType = null;
452 if(roleData != null)
453 {
454 sAssetType = roleData.getAssetFileType();
455 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "sAssetType is: " + sAssetType);
456 }
457 if (sInputPath != null)
458 {
459 // Set up the conversion options data structure and process the element.
460 // If true is returned, the file either converted successfully or it had a default
461 // thumbnail specified in the conversion config file setting associating a thumbnail
462 // for a non convertable file.
463 if ( processElement(sAssetType, sInputPath) == false)
464 {
465 //5519=Unable to convert input file: %%1
466 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5519, (new String[] { sInputPath} )) );
467 }
468 } // if if (sSourcePath != null)
469 else
470 {
471 successful = false; // at least one file did not convert right
472
473 //5505=Null input file.
474 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5505) );
475 }
476 } // ServerPath else
477
478 // Create the thumbnail asset and role if necessary
479 AssetRoleData targetRole = hasRole(targetAsset);
480 if ( targetRole == null )
481 {
482 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Alchemy Convert creating new Role");
483 targetRole = AssetRoleData.getAssetRoleData(AssetRolesI.THUMBNAIL);
484 targetAsset.setAssetRole(targetRole);
485 }
486 else
487 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Alchemy Convert updating Role");
488
489 if ( targetRole.getAssetFileType() == null )
490 targetRole.setAssetFileType( getExtensionFrom( getThumbName() ));
491
492 targetAsset.setFileSize(stringToLong(getThumbSize()));
493
494 if ( bUpdateTargetPath )
495 {
496 String sTNServer = getThumbServer();
497 if (sTNServer.equals(" ") == false)
498 targetAsset.setServer(sTNServer);
499 else
500 targetAsset.setServer(refSourceAsset.getServer());
501
502 // Thumb name includes path/filename. If default icon is used it will be found
503 // in getThumbLocation(). Otherwise, use the actual sThumbPath set in convertImage()
504 String sThumbLoc = getThumbLocation();
505
506 if ( sThumbLoc.startsWith(fileSeparator) )
507 sThumbLoc = sThumbLoc.substring(1); // remove leading slash, if any
508 if ( !sThumbLoc.endsWith(fileSeparator) )
509 sThumbLoc += fileSeparator; // add trailing slash
510
511 targetAsset.setLocation( sThumbLoc );
512 targetAsset.setFileName(getThumbName());
513 }
514
515 // Set the file size if it isn't already set
516 if ( refSourceAsset.getFileSize() < 0 )
517 refSourceAsset.setFileSize( stringToLong(sPrimaryFileSize) );
518
519 if ( roleData instanceof ImageRoleData )
520 {
521 ImageRoleData imageRoleData = (ImageRoleData) roleData;
522 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Setting values for " + roleData.getAssetFileType());
523 imageRoleData.setXRes( stringToFloat(sPrimaryDPIX) );
524 imageRoleData.setYRes( stringToFloat(sPrimaryDPIY) );
525 imageRoleData.setWidth( stringToFloat(sPrimaryWidth) );
526 imageRoleData.setHeight( stringToFloat(sPrimaryHeight) );
527 imageRoleData.setColorDepth( stringToFloat(sPrimaryColorDepth) );
528 imageRoleData.setColorSpace( sPrimaryColorSpace );
529 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "----> " + imageRoleData.getXRes() + ", " + imageRoleData.getYRes() + ", " + imageRoleData.getWidth()
530 + ", " + imageRoleData.getHeight() + ", " + imageRoleData.getColorDepth() + ", " + imageRoleData.getColorSpace());
531 }
532 return true;
533 } // convertAsset
534
535 /**
536 * Build up the directory from the element & root paths specified.
537 * @param sLocation The element path.
538 * @param sRootPath The root path.
539 */
540 protected String buildCompositeDir(String sLocation, String sRootPath, String sListBasePath)
541 {
542 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "sLocation, sRootPath and sListBase in buildCompositeDir: " + sLocation + ", " +
543 sRootPath + ", " + sListBasePath);
544 String sOutputPath = sRootPath;
545 String fileSeparator = System.getProperty("file.separator");
546 // Find the base path in the user specified input file path
547 if (sLocation.startsWith(sListBasePath))
548 sOutputPath += fileSeparator + sLocation.substring(sListBasePath.length() + 1);
549 else
550 sOutputPath += sLocation;
551
552 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "The output path from buildCompositeDir is: " + sOutputPath);
553 return sOutputPath;
554 } // buildCompositeDir
555
556 /**
557 *
558 */
559 private boolean readCfgFile()
560 {
561 // Need to read the .cfg conversion file to get all of the parameters
562 // that Alchemy needs to do the conversions
563 refConversionOptions = new ConversionOptions(sConfigFile);
564 if ( refConversionOptions.processSection(false) )
565 return true; // we read the file OK so return true
566 else
567 {
568 //5521=Unable to read configuration input file: %%1
569 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5521, (new String[] { sConfigFile })) );
570 return false;
571 }
572 } // readCfgFile()
573
574 /**
575 *
576 */
577 public boolean processElement(String sAssetType, String sSourcePath)
578 {
579 // Setup translator path first, so if not found can quit now
580 sTranslator = refConversionOptions.getConversionItem("TRANSLATOR", "PATH");
581 if (sTranslator.equals("") == true)
582 return false;
583
584 // If file is non-convertable, use its default icon for thumbnail, else convert
585 if (checkFileType(sAssetType, "NC") == false)
586 {
587 // Extract TN's
588 if (convertImage(sSourcePath, sAssetType) == false)
589 {
590 // process element returns false if no Thumbnail was created
591 return false;
592 }
593 } // checkFileType if
594
595 return true;
596 } // processElement
597
598 /**
599 * Convert the specified image to a thumbnail
600 * @param imagePath The image to convert.
601 */
602 private boolean convertImage(String imagePath, String sAssetType)
603 {
604 // Get the file type in order to get the user-supplied conversion options from the config file.
605 // Use the format type specified by the last "TYPE =". If it is null, use the type returned
606 // by Alchemy. If that is null, get the "UNKNOWN" type specified in the config file.
607 sFormat = sAssetType;
608
609 if (sFormat.equals("") == true)
610 {
611 sFormat = refConversionOptions.getConversionItem("UNKNOWN", "TYPE");
612 if (sFormat.equals("") == true)
613 {
614 // No 'unknown' file type specified by user so:
615 // Use the 'unknown' default icon
616 checkFileType("UNKNOWN", "C");
617 return false;
618 }
619 }
620
621 String sOptions = "";
622
623 // Get user-provided conversion options
624 String sUserOptions = refConversionOptions.getConversionItem(sFormat, "OPTION");
625
626 if(sUserOptions.equals("") == true)
627 {
628 // No options specified for this type so get the user-specified default
629 sUserOptions = refConversionOptions.getConversionItem("DEFAULT", "OPTION");
630 //"Using 'Default' conversion options: " + sUserOptions
631 if(sUserOptions.equals("") == true)
632 {
633 // No user-specifed default so use default output file type
634 sOptions += " -jh32 -Zm2 -o ---. --W -Za2 -Ze1 -Zo300p 300p -Yb300p -Xb300p -Z+ -+";
635 }
636 else
637 sOptions += " " + sUserOptions;
638 }
639 else
640 sOptions += " " + sUserOptions;
641
642 // Setup output path (create it if it doesn't exist)
643 if (sTargetPath.equals("") == true)
644 return false;
645
646 FlexXFile xOutputPath = new FlexXFile( sTargetPath );
647
648 if (createDir(xOutputPath) == false)
649 {
650 //5501=Failure creating output directory path: %%1
651 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5501, (new String[] { xOutputPath.getLocalPath() })) );
652 return false;
653 }
654
655 if ( bUpdateTargetPath )
656 {
657 // String sOutputPath = xOutputPath.getLocalPath() + fileSeparator;
658 String sOutputPath = xOutputPath.getLocalPath();
659 // Get the extension of the file to convert to from config file
660 String sExtType = getExtensionType(sFormat);
661
662 // Get the file name (with no path and no extension)
663 String sNameOnly = getNameNoPath(imagePath);
664 String sOriginalExt = "";
665 // If there is an extension, split the filename and its extension.
666 if ( sNameOnly.indexOf('.') != -1 )
667 {
668 sOriginalExt = sNameOnly.substring( sNameOnly.lastIndexOf('.') );
669 sNameOnly = sNameOnly.substring( 0, sNameOnly.lastIndexOf(sOriginalExt) );
670 }
671
672 // If an output extension was set, we should find out whether we should append the extension to the
673 // file name or remove the old extension first; this will be specified by the existence of the ---.
674 // option in the sOptions String.
675 // If the output extension is set, but the --o (use input file for output) is also set, we should
676 // log an error because this is ambigious.
677 // If the output extension is not set, then use the original filename as the output file
678 if ( sExtType != null && !sExtType.equals("") )
679 {
680 if ( sOptions.indexOf( "--o" ) != -1 )
681 {
682 // Log an error
683 // 7050=%%1 failed to process. outputext and --o cannot be specified together in %%2
684 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 7050, new String[] { imagePath, sConfigFile });
685 return false;
686 }
687 else if ( sOptions.indexOf( "---." ) != -1 )
688 {
689 sThumbName = sNameOnly + sOriginalExt + "." + sExtType.trim();
690
691 // Remove the ---. option from the sOption
692 sOptions = removeOption( sOptions, "---." );
693 }
694 else
695 sThumbName = sNameOnly + "." + sExtType.trim();
696 }
697 else
698 {
699 // If an extension is not defined; remove the --o and ---. options from sOptions; there is no
700 // point on specifying them at all
701 sOptions = removeOption( sOptions, "--o" );
702 sOptions = removeOption( sOptions, "---." );
703
704 sThumbName = sNameOnly + sOriginalExt;
705 }
706
707 // Setup thumb path and name
708 sThumbLocation = sOutputPath;
709 sThumbPath = sOutputPath + sThumbName;
710 }
711 else
712 {
713 // If the full path to the thumbnail is specified, then remove the "--o" and "---." options from
714 // the sOptions; they are meaningless when the output file is specified
715 sOptions = removeOption( sOptions, "--o" );
716 sOptions = removeOption( sOptions, "---." );
717 }
718
719 // Setup the translator program command line
720 String[] saProcess = ImportConvert.createCommand( sTranslator, imagePath, sThumbPath, sOptions );
721
722 // Call the external translation program
723 // "Executing Conversion program (convert): " + sProcess
724 executeConversion(saProcess);
725
726 // Get the thumbnail file size
727 FlexXFile fThumb = new FlexXFile(sThumbPath);
728 if (!fThumb.exists())
729 {
730 // Output default icon for unknown file type
731 if (checkFileType(sAssetType, "C") == false)
732 checkFileType("UNKNOWN", "C");
733 //"Thumbnail file does not exist : " + fThumb.toString()
734 //5514=Converted file not created: %%1
735 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5514, (new String[] { sThumbPath })) );
736 // False because no thumbnail was created successfully
737 return false; // added for ConvertResult for Send Elements
738 } // if (!fThumb.exists())
739 else
740 sThumbSize = String.valueOf(fThumb.length());
741
742 return true;
743 } // convertImage
744
745 private String removeOption( String sOptions, String sOptionToRemove )
746 {
747 if ( sOptions.indexOf( sOptionToRemove ) != -1 )
748 {
749 int nEndIndex = sOptions.indexOf(sOptionToRemove);
750 int nStartIndex = nEndIndex + sOptionToRemove.length() + 1;
751 String sOptions1 = sOptions.substring( 0, nEndIndex );
752 String sOptions2 = sOptions.substring( nStartIndex );
753 sOptions = sOptions1 + sOptions2;
754 }
755 return sOptions;
756 }
757
758 /**
759 *
760 */
761 protected String getExtensionType(String sInputType)
762 {
763 String sOutputExtension = "";
764
765 if (sInputType.length() > 0)
766 {
767 sOutputExtension = refConversionOptions.getConversionItem(sInputType, "OUTPUTEXT");
768 if (sOutputExtension.equals(""))
769 sOutputExtension = refConversionOptions.getConversionItem("DEFAULT", "OUTPUTEXT");
770 }
771 return sOutputExtension;
772 } // getExtensionType
773
774
775 /**
776 *
777 */
778 protected boolean createDir(FlexXFile xOutputPath)
779 {
780 // In order to create DOS dirs, must use back-slashes
781 if (xOutputPath != null)
782 {
783 if (xOutputPath.exists() == false)
784 {
785 if (xOutputPath.mkdirs() == false)
786 {
787 //5501=Failure creating output directory path: %%1
788 refImportData.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5501, (new String[] { xOutputPath.getLocalPath() })) );
789 return false;
790 }
791 }
792 } // fOutputPath null if
793 else
794 return false;
795 return true;
796 } // createDir
797
798 private float stringToFloat(String theString)
799 {
800 float returnVal = 0.0f;
801 try{ returnVal = Float.valueOf(theString).floatValue();} catch(NumberFormatException e){};
802 return returnVal;
803 }
804
805 private long stringToLong(String theString)
806 {
807 long returnVal = 0;
808 try{ returnVal = Long.valueOf(theString).longValue();} catch(NumberFormatException e){};
809 return returnVal;
810 }
811
812 /**
813 * Do the actual image conversion creating the thumbnail.
814 * @param sCommandLine The parameter and options string for the conversion process.
815 */
816 protected boolean executeConversion(String[] saCommandLine)
817 {
818 boolean bResult = true;
819
820 // Get the user specified value for the watchdog timer interval
821 // Timeout value is specified in seconds
822 String sTimeOut = context.getProperty("intervaltimer_timeout");
823 // If the intervaltimer_timeout property wasn't set, check for the conversion_timeout
824 // property; this is left here for backward compatibility with release 3.0.6.0
825 if ( sTimeOut == null )
826 sTimeOut = context.getProperty("conversion_timeout");
827
828 ImportConvert refConvert = new ImportConvert();
829
830 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, 4, "Conversion: " + refConvert.toString(saCommandLine));
831 bResult = refConvert.executeConversion(saCommandLine, nOemAlchemy, sTimeOut);
832 if (bResult == true)
833 {
834 //sFormat = refConvert.getFormat();
835 //sPrimaryFormat = refConvert.getFormat();
836 sPrimaryDPIX = refConvert.getDPIX();
837 sPrimaryDPIY = refConvert.getDPIY();
838 sPrimaryWidth = refConvert.getWidth();
839 sPrimaryHeight = refConvert.getHeight();
840 sPrimaryColorDepth = refConvert.getColorDepth();
841 sPrimaryColorSpace = refConvert.getColorMode();
842 sPrimaryFileSize = refConvert.getFileSize();
843 }
844 return bResult;
845 } // executeConversion
846
847 /**
848 *
849 */
850 protected String getExtensionFrom(String sPath)
851 {
852 // Get the extension
853 int nPos = sPath.lastIndexOf(".");
854 if (nPos != -1)
855 return sPath.substring(nPos + 1);
856
857 return "";
858 } // getExtensionFrom
859
860 /**
861 *
862 */
863 protected String getNameNoPath(String sFullPath)
864 {
865 int nPos = sFullPath.lastIndexOf("/");
866 if (nPos != -1)
867 return sFullPath.substring(nPos + 1);
868
869 return sFullPath;
870 } // getNameNoPath
871
872 /**
873 *
874 */
875 protected boolean checkFileType(String sExtension, String sType)
876 {
877 // Return true if the extension is found
878 boolean bResult = false;
879
880 int nExtIndex = -1;
881 if (sExtension.length() > 0)
882 nExtIndex = refConversionOptions.checkExtension(sExtension, sType);
883
884 if (nExtIndex >= 0)
885 {
886 // A default icon exists
887 sThumbServer = refConversionOptions.getDefaultIconServer(nExtIndex);
888 sThumbName = refConversionOptions.getDefaultIconName(nExtIndex);
889 sThumbLocation = refConversionOptions.getDefaultIconLocation(nExtIndex);
890 sThumbSize = "0";
891 bResult = true;
892 }
893 return bResult;
894 } // checkFileType
895
896 private int getRoleId( String sRole )
897 {
898 // Get the proper Role Id
899 if ( sRole.equalsIgnoreCase("LOWRES") )
900 return AssetRolesI.LOWRES;
901 else if ( sRole.equalsIgnoreCase("THUMBNAIL") )
902 return AssetRolesI.THUMBNAIL;
903 else if ( sRole.equalsIgnoreCase("LAYOUT") )
904 return AssetRolesI.LAYOUT;
905 else if ( sRole.equalsIgnoreCase("AUDIO") )
906 return AssetRolesI.AUDIO;
907 else if ( sRole.equalsIgnoreCase("VIDEO") )
908 return AssetRolesI.VIDEO;
909 else if ( sRole.equalsIgnoreCase("ALL") )
910 return -1;
911 else
912 return AssetRolesI.HIGHRES; // HIGHRES is the default
913 }
914
915 private AssetRoleData hasRole( DisguiseAssetRecordData asset )
916 {
917 AssetRoleData role = asset.getAssetRole();
918 if ( role == null ||
919 (!(role instanceof ImageRoleData) &&
920 !(role instanceof LayoutRoleData) &&
921 !(role instanceof VideoRoleData) &&
922 !(role instanceof AudioRoleData)) )
923 return null;
924 else
925 return role;
926 }
927
928 public String getFormat() { return sFormat; }
929 public String getThumbLocation() { return sThumbLocation; }
930 public String getThumbServer() { return sThumbServer; }
931 public String getThumbName() { return sThumbName; }
932 public String getThumbSize() { return sThumbSize; }
933 public String getPrimaryFormat() { return sPrimaryFormat; }
934 public String getPrimaryWidth() { return sPrimaryWidth; }
935 public String getPrimaryHeight() { return sPrimaryHeight; }
936 public String getPrimaryDPIX() { return sPrimaryDPIX; }
937 public String getPrimaryDPIY() { return sPrimaryDPIY; }
938 public String getPrimaryColorDepth() { return sPrimaryColorDepth; }
939 public String getPrimaryColorSpace() { return sPrimaryColorSpace; }
940 public String getPrimaryFileSize() { return sPrimaryFileSize; }
941 } // ImageConvertService