Source code: com/flexstor/flexdbserver/services/resourcefork/ResourceForkService.java
1 /*
2 * ResourceForkService.java
3 *
4 * Copyright $Date: 2003/08/14 07:17:08 $ 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.resourcefork;
12
13 import java.util.Enumeration;
14 import java.util.Hashtable;
15 import java.util.Vector;
16
17 import com.flexstor.common.constants.ActionPropertiesI;
18 import com.flexstor.common.constants.RsrcForkConstantsI;
19 import com.flexstor.common.data.ActionData;
20 import com.flexstor.common.data.ActionResult;
21 import com.flexstor.common.data.AssetRecordData;
22 import com.flexstor.common.errorlogger.FlexError;
23 import com.flexstor.common.io.xfile.FlexXFile;
24 import com.flexstor.common.resources.Resources;
25 import com.flexstor.common.settings.Settings;
26 import com.flexstor.common.util.Diagnostic;
27 import com.flexstor.common.util.ServerList;
28 import com.flexstor.flexdbserver.services.Service;
29 import com.flexstor.flexdbserver.services.ServiceContext;
30 import com.flexstor.flexdbserver.services.resourcefork.dave.DaveResourceFork;
31 import com.flexstor.flexdbserver.services.resourcefork.helios.HeliosResourceFork;
32 import com.flexstor.flexdbserver.services.resourcefork.ipt.IPTResourceFork;
33 import com.flexstor.flexdbserver.services.resourcefork.none.NoneResourceFork;
34 import com.flexstor.flexdbserver.services.resourcefork.xinet.XinetResourceFork;
35 import com.flexstor.flexdbserver.util.PathAnalyzer;
36 import com.flexstor.flexdbserver.util.PathBuilder;
37
38 /**
39 * <P>
40 * ResourceForkService <BR>
41 * <BLOCKQUOTE>
42 * Converts Macintosh files stored in either UNIX or NT file systems to MacBinary format
43 * and vice versa. This service supports files stored using the following AppleTalk vendors
44 * and software: Helios, IPT, Xinet, and DAVE.
45 * </BLOCKQUOTE>
46 * </P>
47 *
48 * <P>
49 * Configurable Properties in services.config <BR>
50 * <BLOCKQUOTE>
51 * NTVolumes: Specifies Windows NTFS volumes where assets reside. These volumes need to
52 * be mapped to the server running the FLEXSTOR.db application server; the mount point should be named after
53 * the volume name assigned in NT. More than one volume is accepted (optional; only required
54 * if files reside in NT volume and AppleTalk Vendor is DAVE). <BR>
55 * Legal values: A comma or space delimited list of NT volumes
56 * </P>
57 * <P>
58 * NTHost: The IP address of the NT host where files are located (optional; only required if files
59 * reside in NT volume and AppleTalk vendor is DAVE). <BR>
60 * Legal values: Valid IP address <BR>
61 * </P>
62 * <P>
63 * NTPort: Port number where Remote Asset Server is running in NT machine (optional; only
64 * required if files reside in NT volume and AppleTalk Vendor is DAVE; defaults to 1099). <BR>
65 * Legal values: Valid port number <BR>
66 * </P>
67 * <P>
68 * appletalk_installpath: Installation directory for AppleTalk software (required for Helios,
69 * IPT, and Xinet). <BR>
70 * Legal values: Full path <BR>
71 * </P>
72 * <P>
73 * appletalk_converterpath: Path to conversion utilities (required for Helios and Xinet). <BR>
74 * Legal values: Full path (not including name of utilities) <BR>
75 * </BLOCKQUOTE>
76 * </P>
77 *
78 * <P>
79 * Input Data Object <BR>
80 * <BLOCKQUOTE>
81 * com.flexstor.common.data.ActionData
82 * </BLOCKQUOTE>
83 * </P>
84 *
85 * <P>
86 * Output Data Object <BR>
87 * <BLOCKQUOTE>
88 * com.flexstor.common.data.ActionResult
89 * </BLOCKQUOTE>
90 * </P>
91 *
92 * <P>
93 * Programmable Properties (passed inside data object) <BR>
94 * <BLOCKQUOTE>
95 * Global Properties (apply to all assets) <BR>
96 * <BLOCKQUOTE>
97 * RsrcForkAction: The action to be performed by this ResourceForkService. <BR>
98 * Data type: Integer <BR>
99 * Legal values: COMBINE_FORKS, COPY_FORKS, MOVE_FORKS, SEPARATE_FORKS, DELETE_FORKS
100 * defined in com.flexstor.common.constants.RsrcForkConstantsI <BR>
101 * </P>
102 * <P>
103 * DestinationLocation: Location where output files will be placed. This property
104 * will only be used if DestinationLocation is not defined as a specific property. <BR>
105 * Data type: String <BR>
106 * Legal values: Full path to destination directory <BR>
107 * </P>
108 * <P>
109 * DeleteDirectory: Attempts to delete original directory after action is performed.
110 * Directory will be deleted only if DeleteOriginal is set to true and no more files
111 * remained inside directory (optional; defaults to false). <BR>
112 * Data type: Boolean <BR>
113 * Legal values: true or false <BR>
114 * </P>
115 * <P>
116 * DeleteOriginal: Delete original file after action is performed (optional; defaults to false). <BR>
117 * Data type: Boolean <BR>
118 * Legal values: true or false <BR>
119 * </P>
120 * <P>
121 * AddToAssetsInProcess: By default, if an action is going to place files in the original
122 * hot directories, the ResourceForkService adds the files to the ASSET_INPROCESS table so
123 * they don't get imported. If a previous service or process is doing so already, this option
124 * can be disabled from the ResourceForkService (defaults to true). <BR>
125 * Data type: Boolean <BR>
126 * Legal values: true or false <BR>
127 * </BLOCKQUOTE>
128 * </P>
129 * <P>
130 * Specific Properties (apply to each individual asset) <BR>
131 * <BLOCKQUOTE>
132 * DestinationLocation: Location where the record will be placed; if not specified it
133 * will use the DestinationLocation property defined in the Global section (if defined);
134 * otherwise, the action will fail. <BR>
135 * Data type: String <BR>
136 * Legal values: Full path to destination directory <BR>
137 * </BLOCKQUOTE>
138 * </BLOCKQUOTE>
139 * </P>
140 */
141 public class ResourceForkService
142 implements Service, RsrcForkConstantsI, ActionPropertiesI
143 {
144 // To get the version number from MKS
145 public final static String IDENTIFIER="$Id: ResourceForkService.java,v 1.6 2003/08/14 07:17:08 dcardozo Exp $";
146
147 public static final String FORK = "MacBinary Converter";
148
149 private ActionData forkData = null;
150 private Vector vRecords = new Vector();
151 private Vector vBadRecords = new Vector();
152 private String sAppServer = null;
153 private String sThisService = "";
154 private String sDefaultDestination = null;
155 private int nAction = -1;
156 private int nServerAppleTalkVendor = RsrcForkConstantsI.NONE;
157 private Hashtable htProperties = null;
158 // This variables are used in case we have non-appletalk vendor assets in a
159 // Appletalk vendor machine
160 private ResourceFork noneResourceFork;
161 private ResourceFork tempResourceFork;
162 private boolean bUsingNoneRsrc;
163 protected ServiceContext context;
164 protected int id = -1;
165 protected String fileSeparator;
166
167 /**
168 * Calls before the service is initialized (before initData is called) to
169 * pass information about the environment in which the service is running.
170 * This environment consists of information about the properties set for the
171 * service in one of these files (services.config, roletype_services.config,
172 * or *.ctl), plus methods to access other information such as an instance
173 * of the service broker to invoke other services, the transaction id for
174 * the service, file separator character and local path for the installation
175 * directory and configuration directory.
176 *
177 * @param context Holds information about the environment in which the service
178 * is running.
179 */
180 public void setServiceContext( ServiceContext context )
181 {
182 this.context = context;
183 id = context.getTransactionId();
184 fileSeparator = context.getFileSeparator();
185 }
186
187 public void initData(ActionData actionData)
188 {
189 forkData = actionData;
190 vRecords = forkData.getRecords();
191
192 sAppServer = Settings.getString( Settings.APP_SERVER_HOST );
193
194 htProperties = context.getProperties();
195 // Load the following key-value pairs into the Hashtable of properties to make them
196 // available for the XXXXResourceFork classes
197 try { nAction = forkData.getInteger( RESOURCEFORK_ACTION ).intValue(); }
198 catch ( NullPointerException npe ) { /* Uses default set in definitions */ }
199 finally{ htProperties.put( RESOURCEFORK_ACTION, new Integer( nAction ) ); }
200
201 boolean bDeleteDir = false;
202 Boolean bBoolean = forkData.getBoolean( DELETE_DIRECTORY );
203 // If bBoolean is not, use default value set in definition
204 if ( bBoolean != null )
205 bDeleteDir = bBoolean.booleanValue();
206
207 htProperties.put( DELETE_DIRECTORY, new Boolean( bDeleteDir ) );
208
209 boolean bDeleteOriginal = false;
210 bBoolean = forkData.getBoolean( DELETE_ORIGINAL );
211 // If bBoolean is not, use default value set in definition
212 if ( bBoolean != null )
213 bDeleteOriginal = bBoolean.booleanValue();
214
215 htProperties.put( DELETE_ORIGINAL, new Boolean( bDeleteOriginal ) );
216
217 boolean bAddToAssetsInProcess = true;
218 bBoolean = forkData.getBoolean( ADD_TO_ASSETS_INPROCESS );
219 // If bBoolean is not, use default value set in definition
220 if ( bBoolean != null )
221 bAddToAssetsInProcess = bBoolean.booleanValue();
222 htProperties.put( ADD_TO_ASSETS_INPROCESS, new Boolean( bAddToAssetsInProcess ) );
223
224 // If a destination is not set per asset, this will be used.
225 sDefaultDestination = forkData.getString( ActionPropertiesI.DESTINATION_LOCATION );
226 if ( sDefaultDestination != null && sDefaultDestination.endsWith( fileSeparator ) == false )
227 sDefaultDestination += fileSeparator;
228 }
229
230 public ActionResult go()
231 {
232 // check taht we have an action to perform
233 if ( nAction == -1 )
234 {
235 //6295=No Action Specified for service.
236 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 6295);
237 // send the response object to the calling class
238 return createBadResultObject();
239 }
240
241 ResourceFork resourceFork = getResourceForkInstance();
242 if ( resourceFork == null )
243 {
244 //6912=An invalid AppleShare/OPI product has been specified; %%1 failed..
245 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, Resources.get(6912, sThisService));
246 // send the response object to the calling class
247 return createBadResultObject();
248 }
249
250 if ( resourceFork.converterExists() == false)
251 {
252 //5538=No MacBinary converter found; check that converter path is properly set in services.config. Process failed.
253 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5538 );
254 // send the response object to the calling class
255 return createBadResultObject();
256 }
257
258 // Do a shallow clone so we can safely remove items from vRecord.
259 Vector vRecordCounter = (Vector)vRecords.clone();
260 AssetRecordData record;
261 FlexXFile xFile, xTargetDir = null;
262 for (Enumeration e = vRecordCounter.elements(); e.hasMoreElements(); )
263 {
264 record = (AssetRecordData) e.nextElement();
265 int nRecordAppleTalkVendor = record.getAppleTalkVendor().intValue();
266 xFile = new FlexXFile( PathBuilder.constructFilePath( record ) );
267
268 if ( nRecordAppleTalkVendor == nServerAppleTalkVendor )
269 {
270 if ( bUsingNoneRsrc )
271 {
272 resourceFork = tempResourceFork;
273 bUsingNoneRsrc = false;
274 }
275 }
276 else if ( nRecordAppleTalkVendor == NONE )
277 {
278 if ( !bUsingNoneRsrc )
279 {
280 if ( noneResourceFork == null )
281 noneResourceFork = (ResourceFork) new NoneResourceFork( id, sThisService, htProperties );
282
283 tempResourceFork = resourceFork;
284 resourceFork = noneResourceFork;
285 bUsingNoneRsrc = true;
286 }
287 }
288 else
289 {
290 vBadRecords.addElement( record );
291 vRecords.removeElement( record );
292 //6296=%%1 cannot be processed. AppleShare/OPI Vendor for this file doesn't match
293 // AppleShare/OPI Vendor installed in server.
294 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 6296, new String[] { xFile.getAbsolutePath() } );
295 continue;
296 }
297
298 if ( nAction != DELETE_FORKS )
299 {
300 String sTargetDir = record.getDestinationLocation();
301 // If a destination location is not specified, check for the default destination; is one is
302 // specified, use it; otherwise, if the action is COMBINE_FORKS or SEPARATE_FORKS use the
303 // source location as the destination; for other actions, fail.
304 if ( (sTargetDir == null) || sTargetDir.equals("") )
305 {
306 if ( sDefaultDestination != null )
307 sTargetDir = sDefaultDestination;
308 else if ( nAction == COMBINE_FORKS || nAction == SEPARATE_FORKS )
309 sTargetDir = record.getLocation();
310 else //if ( nAction == COPY_FORKS || nAction == MOVE_FORKS )
311 {
312 //6936=Destination directory not specified. Cannot proceed with file %%1.
313 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 6936, new String[] { fileSeparator + record.getLocation() + record.getFileName()} );
314 vBadRecords.addElement( record );
315 vRecords.removeElement( record );
316 continue;
317 }
318 }
319 sTargetDir = PathBuilder.constructPath( sAppServer, sTargetDir, true );
320
321 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "Target Dir for MacBinary " + sTargetDir);
322 xTargetDir = new FlexXFile( sTargetDir );
323 }
324
325 boolean bActionResult = false;
326 if ( nAction == DELETE_FORKS )
327 {
328 bActionResult = resourceFork.deleteForks( xFile );
329 }
330 else
331 {
332 if( resourceFork.isDirCreated(xTargetDir) )
333 {
334 if ( resourceFork.checkForDiskSpace( xFile, xTargetDir, nAction ) )
335 {
336 String sFixFileName, sTargetDir;
337 record.setString( "OriginalServer", record.getServer() );
338 record.setString( "OriginalLocation", record.getLocation() );
339 record.setString( "OriginalFileName", record.getFileName() );
340
341 switch ( nAction )
342 {
343 case COMBINE_FORKS:
344 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, 10, "ResourceForkService: Combining Forks" );
345 bActionResult = resourceFork.combineForks( xFile, xTargetDir );
346 // each individual record will need to be updated with the correct filename
347 // and the correct path. If NoneResourceFork was used the file name doesn't
348 // need the .bin extension
349 if ( resourceFork instanceof NoneResourceFork )
350 sFixFileName= record.getFileName();
351 else
352 sFixFileName = new String( record.getFileName() + RsrcForkConstantsI.MACBINARY_EXT );
353
354 record.setFileName( sFixFileName );
355 // Make sure path does not contain a leading slash but contains a trailing slash
356 sTargetDir = xTargetDir.getLocalPath();
357 sTargetDir = PathBuilder.removeFileSeparators( sTargetDir, true, false );
358 sTargetDir = PathBuilder.addFileSeparators( sTargetDir, false, true );
359 record.setLocation( sTargetDir );
360 break;
361 case SEPARATE_FORKS:
362 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, 10, "ResourceForkService: Separating Forks" );
363 bActionResult = resourceFork.separateForks( xFile, xTargetDir );
364 // each individual record will need to be updated with the correct filename
365 // and the correct path.If NoneResourceFork was used the file name doesn't
366 // have the .bin extension
367 sFixFileName = record.getFileName();
368 if ( !(resourceFork instanceof NoneResourceFork) )
369 sFixFileName = sFixFileName.substring( 0, sFixFileName.lastIndexOf(RsrcForkConstantsI.MACBINARY_EXT) );
370
371 record.setFileName( sFixFileName );
372 // Make sure path does not contain a leading slash but contains a trailing slash
373 sTargetDir = xTargetDir.getLocalPath();
374 sTargetDir = PathBuilder.removeFileSeparators( sTargetDir, true, false );
375 sTargetDir = PathBuilder.addFileSeparators( sTargetDir, false, true );
376 record.setLocation( sTargetDir );
377 break;
378 case COPY_FORKS:
379 bActionResult = resourceFork.copyForks( xFile, xTargetDir );
380 break;
381 case MOVE_FORKS:
382 bActionResult = resourceFork.moveForks( xFile, xTargetDir );
383 break;
384 }
385 }
386 else
387 {
388 //6294=There is not enough disk space in %%1 to perform this service on asset %%2.
389 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 6294, new String[] { xTargetDir.getPath(), PathBuilder.constructFilePath(record) } );
390 bActionResult = false;
391 }
392 }
393 else
394 {
395 //5537=Destination %%1 not found; MacBinary converted failed.
396 new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, 5537, new String[] { xTargetDir.getLocalPath() });
397 bActionResult = false;
398 }
399 }
400
401 // we only want to set the result record to [whatever].bin if it was successful
402 // and it was not trying to separate the forks.
403 if ( bActionResult && (nAction != SEPARATE_FORKS) )
404 {
405 String sOutFile = resourceFork.getOutFile();
406 if ( sOutFile.equals("") == false )
407 {
408 String sServer = PathAnalyzer.getServerName(sOutFile);
409 String sLocalParent = PathAnalyzer.getLocalParent(sOutFile);
410 sLocalParent = PathBuilder.addFileSeparators( sLocalParent, false, true );
411 sLocalParent = PathBuilder.removeFileSeparators( sLocalParent, true, false );
412 String sFileName = PathAnalyzer.getFileName(sOutFile);
413
414 record.setString( ActionPropertiesI.DESTINATION_SERVER, sServer );
415 record.setString( ActionPropertiesI.DESTINATION_LOCATION, sLocalParent );
416 record.setString( ActionPropertiesI.DESTINATION_FILENAME, sFileName );
417 }
418 }
419 else if ( !bActionResult )
420 {
421 vBadRecords.addElement( record );
422 vRecords.removeElement( record );
423 }
424 } // end of for (int i...
425
426 // If any temporary directory was created, delete it
427 resourceFork.deleteTempDir();
428
429 ActionResult result;
430 // if there is not a good file, or at least one bad file
431 if (vBadRecords.size() > 0)
432 {
433 result = new ActionResult(false); // failures occurred
434 result.setBadRecords(vBadRecords); // return the bad records
435 }
436 else
437 {
438 result = new ActionResult(true);
439 }
440
441 // we do not need to copy the vRecords back into the forkData ActionData object
442 // because we have been changing it as we go along.
443
444 result.setString( ActionPropertiesI.MESSAGE_STRING, FORK );
445 result.setData(forkData);
446 result.setId( id );
447 // send the response object to the calling class
448 return result;
449 }
450
451 private ResourceFork getResourceForkInstance()
452 {
453 ResourceFork resourceFork = null;
454
455 nServerAppleTalkVendor = ServerList.getAppleTalkVendor( sAppServer );
456
457 switch( nServerAppleTalkVendor )
458 {
459 case DAVE:
460 //6747=Dave Resource Fork Service
461 sThisService = Resources.get(6747) + " (" + id + ")";
462 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "It is a DAVE Fork Service");
463 resourceFork = (ResourceFork) new DaveResourceFork( id, sThisService, htProperties );
464 break;
465 case HELIOS:
466 //5540=Helios Resource Fork Service
467 sThisService = Resources.get(5540) + " (" + id + ")";
468 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "It is a HELIOS Fork Service");
469 resourceFork = (ResourceFork) new HeliosResourceFork( id, sThisService, htProperties );
470 break;
471 case IPT:
472 //5541=IPT Resource Fork Service
473 sThisService = Resources.get(5541) + " (" + id + ")";
474 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "It is a IPT Fork Service");
475 resourceFork = (ResourceFork) new IPTResourceFork( id, sThisService, htProperties );
476 break;
477 case XINET:
478 //5542=Xinet Resource Fork Service
479 sThisService = Resources.get(5542) + " (" + id + ")";
480 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "It is a XINET Fork Service");
481 resourceFork = (ResourceFork) new XinetResourceFork( id, sThisService, htProperties );
482 break;
483 default:
484 //7082=None Resource Fork Service
485 sThisService = Resources.get(7082) + " (" + id + ")";
486 Diagnostic.trace(Diagnostic.APPSERVER_SERVICES, "It is a NONE Fork Service");
487 resourceFork = (ResourceFork) new NoneResourceFork( id, sThisService, htProperties );
488 break;
489 }
490 return resourceFork;
491 }
492
493 private ActionResult createBadResultObject()
494 {
495 // Copy all the records to the Vector containing all bad records; and delete
496 // the list of records from ActionData.
497 vBadRecords = (Vector) vRecords.clone();
498 vRecords.removeAllElements();
499
500 ActionResult result = new ActionResult(false); // failures occurred
501 result.setBadRecords(vBadRecords); // return the bad records
502 result.setString( ActionPropertiesI.MESSAGE_STRING, FORK );
503 result.setData(forkData);
504 return result;
505 }
506 }
507
508
509
510