Commit 9b292489 authored by Ivan Tyagov's avatar Ivan Tyagov

Merge branch 'cache'

parents 364be170 255d3227
...@@ -1128,7 +1128,8 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -1128,7 +1128,8 @@ class ObjectTemplateItem(BaseTemplateItem):
container._mapTransform(obj) container._mapTransform(obj)
elif obj.meta_type in ('ERP5 Ram Cache', elif obj.meta_type in ('ERP5 Ram Cache',
'ERP5 Distributed Ram Cache',): 'ERP5 Distributed Ram Cache',):
assert container.meta_type == 'ERP5 Cache Factory' assert container.meta_type in ('ERP5 Cache Factory',
'ERP5 Cache Bag')
container.getParentValue().updateCache() container.getParentValue().updateCache()
elif (container.meta_type == 'CMF Skins Tool') and \ elif (container.meta_type == 'CMF Skins Tool') and \
(old_obj is not None): (old_obj is not None):
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/CacheBag_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -19,7 +19,12 @@ ...@@ -19,7 +19,12 @@
<item>Action Information</item> <item>Action Information</item>
<item>Role Information</item> <item>Role Information</item>
</portal_type> </portal_type>
<portal_type id="Cache Bag">
<item>Distributed Ram Cache</item>
<item>Ram Cache</item>
</portal_type>
<portal_type id="Cache Factory"> <portal_type id="Cache Factory">
<item>Cache Bag</item>
<item>Distributed Ram Cache</item> <item>Distributed Ram Cache</item>
<item>Ram Cache</item> <item>Ram Cache</item>
</portal_type> </portal_type>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>document_icon.gif</string> </value>
</item>
<item>
<key> <string>content_meta_type</string> </key>
<value> <string>ERP5 Cache Factory</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string> CacheBag is .....</string> </value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addCacheFactory</string> </value>
</item>
<item>
<key> <string>filter_content_types</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Cache Bag</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>CacheBag</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5Form" module="Products.ERP5Form.Form"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>listbox</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_id</string>
<string>my_title</string>
<string>my_cache_duration</string>
<string>my_int_index</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>CacheBag_view</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>General</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Cache Bag</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>columns</string>
<string>count_method</string>
<string>list_method</string>
<string>portal_types</string>
<string>search_columns</string>
<string>selection_name</string>
<string>sort</string>
<string>sort_columns</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>all_editable_columns</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>columns</string> </key>
<value>
<list>
<tuple>
<string>id</string>
<string>ID</string>
</tuple>
<tuple>
<string>int_index</string>
<string>Priority</string>
</tuple>
<tuple>
<string>title</string>
<string>Title</string>
</tuple>
<tuple>
<string>portal_type</string>
<string>Type</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>count_method</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_list_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value>
<list>
<tuple>
<string>Ram Cache</string>
<string>Ram Cache</string>
</tuple>
<tuple>
<string>Distributed Ram Cache</string>
<string>Distributed Ram Cache</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>search_columns</string> </key>
<value>
<list>
<tuple>
<string>id</string>
<string>ID</string>
</tuple>
<tuple>
<string>title</string>
<string>Title</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>selection_name</string> </key>
<value> <string>search_selection1</string> </value>
</item>
<item>
<key> <string>sort</string> </key>
<value>
<list>
<tuple>
<string>int_index</string>
<string>Priority</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>sort_columns</string> </key>
<value>
<list>
<tuple>
<string>id</string>
<string>ID</string>
</tuple>
<tuple>
<string>int_index</string>
<string>Priority</string>
</tuple>
<tuple>
<string>title</string>
<string>Title</string>
</tuple>
<tuple>
<string>portal_type</string>
<string>Type</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Cache Plugins</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>objectValues</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="IntegerField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_cache_duration</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>integer_out_of_range</string> </key>
<value> <string>The integer you entered was out of range.</string> </value>
</item>
<item>
<key> <string>not_integer</string> </key>
<value> <string>You did not enter an integer.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Cache Duration</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StringField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_id</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>Too much input was given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Identification</string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="IntegerField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_int_index</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>integer_out_of_range</string> </key>
<value> <string>The integer you entered was out of range.</string> </value>
</item>
<item>
<key> <string>not_integer</string> </key>
<value> <string>You did not enter an integer.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Lower value means higher priority</string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Priority</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -129,6 +129,10 @@ ...@@ -129,6 +129,10 @@
<string>Cache Factory</string> <string>Cache Factory</string>
<string>Cache Factory</string> <string>Cache Factory</string>
</tuple> </tuple>
<tuple>
<string>Cache Bag</string>
<string>Cache Bag</string>
</tuple>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -35,6 +35,7 @@ Business Template | view ...@@ -35,6 +35,7 @@ Business Template | view
Business Template | view_catalog Business Template | view_catalog
Business Template | view_detail Business Template | view_detail
Business Template | view_portal_types Business Template | view_portal_types
Cache Bag | view
Cache Factory | statistics Cache Factory | statistics
Cache Factory | view Cache Factory | view
Cache Tool | statistics Cache Tool | statistics
......
...@@ -6,6 +6,9 @@ Base Domain | Domain ...@@ -6,6 +6,9 @@ Base Domain | Domain
Base Domain | Domain Generator Base Domain | Domain Generator
Base Type | Action Information Base Type | Action Information
Base Type | Role Information Base Type | Role Information
Cache Bag | Distributed Ram Cache
Cache Bag | Ram Cache
Cache Factory | Cache Bag
Cache Factory | Distributed Ram Cache Cache Factory | Distributed Ram Cache
Cache Factory | Ram Cache Cache Factory | Ram Cache
Cache Tool | Cache Factory Cache Tool | Cache Factory
......
...@@ -15,6 +15,7 @@ Base Category ...@@ -15,6 +15,7 @@ Base Category
Base Domain Base Domain
Base Type Base Type
Business Template Business Template
Cache Bag
Cache Factory Cache Factory
Cache Tool Cache Tool
Category Category
......
...@@ -74,20 +74,15 @@ class CachedConvertableMixin: ...@@ -74,20 +74,15 @@ class CachedConvertableMixin:
def _getCacheFactory(self): def _getCacheFactory(self):
""" """
""" """
# XXX: is this really needed ?
if self.getOriginalDocument() is None: if self.getOriginalDocument() is None:
return None return None
portal = self.getPortalObject() portal = self.getPortalObject()
cache_tool = portal.portal_caches cache_factory_name = portal.portal_preferences.getPreferredConversionCacheFactory('document_cache_factory')
preference_tool = portal.portal_preferences if cache_factory_name is not None:
cache_factory_name = preference_tool.getPreferredConversionCacheFactory('document_cache_factory') return getattr(portal.portal_caches, cache_factory_name, None)
cache_factory = cache_tool.getRamCacheRoot().get(cache_factory_name)
#XXX This conditional statement should be remove as soon as
#Broadcasting will be enable among all zeo clients.
#Interaction which update portal_caches should interact with all nodes.
if cache_factory is None and getattr(cache_tool, cache_factory_name, None) is not None:
#ram_cache_root is not up to date for current node
cache_tool.updateCache()
return cache_tool.getRamCacheRoot().get(cache_factory_name)
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'generateCacheId') 'generateCacheId')
...@@ -167,23 +162,18 @@ class CachedConvertableMixin: ...@@ -167,23 +162,18 @@ class CachedConvertableMixin:
self.temp_conversion_data = {} self.temp_conversion_data = {}
self.temp_conversion_data[cache_id] = stored_data_dict self.temp_conversion_data[cache_id] = stored_data_dict
return return
cache_duration = cache_factory.cache_duration
# The purpose of this transaction cache is to help calls # The purpose of this transaction cache is to help calls
# to the same cache value in the same transaction. # to the same cache value in the same transaction.
tv = getTransactionalVariable() tv = getTransactionalVariable()
tv[cache_id] = stored_data_dict tv[cache_id] = stored_data_dict
for cache_plugin in cache_factory.getCachePluginList(): cache_factory.set(cache_id, stored_data_dict)
cache_plugin.set(cache_id, DEFAULT_CACHE_SCOPE,
stored_data_dict, cache_duration=cache_duration)
security.declareProtected(Permissions.View, '_getConversionDataDict') security.declareProtected(Permissions.View, '_getConversionDataDict')
def _getConversionDataDict(self, **kw): def _getConversionDataDict(self, **kw):
""" """
""" """
cache_id = self._getCacheKey(**kw) cache_id = self._getCacheKey(**kw)
cache_factory = self._getCacheFactory()
if cache_factory is None:
return getattr(aq_base(self), 'temp_conversion_data', {})[cache_id]
# The purpose of this cache is to help calls to the same cache value # The purpose of this cache is to help calls to the same cache value
# in the same transaction. # in the same transaction.
tv = getTransactionalVariable() tv = getTransactionalVariable()
...@@ -191,10 +181,16 @@ class CachedConvertableMixin: ...@@ -191,10 +181,16 @@ class CachedConvertableMixin:
return tv[cache_id] return tv[cache_id]
except KeyError: except KeyError:
pass pass
for cache_plugin in cache_factory.getCachePluginList():
cache_entry = cache_plugin.get(cache_id, DEFAULT_CACHE_SCOPE) # get preferred cache factory or cache bag
if cache_entry is not None: cache_factory = self._getCacheFactory()
data_dict = cache_entry.getValue()
# volatile case
if cache_factory is None:
return getattr(aq_base(self), 'temp_conversion_data', {})[cache_id]
else:
data_dict = cache_factory.get(cache_id, None)
if data_dict: if data_dict:
if isinstance(data_dict, tuple): if isinstance(data_dict, tuple):
# Backward compatibility: if cached value is a tuple # Backward compatibility: if cached value is a tuple
...@@ -211,6 +207,7 @@ class CachedConvertableMixin: ...@@ -211,6 +207,7 @@ class CachedConvertableMixin:
# querying real cache during same transaction # querying real cache during same transaction
tv[cache_id] = data_dict tv[cache_id] = data_dict
return data_dict return data_dict
raise KeyError, 'Conversion cache key does not exists for %r' % cache_id raise KeyError, 'Conversion cache key does not exists for %r' % cache_id
security.declareProtected(Permissions.View, 'getConversion') security.declareProtected(Permissions.View, 'getConversion')
......
...@@ -133,9 +133,14 @@ class TestDocumentMixin(ERP5TypeTestCase): ...@@ -133,9 +133,14 @@ class TestDocumentMixin(ERP5TypeTestCase):
preference_list = self.portal.portal_preferences.contentValues( preference_list = self.portal.portal_preferences.contentValues(
portal_type=portal_type) portal_type=portal_type)
if not preference_list: if not preference_list:
# create a Cache Factory for tests
cache_factory = self.portal.portal_caches.newContent(portal_type = 'Cache Factory')
cache_factory.cache_duration = 36000
cache_plugin = cache_factory.newContent(portal_type='Ram Cache')
cache_plugin.cache_expire_check_interval = 54000
preference = self.portal.portal_preferences.newContent(title="Default System Preference", preference = self.portal.portal_preferences.newContent(title="Default System Preference",
# use local RAM based cache as some tests need it # use local RAM based cache as some tests need it
preferred_conversion_cache_factory = 'erp5_content_long', preferred_conversion_cache_factory = cache_factory.getId(),
portal_type=portal_type) portal_type=portal_type)
else: else:
preference = preference_list[0] preference = preference_list[0]
......
...@@ -44,11 +44,14 @@ class TestDocumentWithFlare(TestDocument): ...@@ -44,11 +44,14 @@ class TestDocumentWithFlare(TestDocument):
def setSystemPreference(self): def setSystemPreference(self):
system_preference = TestDocument.setSystemPreference(self) system_preference = TestDocument.setSystemPreference(self)
memcached = _getPersistentMemcachedServerDict() memcached = _getPersistentMemcachedServerDict()
system_preference.setPreferredConversionCacheFactory('dms_cache_factory') # create a Cache Factory for tests
cache_factory = self.portal.portal_caches.newContent(portal_type = 'Cache Factory')
cache_factory.cache_duration = 15768000
cache_plugin = cache_factory.newContent(portal_type='Distributed Ram Cache')
system_preference.setPreferredConversionCacheFactory(cache_factory.getId())
persistent_memcached_plugin = self.portal.portal_memcached.persistent_memcached_plugin persistent_memcached_plugin = self.portal.portal_memcached.persistent_memcached_plugin
persistent_memcached_plugin.setUrlString('%s:%s' %(memcached['hostname'], memcached['port'])) persistent_memcached_plugin.setUrlString('%s:%s' %(memcached['hostname'], memcached['port']))
self.portal.portal_caches.dms_cache_factory.persistent_cache_plugin.setSpecialiseValue(persistent_memcached_plugin) cache_plugin.setSpecialiseValue(persistent_memcached_plugin)
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
...@@ -68,7 +68,6 @@ class TestDocumentWithPreConversion(TestDocument): ...@@ -68,7 +68,6 @@ class TestDocumentWithPreConversion(TestDocument):
""" """
Test pre converion only happens on proper documents. Test pre converion only happens on proper documents.
""" """
print "da"
image = self.portal.image_module.newContent(portal_type='Image', image = self.portal.image_module.newContent(portal_type='Image',
reference='Embedded-XXX', reference='Embedded-XXX',
version='001', version='001',
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
import unittest import unittest
from testIngestion import TestIngestion from testIngestion import TestIngestion
from Products.ERP5Type.tests.ERP5TypeTestCase import _getPersistentMemcachedServerDict
class TestIngestionWithFlare(TestIngestion): class TestIngestionWithFlare(TestIngestion):
""" """
...@@ -47,7 +48,15 @@ class TestIngestionWithFlare(TestIngestion): ...@@ -47,7 +48,15 @@ class TestIngestionWithFlare(TestIngestion):
def setSystemPreference(self): def setSystemPreference(self):
default_pref = self.portal.portal_preferences.default_site_preference default_pref = self.portal.portal_preferences.default_site_preference
default_pref.setPreferredConversionCacheFactory('dms_cache_factory') memcached = _getPersistentMemcachedServerDict()
# create a Cache Factory for tests
cache_factory = self.portal.portal_caches.newContent(portal_type = 'Cache Factory')
cache_factory.cache_duration = 15768000
cache_plugin = cache_factory.newContent(portal_type='Distributed Ram Cache')
default_pref.setPreferredConversionCacheFactory(cache_factory.getId())
persistent_memcached_plugin = self.portal.portal_memcached.persistent_memcached_plugin
persistent_memcached_plugin.setUrlString('%s:%s' %(memcached['hostname'], memcached['port']))
cache_plugin.setSpecialiseValue(persistent_memcached_plugin)
TestIngestion.setSystemPreference(self) TestIngestion.setSystemPreference(self)
......
...@@ -265,9 +265,7 @@ class TestDocumentConversionCache(TestDocumentMixin): ...@@ -265,9 +265,7 @@ class TestDocumentConversionCache(TestDocumentMixin):
document.convert(**kw) document.convert(**kw)
cache_id = document._getCacheKey(**kw) cache_id = document._getCacheKey(**kw)
cache_factory = document._getCacheFactory() cache_factory = document._getCacheFactory()
for cache_plugin in cache_factory.getCachePluginList(): data_dict = cache_factory.get(cache_id)
cache_entry = cache_plugin.get(cache_id, DEFAULT_CACHE_SCOPE)
data_dict = cache_entry.getValue()
#get data from cache #get data from cache
self.assertTrue(data_dict['content_md5']) self.assertTrue(data_dict['content_md5'])
self.assertTrue(data_dict['conversion_md5']) self.assertTrue(data_dict['conversion_md5'])
...@@ -277,7 +275,7 @@ class TestDocumentConversionCache(TestDocumentMixin): ...@@ -277,7 +275,7 @@ class TestDocumentConversionCache(TestDocumentMixin):
self.assertTrue(data_dict['size']) self.assertTrue(data_dict['size'])
#Change md5 manualy #Change md5 manualy
data_dict['content_md5'] = 'Anything which is not md5' data_dict['content_md5'] = 'Anything which is not md5'
cache_plugin.set(cache_id, DEFAULT_CACHE_SCOPE, data_dict, 100, 0) cache_factory.set(cache_id, data_dict)
self.commit() self.commit()
self.assertRaises(KeyError, document.getConversion, format='html') self.assertRaises(KeyError, document.getConversion, format='html')
......
...@@ -200,6 +200,16 @@ class CacheFactory: ...@@ -200,6 +200,16 @@ class CacheFactory:
return cp return cp
return None return None
def getCachePluginById(self, id, default=None):
""" get cache plugin by its id """
for cp in self.cache_plugins:
if id == cp.id:
return cp
if default is not None:
return default
raise KeyError("No such plugin exists %s" % id)
def clearCache(self): def clearCache(self):
""" clear cache for this cache factory """ """ clear cache for this cache factory """
for cp in self.cache_plugins: for cp in self.cache_plugins:
......
...@@ -94,10 +94,11 @@ class BaseCache(object): ...@@ -94,10 +94,11 @@ class BaseCache(object):
## Time interval (s) to check for expired objects ## Time interval (s) to check for expired objects
cache_expire_check_interval = 60 cache_expire_check_interval = 60
def __init__(self, params={}): def __init__(self, id, params={}):
self._next_cache_expire_check_at = time() self._next_cache_expire_check_at = time()
self._cache_hit_count = 0 self._cache_hit_count = 0
self._cache_miss_count = 0 self._cache_miss_count = 0
self.id = id
def markCacheHit(self, delta=1): def markCacheHit(self, delta=1):
""" Mark a read operation from cache """ """ Mark a read operation from cache """
......
...@@ -55,14 +55,14 @@ class DistributedRamCache(BaseCache): ...@@ -55,14 +55,14 @@ class DistributedRamCache(BaseCache):
interfaces.ICachePlugin interfaces.ICachePlugin
) )
def __init__(self, params={}): def __init__(self, uid, params={}):
self._servers = params.get('server', '') self._servers = params.get('server', '')
self._expiration_time = params.get('expiration_time', 0) self._expiration_time = params.get('expiration_time', 0)
self._server_max_key_length = params.get('server_max_key_length', 250) self._server_max_key_length = params.get('server_max_key_length', 250)
self._server_max_value_length = params.get('server_max_value_length', 1024*1024) self._server_max_value_length = params.get('server_max_value_length', 1024*1024)
self._debug_level = params.get('debug_level', 0) self._debug_level = params.get('debug_level', 0)
self._key_prefix = params.get('key_prefix', '') self._key_prefix = params.get('key_prefix', '')
BaseCache.__init__(self) BaseCache.__init__(self, uid)
def initCacheStorage(self): def initCacheStorage(self):
""" Init cache storage """ """ Init cache storage """
......
...@@ -59,9 +59,9 @@ class RamCache(BaseCache): ...@@ -59,9 +59,9 @@ class RamCache(BaseCache):
cache_expire_check_interval = 300 cache_expire_check_interval = 300
def __init__(self, params={}): def __init__(self, uid, params={}):
self._cache_dict = {} self._cache_dict = {}
BaseCache.__init__(self) BaseCache.__init__(self, uid)
def initCacheStorage(self): def initCacheStorage(self):
""" Init cache storage """ """ Init cache storage """
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Ivan Tyagov <ivan@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
from Products.ERP5Type.Core.CacheFactory import CacheFactory
class CacheBag(CacheFactory):
"""
CacheBag is a special type of a CacheFactory that allows multi level caching
in different backends describe by CachePlugin.
CacheBag 1
- Cache Plugin 1 (priority 0)
- Cache Plugin 2 (priority 1)
"""
meta_type = 'ERP5 Cache Bag'
portal_type = 'Cache Bag'
security = ClassSecurityInfo()
security.declareProtected(Permissions.AccessContentsInformation, 'get')
def get(self, cache_id, default=None):
"""
Get value or return default.
"""
ram_cache_factory_plugin_list = self.getRamCacheFactoryPluginList()
for cache_plugin in ram_cache_factory_plugin_list:
data_dict = cache_plugin.get(cache_id, DEFAULT_CACHE_SCOPE, default)
if data_dict is not None:
value = data_dict.getValue()
if ram_cache_factory_plugin_list.index(cache_plugin) > 0:
# update first plugin as it's the one to be used
# XXX: JPS we can have different update policy here based on a project requirements.
# c0 c1 c2....cN where c0 is filled from cN
# c1.... cN-1 untouched then rotate i -> i+1
# this way you can create "groups of caches" per date and trash old stuff
# instead of using 2x more disk space, you can use 1/N more disk space
cache_duration = self.getRamCacheFactory().cache_duration
ram_cache_factory_plugin_list[0].set(cache_id, DEFAULT_CACHE_SCOPE, value, cache_duration)
return value
return default
security.declareProtected(Permissions.AccessContentsInformation, 'set')
def set(self, cache_id, value):
"""
Set value.
"""
cache_duration = self.getRamCacheFactory().cache_duration
ram_cache_factory_plugin_list = self.getRamCacheFactoryPluginList()
# set only in first plugin in sequence
ram_cache_factory_plugin_list[0].set(cache_id, DEFAULT_CACHE_SCOPE, value, cache_duration)
...@@ -58,12 +58,48 @@ class CacheFactory(XMLObject): ...@@ -58,12 +58,48 @@ class CacheFactory(XMLObject):
, PropertySheet.SimpleItem , PropertySheet.SimpleItem
, PropertySheet.Folder , PropertySheet.Folder
, PropertySheet.CacheFactory , PropertySheet.CacheFactory
, PropertySheet.SortIndex
) )
def getCacheId(self):
"""
Get a common Cache Factory / Cache Bag ID in this
case relative to portal_caches.
It's required to use relative url (i.e. mainly ID) due
to CachingMethod legacy.
"""
relative_url = self.getRelativeUrl()
assert relative_url[:14] == 'portal_caches/'
return relative_url[14:]
security.declareProtected(Permissions.AccessContentsInformation, 'get')
def get(self, cache_id, default=None):
"""
Get value or return default from all contained Cache Bag
or Cache Plugin.
"""
cache_plugin_list = self.getCachePluginList(list(self.allowed_types) + ['ERP5 Cache Bag'])
for cache_plugin in cache_plugin_list:
value = cache_plugin.get(cache_id, default)
if value is not None:
return value
return default
security.declareProtected(Permissions.AccessContentsInformation, 'set')
def set(self, cache_id, value):
"""
Set value to all contained cache plugin or cache bag.
"""
cache_plugin_list = self.getCachePluginList(list(self.allowed_types) + ['ERP5 Cache Bag'])
for cache_plugin in cache_plugin_list:
cache_plugin.set(cache_id, value)
def getCachePluginList(self): def getCachePluginList(self, allowed_type_list=None):
""" get ordered list of installed cache plugins in ZODB """ """ get ordered list of installed cache plugins in ZODB """
cache_plugins = self.objectValues(self.allowed_types) if allowed_type_list is None:
# fall back to default ones
allowed_type_list = self.allowed_types
cache_plugins = self.objectValues(allowed_type_list)
cache_plugins = map(None, cache_plugins) cache_plugins = map(None, cache_plugins)
cache_plugins.sort(key=lambda x: x.getIntIndex(0)) cache_plugins.sort(key=lambda x: x.getIntIndex(0))
return cache_plugins return cache_plugins
...@@ -71,8 +107,16 @@ class CacheFactory(XMLObject): ...@@ -71,8 +107,16 @@ class CacheFactory(XMLObject):
security.declareProtected(Permissions.AccessContentsInformation, 'getRamCacheFactory') security.declareProtected(Permissions.AccessContentsInformation, 'getRamCacheFactory')
def getRamCacheFactory(self): def getRamCacheFactory(self):
""" Return RAM based cache factory """ """ Return RAM based cache factory """
erp5_site_id = self.getPortalObject().getId() cache_factory_name = self.getCacheId()
return CachingMethod.factories[erp5_site_id][self.cache_scope] cache_tool = self.portal_caches
cache_factory = CachingMethod.factories.get(cache_factory_name)
#XXX This conditional statement should be remove as soon as
#Broadcasting will be enable among all zeo clients.
#Interaction which update portal_caches should interact with all nodes.
if cache_factory is None and getattr(cache_tool, cache_factory_name, None) is not None:
#ram_cache_root is not up to date for current node
cache_tool.updateCache()
return CachingMethod.factories[cache_factory_name]
security.declareProtected(Permissions.AccessContentsInformation, 'getRamCacheFactoryPluginList') security.declareProtected(Permissions.AccessContentsInformation, 'getRamCacheFactoryPluginList')
def getRamCacheFactoryPluginList(self): def getRamCacheFactoryPluginList(self):
...@@ -81,5 +125,5 @@ class CacheFactory(XMLObject): ...@@ -81,5 +125,5 @@ class CacheFactory(XMLObject):
def clearCache(self): def clearCache(self):
""" clear cache for this cache factory """ """ clear cache for this cache factory """
for cp in self.getRamCacheFactory().getCachePluginList(): for cp in self.getRamCacheFactoryPluginList():
cp.clearCache() cp.clearCache()
...@@ -31,8 +31,9 @@ from AccessControl import ClassSecurityInfo ...@@ -31,8 +31,9 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type import PropertySheet from Products.ERP5Type import PropertySheet
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.mixin.cache_provider import CacheProviderMixIn
class DistributedRamCache(XMLObject): class DistributedRamCache(CacheProviderMixIn, XMLObject):
""" """
DistributedRamCache is a Zope (persistent) representation of DistributedRamCache is a Zope (persistent) representation of
the Distributed RAM Cache real cache plugin object. the Distributed RAM Cache real cache plugin object.
......
...@@ -32,8 +32,9 @@ from AccessControl import ClassSecurityInfo ...@@ -32,8 +32,9 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type import PropertySheet from Products.ERP5Type import PropertySheet
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.mixin.cache_provider import CacheProviderMixIn
class RamCache(XMLObject): class RamCache(CacheProviderMixIn, XMLObject):
""" """
RamCache is a Zope (persistent) representation of RamCache is a Zope (persistent) representation of
the RAM based real cache plugin object. the RAM based real cache plugin object.
......
...@@ -66,21 +66,15 @@ class CacheTool(BaseTool): ...@@ -66,21 +66,15 @@ class CacheTool(BaseTool):
security.declareProtected(Permissions.AccessContentsInformation, 'getCacheFactoryList') security.declareProtected(Permissions.AccessContentsInformation, 'getCacheFactoryList')
def getCacheFactoryList(self): def getCacheFactoryList(self):
""" Return available cache factories """ """ Return available cache factories """
rd = {}
for cf in self.objectValues('ERP5 Cache Factory'): def getRamCachePlugin(cp):
cache_scope = cf.getId()
rd[cache_scope] = {}
rd[cache_scope]['cache_plugins'] = []
rd[cache_scope]['cache_params'] = {}
for cp in cf.getCachePluginList():
cache_obj = None
cp_meta_type = cp.meta_type cp_meta_type = cp.meta_type
id = cp.getCacheId()
if cp_meta_type == 'ERP5 Ram Cache': if cp_meta_type == 'ERP5 Ram Cache':
cache_obj = RamCache() return RamCache(id)
elif cp_meta_type == 'ERP5 Distributed Ram Cache': if cp_meta_type == 'ERP5 Distributed Ram Cache':
## even thougn we have such plugin in ZODB that doens't mean ## even thougn we have such plugin in ZODB that doens't mean
## we have corresponding memcache module installed ## we have corresponding memcache module installed
cache_obj = None
if getattr(cp, 'getSpecialiseValue', None) is not None: if getattr(cp, 'getSpecialiseValue', None) is not None:
memcached_plugin = cp.getSpecialiseValue() memcached_plugin = cp.getSpecialiseValue()
if memcached_plugin is not None: if memcached_plugin is not None:
...@@ -90,14 +84,38 @@ class CacheTool(BaseTool): ...@@ -90,14 +84,38 @@ class CacheTool(BaseTool):
'expiration_time': cf.getCacheDuration(), 'expiration_time': cf.getCacheDuration(),
'server_max_key_length': memcached_plugin.getServerMaxKeyLength(), 'server_max_key_length': memcached_plugin.getServerMaxKeyLength(),
'server_max_value_length': memcached_plugin.getServerMaxValueLength(), 'server_max_value_length': memcached_plugin.getServerMaxValueLength(),
'key_prefix': getattr(self, 'erp5_site_global_id', '') 'key_prefix': getattr(self, 'erp5_site_global_id', '')}
} return DistributedRamCache(id, init_dict)
cache_obj = DistributedRamCache(init_dict)
rd = {}
for cf in self.objectValues('ERP5 Cache Factory'):
cache_scope = cf.getCacheId()
rd[cache_scope] = {}
rd[cache_scope]['cache_plugins'] = []
rd[cache_scope]['cache_params'] = {}
for cp in cf.getCachePluginList():
cache_obj = getRamCachePlugin(cp)
if cache_obj is not None:
## set cache expire check interval
cache_obj.cache_expire_check_interval = cp.getCacheExpireCheckInterval()
rd[cache_scope]['cache_plugins'].append(cache_obj)
rd[cache_scope]['cache_params']['cache_duration'] = cf.getCacheDuration()
# support for cache bags which are like Cache Factory
# i.e. provide Cache Plugins
for cache_bag in cf.objectValues('ERP5 Cache Bag'):
cache_scope = cache_bag.getCacheId()
rd[cache_scope] = {}
rd[cache_scope]['cache_plugins'] = []
rd[cache_scope]['cache_params'] = {}
for cp in cache_bag.getCachePluginList():
cache_obj = getRamCachePlugin(cp)
if cache_obj is not None: if cache_obj is not None:
## set cache expire check interval ## set cache expire check interval
cache_obj.cache_expire_check_interval = cp.getCacheExpireCheckInterval() cache_obj.cache_expire_check_interval = cp.getCacheExpireCheckInterval()
rd[cache_scope]['cache_plugins'].append(cache_obj) rd[cache_scope]['cache_plugins'].append(cache_obj)
rd[cache_scope]['cache_params']['cache_duration'] = cf.getCacheDuration() rd[cache_scope]['cache_params']['cache_duration'] = cf.getCacheDuration()
return rd return rd
## ##
......
...@@ -36,7 +36,7 @@ class ICachePlugin(Interface): ...@@ -36,7 +36,7 @@ class ICachePlugin(Interface):
"""CachePlugin Interface Specification """CachePlugin Interface Specification
""" """
def __init__(params={}): def __init__(uid, params={}):
"""Initialise default values """Initialise default values
""" """
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
# Ivan Tyagov <ivan@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
class CacheProviderMixIn:
"""
Generic Cache Plugin set / get API implementation.
"""
security = ClassSecurityInfo()
def _getRamCachePlugin(self):
"""
Get RAM based cache plugin for this ZODB cache plugin.
"""
return self.getParentValue().getRamCacheFactory().getCachePluginById(self.getCacheId())
security.declareProtected(Permissions.AccessContentsInformation, 'get')
def get(self, cache_id, default=None):
"""
Get value from cache plugin.
"""
cache_plugin = self._getRamCachePlugin()
value = cache_plugin.get(cache_id, DEFAULT_CACHE_SCOPE, default)
if value is not None:
value = value.getValue()
return value
security.declareProtected(Permissions.AccessContentsInformation, 'set')
def set(self, cache_id, value):
"""
Set value to cache plugin.
"""
cache_duration = self.getParentValue().getRamCacheFactory().cache_duration
cache_plugin = self._getRamCachePlugin()
cache_plugin.set(cache_id, DEFAULT_CACHE_SCOPE, value, cache_duration)
def getCacheId(self):
"""
Get a common Cache Factory / Cache Bag ID in this
case relative to portal_caches.
It's required to use relative url (i.e. mainly ID) due
to CachingMethod legacy.
"""
relative_url = self.getRelativeUrl()
assert relative_url[:14] == 'portal_caches/'
return relative_url[14:]
...@@ -50,8 +50,9 @@ class TestRamCache(ERP5TypeTestCase): ...@@ -50,8 +50,9 @@ class TestRamCache(ERP5TypeTestCase):
return "Cache" return "Cache"
def afterSetUp(self): def afterSetUp(self):
self.cache_plugins = (RamCache(), self.cache_plugins = (RamCache('ram_cache'),
DistributedRamCache({'server': '127.0.0.1:11211', DistributedRamCache('distributed_ram_cache',
{'server': '127.0.0.1:11211',
'debug_level': 7, 'debug_level': 7,
'server_max_key_length': 250, 'server_max_key_length': 250,
'server_max_value_length': 1048576,}), 'server_max_value_length': 1048576,}),
......
...@@ -35,7 +35,7 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase ...@@ -35,7 +35,7 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.ERP5TypeTestCase import _getPersistentMemcachedServerDict, _getVolatileMemcachedServerDict from Products.ERP5Type.tests.ERP5TypeTestCase import _getPersistentMemcachedServerDict, _getVolatileMemcachedServerDict
from Products.ERP5Type.CachePlugins.DummyCache import DummyCache from Products.ERP5Type.CachePlugins.DummyCache import DummyCache
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from Products.ERP5Type.Cache import CachingMethod from Products.ERP5Type.Cache import CachingMethod, DEFAULT_CACHE_SCOPE
from zLOG import LOG from zLOG import LOG
class TestingCache(DummyCache): class TestingCache(DummyCache):
...@@ -483,6 +483,83 @@ return 'a' * 1024 * 1024 * 25 ...@@ -483,6 +483,83 @@ return 'a' * 1024 * 1024 * 25
calculation_time = self._callCache(my_cache, real_calculation=True) calculation_time = self._callCache(my_cache, real_calculation=True)
print "\n\tCalculation time (3rd call)", calculation_time print "\n\tCalculation time (3rd call)", calculation_time
def test_06_CheckCacheBag(self):
"""
Check Cache Bag
"""
portal_caches = self.portal.portal_caches
cache_factory = portal_caches.newContent(portal_type="Cache Factory",
cache_duration=3600)
cache_bag = cache_factory.newContent(portal_type="Cache Bag",
cache_duration=3600)
cache_plugin1 = cache_bag.newContent(portal_type="Ram Cache")
cache_plugin1.setIntIndex(0)
cache_plugin2 = cache_bag.newContent(portal_type="Ram Cache")
cache_plugin2.setIntIndex(1)
self.tic()
portal_caches.updateCache()
# test proper init
ram_cache_factory_plugin_list = cache_bag.getRamCacheFactoryPluginList()
self.assertEqual(2, len(ram_cache_factory_plugin_list))
# test get / set API
cache_bag.set('x', 'value_fox_x')
self.assertEqual('value_fox_x', cache_bag.get('x'))
# test that only first cache plugin is used to set
self.assertEqual('value_fox_x',
ram_cache_factory_plugin_list[0].get('x',DEFAULT_CACHE_SCOPE).getValue())
self.assertRaises(KeyError, ram_cache_factory_plugin_list[1].get, 'x', DEFAULT_CACHE_SCOPE)
# check hot copy happens from second in order plugin to first
ram_cache_factory_plugin_list[1].set('y', DEFAULT_CACHE_SCOPE, 'value_for_y', cache_bag.cache_duration)
self.assertEqual('value_for_y', cache_bag.get('y'))
self.assertEqual('value_for_y', ram_cache_factory_plugin_list[0].get('y',DEFAULT_CACHE_SCOPE).getValue())
def test_07_CheckCacheFactory(self):
"""
Check Cache Factory set and get API.
"""
portal_caches = self.portal.portal_caches
cache_factory = portal_caches.newContent(portal_type="Cache Factory",
cache_duration=3600)
cache_plugin1 = cache_factory.newContent(portal_type="Ram Cache")
cache_plugin1.setIntIndex(0)
cache_bag1 = cache_factory.newContent(portal_type="Cache Bag",
cache_duration=3600)
cache_bag1.setIntIndex(1)
ram_cache1 = cache_bag1.newContent(portal_type="Ram Cache")
ram_cache2 = cache_bag1.newContent(portal_type="Ram Cache")
self.tic()
portal_caches.updateCache()
# test get / set API
cache_factory.set('x', 'value_for_x')
self.assertEqual('value_for_x', cache_factory.get('x'))
# test that all cache plugin have this set
self.assertEqual('value_for_x', cache_plugin1.get('x'))
self.assertEqual('value_for_x', cache_bag1.get('x'))
# test set on individual cache plugin as this cache plugin has highest priority
# it will affect what root Cache Factory returns
cache_plugin1.set('x', 'new_value_for_x')
self.assertEqual('new_value_for_x', cache_plugin1.get('x'))
self.assertEqual('new_value_for_x', cache_factory.get('x'))
# others cache plugins will remain with old value until ...
self.assertEqual('value_for_x', cache_bag1.get('x'))
# .. root Cache Factory set will update all
cache_factory.set('x', 'new_value_for_x')
self.assertEqual(cache_factory.get('x'), cache_plugin1.get('x'))
self.assertEqual(cache_plugin1.get('x'), cache_bag1.get('x'))
self.assertEqual('new_value_for_x', cache_factory.get('x'))
def test_99_CachePluginInterface(self): def test_99_CachePluginInterface(self):
"""Test Class against Interface """Test Class against Interface
""" """
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment