Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/flexstor/flexdbserver/services/linkupdate/LinkUpdateService.java


1   /*
2    * LinkUpdateService.java
3    *
4    * Copyright $Date: 2003/08/11 02:22:38 $ 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.linkupdate;
12  
13  import java.util.ArrayList;
14  import java.util.HashSet;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.Set;
18  import java.util.StringTokenizer;
19  import java.util.Vector;
20  
21  import com.flexstor.common.constants.ActionPropertiesI;
22  import com.flexstor.common.constants.ServicesI;
23  import com.flexstor.common.data.ActionData;
24  import com.flexstor.common.data.ActionResult;
25  import com.flexstor.common.data.AssetRecordData;
26  import com.flexstor.common.data.ejb.disguiserecord.DisguiseAssetRecordData;
27  import com.flexstor.common.data.ejb.disguiserecord.DisguiseElementRecordData;
28  import com.flexstor.common.data.ejb.disguiserecord.DisguiseRecordData;
29  import com.flexstor.common.errorlogger.FlexError;
30  import com.flexstor.common.exceptions.InvalidDataException;
31  import com.flexstor.common.gateway.exceptions.TransactionFailedException;
32  import com.flexstor.common.importprocessor.ImportData;
33  import com.flexstor.common.importprocessor.ImportResult;
34  import com.flexstor.common.resources.Resources;
35  import com.flexstor.common.services.ServiceArgumentsI;
36  import com.flexstor.common.services.SrvcNotAvailException;
37  import com.flexstor.common.util.DirectDotHot;
38  import com.flexstor.ejb.disguise.persist.ServerDisguiseExtendData;
39  import com.flexstor.flexdbserver.disguise.DisguiseLoader;
40  import com.flexstor.flexdbserver.disguise.DisguiseLoaderException;
41  import com.flexstor.flexdbserver.services.Service;
42  import com.flexstor.flexdbserver.services.ServiceContext;
43  
44  /**
45   * <P>
46   * LinkUpdateService <BR>
47   * <BLOCKQUOTE>
48   *    The Link Update Service run during the import of new assets into FLEXSTOR.db containing links 
49   *    to other assets in the database. The service is intended to be a generic service that can be 
50   *    used in any import or output process. <BR>
51   *    This service provides the following functions: <BR>
52   * <P>
53   *       1) Checks to see if the files contained in the import data object are already in the system. <BR>
54   *          Checking is done by filename only. Check is done for assets already cataloged. <BR>
55   *       2) Updates ImportData to reflect the results of the operation above: creates new elements <BR>
56   *          for new assets, if required, and updates asset information for existing assets; or remove <BR>
57   *          those elements for which one of the links could not be found. <BR>
58   *       3) For files not already in the system, processes a failure using FLEXdbServer standard error <BR>
59   *          handling mechanism and possibly send an email to an assigned user indicating that some of <BR>
60   *          the files are not present. <BR>
61   * </P>
62   *    The following rules apply when defining arguments: <BR>
63   * <P>
64   *       - Properties defined in roletype_services.config override properties already defined in 
65   *         services.config. <BR>
66   *       - Properties defined in either custom_process.xml or FLEXsi xml overrides properties 
67   *         already defined in services.config. <BR>
68   * </P>
69   *    <BR>
70   *    If run as an import service, this service must be defined in the pre-services section of the 
71   *    control file after both the Typer and RoleAssign services have been invoked. This convention 
72   *    guarantees that assets of different types, linked to other assets in the data object, are 
73   *    processed together, before the splitting of the data object by type and role. <BR>
74   * 
75   * </BLOCKQUOTE>
76   * </P>
77   *
78   * <P>
79   * Input Data Object <BR>
80   * <BLOCKQUOTE>
81   *    com.flexstor.common.data.ActionData or
82   *    com.flexstor.common.importprocessor.ImportData
83   * </BLOCKQUOTE>
84   * </P>
85   *
86   * <P>
87   * Output Data Object <BR>
88   * <BLOCKQUOTE>
89   *    com.flexstor.common.data.ActionResult containing a 
90   *    com.flexstor.common.importprocessor.ImportData object.
91   * </BLOCKQUOTE>
92   * </P>
93   *
94   * <P>
95   * Configurable Properties (in services.config or roletype_services.config) and <BR>
96   * Programmable Properties (passed inside data object ) <BR>
97   * <BLOCKQUOTE>
98   *    Global Properties (apply to all assets) <BR>
99   * <BLOCKQUOTE>
100  * <P>
101  *       inrole: The role of the assets to be linked to the primary parent asset. <BR>
102  *       Data type: String <BR>
103  *       Legal values: One of the following: HIGHRES, LOWRES, THUMBNAIL, LAYOUT, AUDIO, 
104  *                                           VIDEO or ALL <BR>
105  * </P>
106  * <P>
107  *       intype: The type of the assets to be linked to the primary parent asset. <BR>
108  *       Data type: String <BR>
109  *       Legal values: A type as defined in typerdat.txt <BR>
110  * </P>
111  * <P>
112  *       inflag: The flag of the assets to be linked to the primary parent asset. <BR>
113  *       Data type: String <BR>
114  *       Legal values: One of the following: PARENT, TEMP_PARENT, CHILDREN, TEMP_CHILDREN, 
115  *                                           ALL, TEMP_ALL <BR>
116  * </P>
117  * <P>
118  *       disguiseList: Disguises in which to check for the existence of a file. If multiple disguises
119  *       are specified they are checked in the order in which they are listed. <BR>
120  *       Data type: String <BR>
121  *       Legal values: comma-separated list of disguise labels or ids <BR>
122  * </P>
123  * <P>
124  *       createNewElement: If this flag is set to true and an asset is not found in the system <BR>
125  *       (but is part of the current ImportData object) a new element is created and the asset <BR>
126  *       imported as a primary asset; the linked assets will then be a logical link to this asset <BR>
127  *       in the database. If the flag is set to false the asset is removed from the ImportData <BR>
128  *       object and added to the error list. If the flag is set to false and the failing asset is <BR>
129  *       a child asset then the entire element is removed and placed in a error state. This assures <BR>
130  *       that only complete collections are imported into the system. <BR>
131  *       Data type: boolean <BR>
132  *       Legal values: true or false <BR>
133  * </P>
134  * </BLOCKQUOTE>
135  * </BLOCKQUOTE>
136  * </P>
137  */
138 public class LinkUpdateService 
139    implements Service
140 {
141    // MKS Identifier
142   public final static String IDENTIFIER = "$id";
143    
144    protected ServiceContext     context;
145    protected int                  id = -1;
146    protected String               fileSeparator;
147    private ImportData importData;
148    private String       sThisService;
149    private boolean      bIsImportData;       // Set to true only if the data passed to the service is an ImportData
150    private boolean      bCreateNewElement;
151    private Set          sFailedParent = new HashSet(); // Use a set to prevent duplicate assets to be added
152    private String       sDisguiseList;       // This is a list containing all disguises used for searching. It is kept
153                                              // for error logging purposes
154 
155    /**
156     * Calls before the service is initialized (before initData is called) to 
157     * pass information about the environment in which the service is running.
158     * This environment consists of information about the properties set for the
159     * service in one of these files (services.config, roletype_services.config,
160     * or *.ctl), plus methods to access other information such as an instance
161     * of the service broker to invoke other services, the transaction id for
162     * the service, file separator character and local path for the installation
163     * directory and configuration directory.
164     * 
165     * @param context Holds information about the environment in which the service
166     *                is running.
167     */
168    public void setServiceContext( ServiceContext context )
169    {
170       this.context = context;
171       id = context.getTransactionId();
172       fileSeparator = context.getFileSeparator();
173    }
174    
175   /**
176     * 
177    * @param data
178    * @see com.flexstor.flexdbserver.services.Service#initData(com.flexstor.common.data.ActionData)
179    */
180   public void initData(ActionData data)
181   {
182       //7098=Linked File Update Service
183       sThisService = Resources.get(7098) + " (" + id + ")";
184 
185       // If the ActionData is not an instance of ImportData, convert it to one
186       if ( data instanceof ImportData )
187       {
188          bIsImportData = true;
189          importData = (ImportData) data;
190       }
191       else
192          importData = createImportData(data);
193          
194       // Get CreateNewElement property
195       bCreateNewElement = Boolean.valueOf(context.getProperty( "createNewElement" )).booleanValue();
196   }
197 
198   /**
199    * @return ActionResult
200    * @see com.flexstor.flexdbserver.services.Service#go()
201    */
202   public ActionResult go()
203   {
204       Vector vAssets = getImportAssets();
205       if ( vAssets != null )
206       {
207          //  Validate disguise (id/label)
208          sDisguiseList = context.getProperty("disguiseList");
209          int[] naDisguises = validateDisguises( sDisguiseList );
210          if ( naDisguises != null )
211          {
212         try
213         {
214           //  Constructs Search Criteria:
215           //     select ... from ...
216           //     where (filename = X and not archived) or 
217           //     (filename = X.bin and archived)
218           //  
219           //  and performs search
220           ImportedFileNameSearch importedSearch = new ImportedFileNameSearch( vAssets, naDisguises );
221           importedSearch.findAssets();
222           Vector vMatchedInDB  = importedSearch.getMatchedAssets();
223           Vector vUnmatched    = importedSearch.getUnmatchedAssets();
224           
225                if ( vMatchedInDB != null )
226                   updateImportData( vMatchedInDB );
227                  
228                if ( vUnmatched != null )
229                {
230                   // Remove from the ImportData object, those assets that were not found in the database
231                   // (including the primary parent so the entire element won't get added)
232                   removePrimaryParents( vUnmatched );
233                   
234              //  Log error including full path to unavailable assets
235              //  
236              //  Send an email to the user specified in the argument indicating that the new record
237              //  could not be created because one of its link is not already in the system.
238              logFailedAssets( vUnmatched, null );
239                }
240         }
241         catch ( InvalidDataException ide )
242         {
243                logFailedAssets( vAssets, ide.getMessage() );
244         }
245             catch ( TransactionFailedException tfe )
246             {
247                logFailedAssets( vAssets, tfe.getMessage() );
248             }
249          }
250       }
251       
252       // Return true only if there are primary assets left in the data object, otherwise return false
253       ImportResult result;
254       if ( importData.getDisguiseRecordRef().getPrimaryAssets().isEmpty() )
255          result = new ImportResult(false);
256       else
257          result = new ImportResult(true);
258           
259       result.setImportData(importData);
260       return result;
261   }
262 
263    /**
264     * Creates a ImportData object based on the ActionData object passed in the argument
265     * @param data
266     * @return ImportData
267     */
268    private ImportData createImportData( ActionData data )
269    {
270       // Call the ImportDataCreateService in order to convert the ActionData object into
271       // a ImportData object.
272       // Before calling the ImportDataCreateService, we first need to set the property that will specify
273       // where the hot directory is. Use the first AssetRecordData in ActionData
274       DirectDotHot refDirectDotHot = new DirectDotHot();
275       
276       AssetRecordData record = (AssetRecordData) data.getRecords().elementAt(0);
277       String sRecordCtlFilePath = refDirectDotHot.getCtlFileName( fileSeparator + record.getLocation(), true );
278       data.setString( ActionPropertiesI.HOT_DIRECTORY, sRecordCtlFilePath );
279       data.setServiceName( ServicesI.IMPORTDATA_CREATE_SERVICE );
280       ActionResult srvResult = null;
281       try { srvResult = (ActionResult) context.getServiceBroker().invokeImmediately(data); }
282       catch ( SrvcNotAvailException snae ) {}
283 
284       if ( srvResult != null && srvResult.isSuccess() )
285          return (ImportData) srvResult.getData();
286       else
287          return null;
288    }
289    
290    /**
291     * Returns a Vector with the DisguiseAssetRecordData objects in the ImportData object
292     * @return Vector
293     */
294    private Vector getImportAssets()
295    {
296       DisguiseRecordData disguiseRecord = importData.getDisguiseRecordRef();
297       if ( disguiseRecord != null )
298       {
299          String sRole = context.getProperty(ServiceArgumentsI.ROLE_DATA_SOURCE);
300          String sType = context.getProperty(ServiceArgumentsI.TYPE_DATA_SOURCE);
301          String sFlag = context.getProperty(ServiceArgumentsI.FLAG_DATA_SOURCE);
302          Vector vAssets = disguiseRecord.getAssets(sRole, sType, sFlag);
303          if ( vAssets != null && !vAssets.isEmpty() )
304             return vAssets;
305       }
306       
307       //6293=No assets found for this service.
308       logError( Resources.get(6293) );
309       return null;
310    }
311    
312    /**
313     * Takes the disguiseList argument (from the services.config, *.ctl or roletype_services.config), 
314     * parses it and validates each disguise (label or id) against the database list. Return only 
315     * those that passed validation; for failed ones, log an error.
316     * @param sDisguiseList
317     * @return int[] list of valid disguise ids, or null if none passed validation
318     */
319    private int[] validateDisguises( String sDisguiseList )
320    {
321       if ( sDisguiseList != null )
322       {
323          String sToken;
324          Integer nToken;
325          ServerDisguiseExtendData disguiseExt;
326          List lDisguiseList = new ArrayList();
327          
328          StringTokenizer st = new StringTokenizer( sDisguiseList, "," );
329          
330          while ( st.hasMoreTokens() )
331          {
332             sToken = st.nextToken().trim();
333    
334             // Try to convert the token to an Integer, if it works the chances are it represents an id
335             try { nToken = Integer.valueOf(sToken); }
336             catch ( NumberFormatException nfe ) { nToken = null; }
337    
338             try
339             {
340                if ( nToken != null )
341                {
342                   DisguiseLoader.getDisguise(nToken.intValue());
343                   lDisguiseList.add(nToken);
344                   continue;
345                }
346             }
347             catch( DisguiseLoaderException dle )
348             {
349                // If the exception is thrown because the number does not represent a disguise id; 
350                // try to use it as the disguise label in the next try/catch block
351             }
352             
353             try
354             {
355                // disguise is not a number, test for label
356                disguiseExt = DisguiseLoader.getDisguise(sToken);
357                lDisguiseList.add( new Integer(disguiseExt.getId()) );
358                continue;
359             }
360             catch( DisguiseLoaderException dle )
361             {
362                // Don't do anything here, errors will be reported in the next statement
363             }
364    
365             // Token is not a valid disguise, abort
366             // 7066=%1 does not represent a valid disguise
367             logError( Resources.get( 7066, sToken ) );
368          }
369 
370          int[] naDisguises = new int[ lDisguiseList.size() ];
371          for ( int i = 0; i < lDisguiseList.size(); i++ )
372             naDisguises[i] = ((Integer)lDisguiseList.get(i)).intValue();
373          
374          return naDisguises;      
375       }
376       else
377          return null;
378    }
379 
380    /**
381     * After the search, the asset records are already updated with the server, location and filename 
382     * of its match. What is left for this method to do is go through the primary parents and 
383     * make sure their path is also updated (if CreateNewElement is true) or they are removed from
384     * the ImportData object (if CreateNewElement is false)
385     * 
386     * @param vAssets
387     * @return Vector
388     */
389    private void updateImportData( Vector vAssets )
390    {
391       // Get all primary assets
392       DisguiseRecordData disguiseRecord = importData.getDisguiseRecordRef();
393       Vector vPrimaryAssets = disguiseRecord.getPrimaryAssets();
394       // For each primary asset we need to find out if they are also defined as child of another asset
395       DisguiseAssetRecordData parent, child;
396       
397       for ( Iterator i = vAssets.iterator(); i.hasNext(); )
398       {
399          child = (DisguiseAssetRecordData) i.next();
400          if ( bCreateNewElement )
401             child.addProperty("createNewElement", Boolean.TRUE );
402 
403          for ( Iterator j = vPrimaryAssets.iterator(); j.hasNext(); )
404          {
405             parent = (DisguiseAssetRecordData) j.next();
406             if ( parent.getFileName().equals(child.getFileName()) )
407             {
408                AssetUpdate.addParentCopySourceInfo(parent, child);
409                
410                // Delete the element that contains this primary asset.
411                removeElement( parent );
412             }
413          }
414       }
415    }
416    
417    /**
418     * For each asset in the unmatched list, find its primary parent and remove it from the
419     * ImportData object.
420     * 
421     * @param vUnmatched List of children that were not matched in the database
422     */
423    private void removePrimaryParents( Vector vUnmatched )
424    {
425       DisguiseAssetRecordData parent;
426       for ( Iterator i = vUnmatched.iterator(); i.hasNext(); )
427       {
428          parent = getPrimaryParent( (DisguiseAssetRecordData)i.next() );
429          removeElement( parent );
430          sFailedParent.add(parent); 
431       }
432    }
433    
434    private void removeElement( DisguiseAssetRecordData parent )
435    {
436       // Delete the element that contains this primary asset.
437       DisguiseElementRecordData element = (DisguiseElementRecordData) parent.getTraversalPath().lastElement();
438       importData.getDisguiseRecordRef().deleteElement(element);
439    }
440    
441    private DisguiseAssetRecordData getPrimaryParent( DisguiseAssetRecordData asset )
442    {
443       if ( asset.isPrimaryParent() )
444          return asset;
445       else
446          return getPrimaryParent( asset.getParent() );
447    }
448    
449    /**
450     * Log error including full path to unavailable assets
451     *  
452     * Send an email to the user specified in the argument indicating that the new record
453     * could not be created because one of its link is not already in the system.
454     */
455    private void logFailedAssets( Vector vUnmatched, String sErrorMsg )
456    {
457       DisguiseAssetRecordData asset;
458       StringBuffer sbError = new StringBuffer();
459       if ( sErrorMsg == null )
460       {
461          // 7099=The following assets were not found in the following disguises %%1:
462          sbError.append( Resources.get(7099, sDisguiseList) );
463          sbError.append( "\r\n" );
464          for ( Iterator i = vUnmatched.iterator(); i.hasNext(); )
465          {
466             asset = (DisguiseAssetRecordData)i.next();
467             sbError.append( "    " );
468             sbError.append( asset.getLocation());
469             sbError.append( asset.getFileName());
470             sbError.append( "\r\n" );
471          }
472       }
473       else
474       {
475          sbError.append( sErrorMsg );
476          sbError.append( "\r\n" );
477       } 
478       // 7100=The following assets and their children will not be imported:
479       sbError.append( Resources.get(7100) );
480       sbError.append( "\r\n" );
481       for ( Iterator i = sFailedParent.iterator(); i.hasNext(); )
482       {
483          asset = (DisguiseAssetRecordData)i.next();
484          sbError.append( "    " );
485          sbError.append( asset.getLocation());
486          sbError.append( asset.getFileName());
487          sbError.append( "\r\n" );
488       }
489       
490       logError( sbError.toString() );
491    }
492    
493    private void logError( String sErrorMsg )
494    {
495       FlexError error = new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, sErrorMsg);
496       if ( bIsImportData )
497          importData.addErrorRecord(error);
498    }
499 }