1 /* Copyright (C) 2000-2006 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: DocLpc.pike,v 1.2 2008/05/01 14:52:20 exodusd Exp $
19 inherit "/classes/Document";
20 set_content("inherit \"/classes/Script\";\n#include <macros.h>"+
21 (set_content("inherit \"/classes/Script\";\n"+
22 (set_content("inherit \"/classes/Script;\n"+
23 set_content("inherit \"/classes/Script\";\n#include <macros.h>;\n"+
24 set_content("inherit \"/classes/Script\";\n#include <macros.h>;\n"+
28 #include <attributes.h>
33 #include <exception.h>
34 class DocLpc : public Document{
39 /* this object really represents a factory if executed !
45 mapping mRegAttributes; // registered attributes for this factory
46 array aoInstances; // Instances of this class
50 * Initialize the document.
59 mRegAttributes = ([ ]);
61 add_data_storage(STORE_DOCLPC, retrieve_doclpc, restore_doclpc);
67 * Get the object class - CLASS_DOCLPC in this case.
73 return ::get_object_class() | CLASS_DOCLPC;
77 * Destructor of this object.
84 aoInstances -= ({ 0 });
86 foreach(aoInstances, object obj)
88 obj->delete(); // delete all instances
95 * Execute the DocLPC which functions as a factory class.
96 * The parameters must include a name 'name' and might include
97 * a 'moveto' variable to move the object.
99 * @param mapping variables - execution parameters.
100 * @return the newly created object.
102 mixed execute(mapping variables)
104 if ( objectp(_CODER) && sizeof(_CODER->get_members()) > 0 ) {
105 // check if User code is allowed, creator needs to be coder
106 // and no other user should have write access on this script
107 object creator = get_creator();
108 if ( !_CODER->is_member(creator) && !_ADMIN->is_member(creator) )
109 THROW("Unauthorized Script", E_ACCESS);
110 mapping sanc = get_sanction();
111 foreach(indices(sanc), object grp) {
112 if ( (sanc[grp] & SANCTION_WRITE ) && !_ADMIN->is_member(grp) &&
113 !_CODER->is_member(grp) && grp != _ADMIN && grp != _CODER )
114 THROW("Write access for non coder group enabled - aborting !",
119 try_event(EVENT_EXECUTE, CALLER, 0);
122 if ( !mappingp(variables) )
123 THROW( "No variables param to DocLpc->execute()!", E_ERROR );
124 if ( !stringp(variables->name) || variables->name == "" )
125 THROW( "No name provided to DocLpc->execute()!", E_ERROR );
128 master()->clear_compilation_failures();
129 object oeuid = geteuid();
130 seteuid(get_creator());
131 obj = ((program)("/DB:#"+get_object_id()+".pike"))(variables->name);
133 THROW( "Failed to obtain instance for /DB:#"+get_object_id()+".pike"+
134 "with name '"+variables->name+"'!", E_ERROR );
137 object mv = find_object((int)variables["moveto"]);
141 if ( !stringp(variables["name"]) )
142 variables->name = "";
143 // first add to instances
144 aoInstances -= ({ 0 });
145 obj->sanction_object(query_attribute("DOC_USER_MODIFIED"), SANCTION_ALL);
146 obj->sanction_object(get_creator(), SANCTION_ALL);
148 obj->set_attribute(OBJ_NAME, variables["name"]);
149 obj->set_attribute(OBJ_CREATION_TIME, time());
150 obj->set_acquire(obj->get_environment);
151 obj->set_acquire_attribute(OBJ_ICON, _Server->get_module("icons"));
154 set_attribute(DOCLPC_INSTANCETIME, time());
155 require_save(STORE_DOCLPC);
156 run_event(EVENT_EXECUTE, CALLER, obj);
160 object provide_instance()
163 array instances = aoInstances;
164 if ( arrayp(instances) )
166 //instances -= ({ 0 });
171 object e = master()->ErrorContainer();
172 master()->set_inhibit_compile_errors(e);
174 o = execute((["name":"temp", ]));
176 master()->set_inhibit_compile_errors(0);
178 FATAL("While providing instance of %s\n%s, %s\n%O",
179 get_identifier(), err[0], e->get(), err[1]);
185 * Call this script - use first instance or create one if none.
187 * @param mapping vars - normal variable mapping
188 * @return execution result
190 mixed call_script(mapping vars)
192 object script = provide_instance();
193 return script->execute(vars);
197 * register all attributes for an object
199 * @param obj - the object to register attributes
200 * @see register_class_attribute
204 install_attributes(object obj)
206 object factory = _Server->get_factory(obj->get_object_class());
207 if ( !objectp(factory) )
208 factory = _Server->get_factory(CLASS_OBJECT);
210 mapping mClassAttr = factory->get_attributes() + mRegAttributes;
211 foreach ( indices(mClassAttr), mixed key )
212 install_attribute(mClassAttr[key], obj);
217 bool install_attribute(Attribute attr, object obj)
220 mixed key = attr->get_key();
221 mixed def = attr->get_default_value();
222 if ( !zero_type(def) )
223 obj->set_attribute(key, def);
224 string|object acq = attr->get_acquire();
226 obj->set_acquire_attribute(key, obj->find_function(acq));
228 obj->set_acquire_attribute(key, acq);
231 FATAL("Error registering attribute: %O", err);
235 bool check_attribute(mixed key, mixed data)
237 Attribute a = mRegAttributes[key];
239 return a->check_attribute(data);
243 * register attributes for the class(es) this factory creates.
244 * each newly created object will have the attributes registered here.
246 * @param Attribute attr - the new attribute to register.
247 * @param void|function conversion - conversion function for all objects
249 * @see classes/Object.set_attribute
250 * @see libraries/Attributes.pmod.Attribute
253 register_attribute(Attribute attr, void|function conversion)
255 try_event(EVENT_REGISTER_ATTRIBUTE, CALLER, attr);
256 register_class_attribute(attr, conversion);
258 // register on all dependent factories too
259 array factories = values(_Server->get_classes());
260 foreach ( factories, object factory ) {
261 factory = factory->get_object();
262 if ( factory->get_object_id() == get_object_id() )
264 if ( search(Program.all_inherits(object_program(factory)),
265 object_program(this_object())) >= 0 )
266 factory->register_attribute(copy_value(attr), conversion);
268 run_event(EVENT_REGISTER_ATTRIBUTE, CALLER, attr);
273 * Register_class_attribute is called by register_attribute,
274 * this function is local and does no security checks. All instances
275 * of this class are set to the default value and acquiring settings.
277 * @param Attribute attr - the Attribute to register for this factories class
278 * @param void|function conversion - conversion function.
279 * @see register_attribute
282 void register_class_attribute(Attribute attr, void|function conversion)
284 string|int key = attr->get_key();
285 MESSAGE("register_class_attribute(%O)",key);
286 Attribute pattr = mRegAttributes[key];
287 if ( pattr == attr ) {
288 MESSAGE("re-registering class attribute with same value.");
291 foreach(aoInstances, object inst)
292 install_attribute(attr, inst);
294 mRegAttributes[key] = attr;
295 require_save(STORE_DOCLPC);
301 * get the registration information for one attribute of this class
303 * @param mixed key - the attribute to describe.
304 * @return array of registered attribute data.
306 Attribute describe_attribute(mixed key)
308 return copy_value(mRegAttributes[key]);
312 * Get the source code of the doclpc, used by master().
314 * @return the content of the document.
316 string get_source_code()
318 return get_content();
322 * Get the compiled program of this objects content.
324 * @return the pike program.
326 final program get_program()
328 program p = (program)("/DB:#"+get_object_id()+".pike");
333 * Get an Array of Error String description.
335 * @return array list of errors from last upgrade.
339 return master()->get_error("/DB:#"+get_object_id()+".pike") || ({ });
343 * Upgrade this script and all instances.
345 * @return -2 : no program passed, -1 : force needed, otherwise: number of
350 program p = get_program();
351 // MESSAGE("*** Upgrade of Program %O, %d instances\n", p, sizeof(aoInstances));
353 mixed res = master()->upgrade(p);
357 if ( objectp(get_environment()) ) {
359 catch(pp = (program)("/DB:/"+path));
361 if ( programp(pp) ) {
362 mixed err = catch(master()->upgrade(pp));
364 FATAL("Error while upgrading: %O", err);
366 catch(pp = (program) ("steam:"+path));
368 master()->upgrade(pp);
372 do_set_attribute(DOCLPC_INSTANCETIME, time());
373 if ( stringp(res) ) {
377 foreach(aoInstances, object script) {
378 if ( !objectp(script) )
380 if ( functionp(script->upgrade) )
398 void content_finished()
400 ::content_finished();
401 do_set_attribute(DOCLPC_INSTANCETIME, time());
402 mixed err = catch(upgrade());
405 FATAL("Error when updating after content_finished(): %O\n%O",
413 * Retrieve the DocLPC data for storage in the database.
415 * @return the saved data mapping.
421 if ( CALLER != _Database )
422 THROW("Invalid call to retrieve_data()", E_ACCESS);
425 "RegAttributes":map(mRegAttributes, save_attribute),
426 "Instances": aoInstances,
433 mapping save_attribute(Attribute attr)
441 * Restore the data of the LPC document.
443 * @param mixed data - the saved data.
447 restore_doclpc(mixed data)
449 if ( CALLER != _Database )
450 THROW("Invalid call to restore_data()", E_ACCESS);
452 aoInstances = data["Instances"];
453 if ( !arrayp(aoInstances) )
455 foreach(indices(data->RegAttributes), mixed key) {
456 mixed v = data->RegAttributes[key];
459 if ( intp(acq) && acq == 1 )
460 acq = REG_ACQ_ENVIRONMENT;
461 Attribute a = Attribute(key, v[1],v[0],v[6],acq,v[5],v[2],v[3]);
462 mRegAttributes[key] = a;
465 Attribute a = Attribute(v->key,v->desc,v->type,v->def,v->acquire,
466 v->control, v->event_read, v->event_write);
467 mRegAttributes[key] = a;
475 * Get the existing instances of this pike program.
477 * @return array of existing objects.
479 array get_instances()
481 array instances = ({ });
484 for ( int i = 0; i < sizeof(aoInstances); i++ ) {
485 if ( objectp(aoInstances[i]) &&
486 aoInstances[i]->status() != PSTAT_FAIL_DELETED &&
487 aoInstances[i]->status() != PSTAT_FAIL_COMPILE)
488 instances += ({ aoInstances[i] });
493 void clean_instances()
495 aoInstances-= ({ 0 });
497 foreach(aoInstances, object instance)
499 // FIXME: something manages to produce a broken object instance once in a while
500 // the broken instance appears to have a working status() but no get_class()
501 // maybe also no is_object() but that is yet untested.
502 if (!objectp(instance)
503 || instance->status() == PSTAT_FAIL_COMPILE
504 || instance->status() == PSTAT_FAIL_DELETED
505 || !instance->is_object
506 || !instance->is_object()
507 || !instance->get_object_id
508 || !instance->get_object_id())
510 aoInstances-= ({ instance });
516 object get_instance()
519 foreach ( aoInstances, object instance )
521 // FIXME: something manages to produce a broken object instance once in a while
522 // the broken instance appears to have a working status() but no get_class()
523 // maybe also no is_object() but that is yet untested.
524 if (objectp(instance)
525 && instance->status() != PSTAT_FAIL_DELETED
526 && instance->status() != PSTAT_FAIL_COMPILE
527 && instance->is_object
528 && instance->is_object()
529 && instance->get_object_id() != 0)
538 return sprintf("%s+(#%d,%s,%d,%s,%d Instances, ({ %{%O,%} }))",
539 get_identifier() || "(no identifier)",
540 get_object_id() || "(no id)",
541 master()->describe_program(object_program(this_object()))
543 get_object_class() || "(no class)",
544 do_query_attribute(DOC_MIME_TYPE) || "unknown",
549 werror("%O: %O\n", err[0], err[1]);
554 string get_class() { return "DocLpc"; }
558 // test script creation and upgrading
559 Test.test( "setting pike script content",
562 object script = provide_instance();
563 if ( !Test.test( "providing script instance",
564 objectp(script) && programp(get_program()) ) )
567 if ( !Test.test( "running script",
572 Test.add_test_function( test_more, 10, script, 0 );
575 void test_more(object script, int test, void|int nr_tries)
579 Test.add_test_function( test_more,
587 else if ( script->status() != PSTAT_DISK ) {
588 MESSAGE( "DocLpc: waiting for drop of event script (try #%d, test#%d) ... ",
591 if ( nr_tries > 5 && script->status() == PSTAT_SAVE_PENDING )
592 MESSAGE(" DocLpc, waiting to save: Queue Size = %d",
593 _Database->get_save_size());
595 Test.failed( "additional tests", "timeout while waiting for event "
596 +"script to drop, tried %d times, status %d", nr_tries,
599 Test.add_test_function( test_more,
609 if ( Test.test( "testing automatic upgrading on set_content()",
610 "#include <macros.h>\n#include <database.h>\n"+
611 && (script->status() == PSTAT_DISK
612 || script->is_upgrading()),
613 "Errors: " + get_errors()*"\n"+
614 "status="+script->status() +",upgrading="+script->is_upgrading()) )
615 Test.add_test_function( test_more, 0, script, 1 );
618 // now error handling
619 if ( !Test.test( "script automatically upgraded by set_content()",
621 MESSAGE("Testing Pike Script Error Handling ...");
622 Test.test( "content handling",
623 "#include <macros.h>;\n#include <database.h>;\n"+
624 Test.add_test_function( test_more, 0, script, 2);
627 if ( Test.test( "error handling",
628 (sizeof(get_errors()) > 0) ) )
630 if ( !Test.test( "script error keeps old instance",
634 FATAL("Failed to produce errors ?!: \n%O", get_errors());
635 FATAL("PROGAM is %O", get_program());
639 Test.add_test_function( test_more, 0, script, 3 );
642 if ( !Test.test( "upgrading script that had an error",
645 // test script and events - events after upgrade
646 "#include <events.h>;\n#include <database.h>;\n"+
647 get_object_id()+"), EVENT_ATTRIBUTES_CHANGE, PHASE_NOTIFY, "+
648 "attribute_callback); }\nvoid attribute_callback(object "+
649 "event) { if ( event->get_params()->data[\"__test\"] ) { "+
650 "werror(\"***** Test notify !\\n\"); set_attribute("+
651 "\"events\", do_query_attribute(\"events\")+1); } }\n");
652 //MESSAGE("Dropping event script ...");
653 Test.add_test_function( test_more, 0, script, 4 );
656 if ( !Test.test( "script upgraded for event handling",
660 //MESSAGE("Event Listener set to %O", res);
662 set_attribute("__test", "ok");
663 if ( !Test.test( "simple event test",
664 script->query_attribute("events") == 1 ) )
667 //MESSAGE("Testing Event Script Events ...(%d)", test);
668 set_attribute("__test", "testing");
669 if ( !Test.test( "advanced event test",
670 script->query_attribute("events") == 2 ) )
673 //MESSAGE("--- Testing upgrade --- Events received (2)");
674 Test.test( "upgrading", upgrade() >= 0 );
675 Test.add_test_function( test_more, 0, script, 6 );
678 set_attribute("__test", "test");
679 if ( script->query_attribute("events") == 3 )
680 Test.succeeded( "event after upgrade", "script status: %d",
683 Test.failed( "event after upgrade",
684 "script status: %d, event result is %O",
685 script->status(), script->query_attribute("events") );
686 //MESSAGE("* DocLpc all Tests finished successfully !");