Source code: com/flexstor/flexdbserver/services/asset/DBUpdateService.java
1 /*
2 * DBUpdateService.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.Iterator;
14 import java.util.StringTokenizer;
15 import java.util.Vector;
16
17 import com.flexstor.common.constants.ServicesI;
18 import com.flexstor.common.data.ActionData;
19 import com.flexstor.common.data.ActionResult;
20 import com.flexstor.common.data.ejb.disguiserecord.DisguiseRecordData;
21 import com.flexstor.common.errorlogger.FlexError;
22 import com.flexstor.common.gateway.ImportRecordGateway;
23 import com.flexstor.common.gateway.exceptions.TransactionFailedException;
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.ServiceBrokerI;
28 import com.flexstor.common.services.SrvcNotAvailException;
29 import com.flexstor.flexdbserver.services.Service;
30 import com.flexstor.flexdbserver.services.ServiceContext;
31 import com.flexstor.flexdbserver.translog.TransactionImport;
32
33 /**
34 * <P>
35 * DBUpdateService <BR>
36 * <BLOCKQUOTE>
37 * Inserts or updates asset information in the database, creating the bucket structure if necessary.
38 * The service first calls the DefaultViewService to assign a default displayable asset for each one
39 * (if an asset has a default view already assigned, this service will not assign a new one). <BR>
40 *
41 * This service is usually called during imports (from hot directories, from GUIs and html uploads)
42 * and also when regenerating the thumbnail of an asset being checked in. Because of this, the DBUpdate
43 * Service can handle the insert and updates of buckets, elements and assets. <BR>
44 * The DB Update service delegates all the database operations to the DisguiseRecordPersist bean. <BR>
45 * To determine whether an asset needs to be updated or inserted, the bean first looks if the asset
46 * has a record id set in the data object; if it does, an update of such asset is performed, otherwise
47 * an insert is done. <BR>
48 * For buckets and elements the first step is to retrieve from the database its record id based on the
49 * meta data set in the data object (NOTE: this operation is performed once outside the DB Update
50 * Service- by the Import Processor -and later checked again by the DB Update Service). Later the
51 * DisguiseRecordPersist bean will check if a record id has been set for the bucket or element in the
52 * data object; if true it will use it to query the database and make sure it is already a row in the
53 * table (a record id could be set by some other process without updating the database). If the record
54 * id is already in the database an update is performed. In cases where the data object does not contain
55 * a record id or when the record id in the data object does not exits in the bucket or element table,
56 * an insert is performed. <BR>
57 * Updates are performed for buckets and elements only if meta data is available. <BR>
58 *
59 * NOTE: During a hot directory import to a bucket that already exists in the database, the
60 * DisguiseRecordPersist bean updates the row in the database with meta data that is already set. This
61 * is done because the import processor adds the sub-directories name as fields in the buckets' data object;
62 * later the DisguiseRecordPersist bean checks if there is at least a field in the data object, and if so,
63 * uses it to update the bucket. <BR>
64 * </BLOCKQUOTE>
65 * </P>
66 *
67 * Configurable Properties in roletype_services.config <BR>
68 *
69 * <P>
70 * explicit_rollback_segment: If enabled, will explicitly allocate a dedicated rollback segment in the
71 * database for this transaction (optional; defaults to false). <BR>
72 * Legal values: true or false
73 * </P>
74 * <P>
75 * Input Data Object <BR>
76 * <BLOCKQUOTE>
77 * com.flexstor.common.importprocessor.ImportData
78 * </BLOCKQUOTE>
79 * </P>
80 *
81 * <P>
82 * Output Data Object <BR>
83 * <BLOCKQUOTE>
84 * com.flexstor.common.importprocessor.ImportResult
85 * </BLOCKQUOTE>
86 * </P>
87 *
88 * <P>
89 * Programmable Properties (passed inside data object) <BR>
90 * <BLOCKQUOTE>
91 * Global Properties (apply to all assets) <BR>
92 * <BLOCKQUOTE>
93 * bUpdateAssets: Signals whether the assets are for inserting or updating. Set in
94 * ImportData.setUpdate( boolean value ) (defaults to false). <BR>
95 * Data type: boolean <BR>
96 * Legal values: true or false <BR>
97 * </BLOCKQUOTE>
98 * </BLOCKQUOTE>
99 * </P>
100 */
101 public class DBUpdateService
102 implements Service
103 {
104 public static final String IDENTIFIER = "$Id: DBUpdateService.java,v 1.4 2003/08/11 02:22:28 aleric Exp $";
105
106 protected ImportData data = null;
107
108 private String sThisService = "";
109 protected ServiceContext context;
110 protected ServiceBrokerI serviceBroker;
111 protected int id = -1;
112
113 public DBUpdateService()
114 {
115 super();
116 }
117
118 /**
119 * Calls before the service is initialized (before initData is called) to
120 * pass information about the environment in which the service is running.
121 * This environment consists of information about the properties set for the
122 * service in one of these files (services.config, roletype_services.config,
123 * or *.ctl), plus methods to access other information such as an instance
124 * of the service broker to invoke other services, the transaction id for
125 * the service, file separator character and local path for the installation
126 * directory and configuration directory.
127 *
128 * @param context Holds information about the environment in which the service
129 * is running.
130 */
131 public void setServiceContext( ServiceContext context )
132 {
133 this.context = context;
134 this.serviceBroker = context.getServiceBroker();
135 }
136
137 /**
138 * Data initialization method called at the beginning of the service.
139 *
140 * @param ActionData is the super class of the ImportData wrapper object
141 * which contains DisguiseRecords, which again contains more
142 * DisguiseBucketRecord(s) and/or ElementRecord(s). Finally
143 * there are DisguiseAssetRecord(s) that is useful to extract
144 * information for the preparation of DBUpdateService.
145 */
146 public void initData( ActionData actionData )
147 {
148 data = (ImportData)actionData;
149
150 //6318=Database Update Service
151 sThisService = Resources.get(6318) + " (" + id + ")";
152 }
153
154 /**
155 * Start of the DBUpdateService.
156 *
157 * @return a Result object with the DBUpdate process results.
158 */
159 public ActionResult go()
160 {
161 // Check for assets; if no assets in data object, don't call the DefaultViewService,
162 // we are importing empty buckets
163 Vector vAssets = data.getDisguiseRecordRef().getPrimaryAssets();
164
165 if ( vAssets != null && vAssets.size() > 0 )
166 {
167 ImportResult result = setDefaultViewAssets();
168 if ( result == null || !result.isSuccess() )
169 return new ImportResult(false);
170 else
171 data = result.getImportData();
172 }
173
174 // Now do the import and return result
175 return importAssets();
176 }
177
178 private ImportResult setDefaultViewAssets()
179 {
180 try
181 {
182 data.setServiceName( ServicesI.DEFAULTVIEW_SERVICE );
183 return ( ImportResult) serviceBroker.invokeImmediately(data);
184 }
185 catch( SrvcNotAvailException e )
186 {
187 data.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, e) );
188 return new ImportResult( false );
189 }
190 }
191
192 private ImportResult importAssets()
193 {
194 DisguiseRecordData disguiseData = data.getDisguiseRecordRef();
195
196 ImportRecordGateway importGateway = null;
197 try
198 {
199 importGateway = new ImportRecordGateway();
200 importGateway.connect();
201
202 boolean bContinue = true;
203 // If this is an insert, first check for the existence of buckets, then do the insert
204 // and finaly do the transaction logging; otherwise just do an insertRecord to update
205 // the database; since during update all buckets already exist in the database.
206 boolean bInsert = !data.isUpdate();
207 if ( bInsert )
208 {
209 disguiseData = importGateway.checkForExisting(disguiseData);
210 Vector vErrors = disguiseData.getErrors();
211 if ( vErrors != null && vErrors.size() > 0 )
212 {
213 // The elements of the Vector are Strings of the following format:
214 // 1000,%%1,%%2,%%3
215 // where the first item is the resource id and the next, if any, are arguments
216 // to be included in the resource
217 int nResourceId = -1;
218 String[] saArgs = null;
219 for ( Iterator i = vErrors.iterator(); i.hasNext(); )
220 {
221 StringTokenizer st = new StringTokenizer( (String)i.next(), "," );
222 nResourceId = Integer.parseInt( st.nextToken() );
223 if ( st.hasMoreTokens() )
224 {
225 saArgs = new String[ st.countTokens() ]; // Remove resource id
226 int j = 0;
227 while ( st.hasMoreTokens() )
228 saArgs[j++] = st.nextToken();
229 }
230 }
231 data.addErrorRecord( new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, nResourceId, saArgs ) );
232
233 // Fail if there are not buckets to import
234 if ( disguiseData.getBucketsAsVector() == null || disguiseData.getBucketsAsVector().size() == 0 )
235 bContinue = false;
236 }
237 }
238
239 if ( bContinue )
240 {
241 // Lets find out if we need to explicitly allocate a rollback segment
242 String sAllocRollBackSgmnt = context.getProperty( "explicit_rollback_segment" );
243 if ( sAllocRollBackSgmnt != null && sAllocRollBackSgmnt.equalsIgnoreCase("true") )
244 disguiseData.setAllocateRollBackSegment(true);
245 else
246 disguiseData.setAllocateRollBackSegment(false);
247
248 disguiseData = importGateway.insertRecord(disguiseData);
249 // Save back to the Import Data
250 data.setDisguiseRecordRef(disguiseData);
251
252 if ( bInsert )
253 (new TransactionImport( data )).log();
254 }
255
256 ImportResult result = new ImportResult(bContinue);
257 result.setImportData(data);
258 return result;
259 }
260 catch(TransactionFailedException rtfe)
261 {
262 return new ImportResult(false);
263 }
264 finally
265 {
266 if ( importGateway != null )
267 importGateway.dispose();
268 }
269 }
270 } // public class DBUpdateService