1 /* Copyright (C) 2000-2004 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * $Id: content.pike,v 1.6 2010/08/18 20:32:45 astra Exp $
24 //! Basic class to support Objects with content (Documents). Content
25 //! is stored in the persistency layer of sTeam.
33 private Thread.Mutex writeMutex = Thread.Mutex();;
37 private int iContentID;
38 private int iContentSize = -1;
40 private object oLockWrite;
41 private object oLockRead;
42 private int iDownloads=0;
44 bool add_data_storage(string s,function a, function b,int|void d);
45 void download_finished();
46 void require_save(string|void a, string|void b) { _Persistence->require_save(a,b); }
50 * This callback function is registered via add_data_storage to provide
51 * necessary data for serialisation. The database object calls this function
52 * to save the values inside the database.
55 * @return a mixed value containing the new introduced persistent values
58 * @see restore_content_data
59 * @see add_data_storage
62 mixed retrieve_content_data(string|void index)
64 if ( CALLER != _Database )
65 THROW("Illegal call to retrieve_content_data()", E_ACCESS);
68 case "CONTENT_SIZE": return iContentSize;
69 case "CONTENT_ID": return iContentID;
73 return ([ "CONTENT_SIZE": iContentSize,
74 "CONTENT_ID": iContentID ]);
80 * This callback function is used to restore data previously read from
82 * retrieve_content_data to restore the state of reading
85 * @param a mixed value previously read via retrieve_content_data
88 * @see retrieve_content_data
89 * @see add_data_storage
92 void restore_content_data(mixed data, string|void index)
94 if ( CALLER != _Database )
95 THROW("Illegal call to restore_content_data()", E_ACCESS);
99 case "CONTENT_SIZE" : iContentSize = data; break;
100 case "CONTENT_ID": iContentID = data; break;
103 else if (arrayp(data)) {
104 [ iContentSize, iContentID ] = data;
107 iContentSize = data["CONTENT_SIZE"];
108 iContentID = data["CONTENT_ID"];
116 * Initialize the content. This function only sets the data storage
117 * and retrieval functions.
124 add_data_storage(STORE_CONTENT, retrieve_content_data,
125 restore_content_data, 1);
129 class DownloadHandler {
133 void create(object oDbHandle, object oFileHandle) {
134 odbhandle = oDbHandle;
135 ofilehandle = oFileHandle;
138 * This function gets called from the socket object associated with
139 * a user downloads a chunk. It cannot be called directly - the
140 * function get_content_callback() has to be used instead.
142 * @param int startpos - the position
143 * @return a chunk of data | 0 if no more data is present
144 * @see receive_content
145 * @see get_content_callback
147 string send_content(int startpos) {
148 if ( !objectp(odbhandle) && !objectp(ofilehandle) )
152 if ( _Persistence->is_storing_content_in_filesystem() &&
153 objectp(ofilehandle) )
154 buf = ofilehandle->read( DB_CHUNK_SIZE );
155 else if ( _Persistence->is_storing_content_in_database() &&
157 buf = odbhandle->read( DB_CHUNK_SIZE );
162 if (objectp(odbhandle) || objectp(ofilehandle))
165 if (iDownloads == 0) // last download finished?
166 destruct(oLockRead); // release writing lock
168 if ( objectp(ofilehandle) ) destruct(ofilehandle);
169 if ( objectp(odbhandle) ) destruct(odbhandle);
171 // callback to notify about finished downloading
178 if ( objectp(odbhandle) || objectp(ofilehandle) )
183 if ( objectp(odbhandle) ) destruct(odbhandle);
184 if ( objectp(ofilehandle) ) destruct(ofilehandle);
187 string _sprintf() { return "DownloadHandler"; }
188 string describe() { return "DownloadHandler"; }
194 class UploadHandler {
199 function content_finished;
201 void create(object oDbHandle, function cfinished, object f) {
202 odbhandle= oDbHandle;
203 content_finished = cfinished;
208 * save_chunk is passed from receive_content to a data storing process,
209 * to store one chunk of data to the database.
211 * @param string chunk - the data to store
212 * @param int start - start position of the chunk relative to complete
214 * @param int end - similar to start
216 * @see receive_content
218 void save_chunk(string chunk) {
219 if ( !stringp(chunk) )
221 local_content_finished();
224 if ( _Persistence->is_storing_content_in_database() )
225 odbhandle->write(chunk);
226 if ( _Persistence->is_storing_content_in_filesystem() && objectp(file) )
228 iWrittenSize += strlen(chunk);
232 * This function gets called, when an upload is finished. All locks
233 * are removed and the object is marked for the database save demon
237 void local_content_finished() {
239 int iWrittenID = odbhandle->dbContID();
243 // clean old content?
244 iContentID = iWrittenID;
245 iContentSize = iWrittenSize;
246 require_save(STORE_CONTENT);
247 odbhandle->close(remote_content_finished);
251 void remote_content_finished() {
260 string _sprintf() { return "UploadHandler"; }
261 string describe() { return "UploadHandler"; }
266 * The function returns the function to download the content. The
267 * object is configured and locked for the download and the
268 * returned function send_content has to be subsequently called
269 * in order to get the data.
272 * @return function "send_content" a function that returns the content
276 function get_content_callback(mapping vars)
278 if ( iContentID == 0 )
279 LOG_DB("get_content_callback: missing ContentID");
283 object lock = writeMutex->lock(); // wait for content_cleanup
288 object oDbDownloadHandle, oFileDownloadHandle;
289 if ( _Persistence->is_storing_content_in_filesystem() )
290 oFileDownloadHandle = _Persistence->open_content_file( iContentID, "r" );
291 if ( _Persistence->is_storing_content_in_database() )
292 oDbDownloadHandle = _Database->new_db_file_handle( iContentID, "r" );
293 object oDownloadHandler =
294 DownloadHandler( oDbDownloadHandle, oFileDownloadHandle );
295 ASSERTINFO( objectp(oDbDownloadHandle) || objectp(oFileDownloadHandle),
296 "No file handle found !" );
297 if ( objectp(oDbDownloadHandle) )
298 LOG( "db_file_handle() allocated, now sending...\n" );
299 if ( objectp(oFileDownloadHandle) )
300 LOG( "file_handle() allocated, now sending...\n" );
301 return oDownloadHandler->send_content;
305 * This function gets called to initialize the download of a content.
306 * The returned function has to be called subsequently to write data.
307 * After the upload is finished the function has to be called with
310 * @param int content_size -- the size of the content that will be
311 * passed in chunks to the function returned
312 * @return a function, that will be used as call_back by the object
313 * calling receive_content to actually store the data.
317 function receive_content(int content_size)
319 object oHandle = _Database->new_db_file_handle(0,"wtc");
321 iContentID = oHandle->dbContID();
322 object file = _Persistence->open_content_file( iContentID, "wct" );
323 object oUploadHandler = UploadHandler(oHandle, content_finished, file);
324 return oUploadHandler->save_chunk;
328 void prepare_upload(int content_size, void|string lock)
334 object get_upload_handler(int content_size)
336 object oHandle = _Database->new_db_file_handle(0,"wtc");
338 iContentID = oHandle->dbContID();
339 object file = _Persistence->open_content_file( iContentID, "wct" );
340 return UploadHandler(oHandle, content_finished, file);
344 * update_content_size - reread the content size from the database
345 * this is a hot-fix function, to allow resyncing with the database tables,
346 * this function definitively should be obsolete.
351 void update_content_size()
353 iContentSize = _Persistence->get_content_size( iContentID );
354 require_save(STORE_CONTENT);
358 * evaluate the size of this content
361 * @return int - size of content in byte
366 if (!iContentID) // no or unfinished upload
369 if ( iContentSize <= 0 )
370 update_content_size();
377 * Get the ID of the content in the database.
379 * @return the content id
387 void set_content_id(int id)
389 if ( CALLER != get_factory(CLASS_DOCUMENT) )
390 steam_error("Unauthorized call to set_content_id() !");
392 require_save(STORE_CONTENT);
396 * Get the content of the object directly. For large amounts
397 * of data the download function should be used. It is possible
398 * to pass a len parameter to the function so only the first 'len' bytes
399 * are being returned.
401 * @param int|void len
402 * @return the content
406 _get_content(int|void len)
410 LOG_DB("content.get_content() of " + iContentID);
415 catch(lock = writeMutex->trylock());
416 if ( !objectp(lock) )
417 THROW("no simultanous write access on content", E_ACCESS);
423 buf = _Persistence->get_content( iContentID, len );
428 destruct(oLockWrite);
438 string get_content(int|void len)
440 return _get_content(len);
444 * set_content, sets the content of this instance.
446 * @param string cont - this will be the new content
447 * @return int - content size (or -1?)
448 * @see receive_content, save_content
452 set_content(string cont)
454 if ( !stringp(cont) )
455 error("set_content: no content given - needs string !");
457 // save directly! (use receive content and upload functionality
458 // for large amount of data)
459 prepare_upload(strlen(cont));
461 iContentID = _Persistence->set_content( cont );
463 // store content id and size
464 iContentSize = strlen(cont);
465 require_save(STORE_CONTENT);
467 // call myself - write_now() finishes writting directly
473 * When the object is deleted its content has to be removed too.
482 object lock = writeMutex->lock();
483 _Persistence->delete_content( iContentID );
492 void content_finished() {
493 // call for compatibility reasons
499 void content_begin() {