IdTool.py 7.92 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
#                    Sebastien Robin <seb@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 Products.CMFCore.utils import UniqueObject

from AccessControl import ClassSecurityInfo
from Globals import InitializeClass, DTMLFile, PersistentMapping
Jean-Paul Smets's avatar
Jean-Paul Smets committed
33
from Products.ERP5Type.Core.Folder import Folder
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34
from Products.ERP5Type import Permissions
35
from Products.CMFCore.utils import getToolByName
Jean-Paul Smets's avatar
Jean-Paul Smets committed
36 37 38 39 40 41

from Products.ERP5 import _dtmldir

import threading

from zLOG import LOG
42
from BTrees.Length import Length
Jean-Paul Smets's avatar
Jean-Paul Smets committed
43 44

class IdTool(UniqueObject, Folder):
Vincent Pelletier's avatar
Vincent Pelletier committed
45 46
  """
    This tools handles the generation of IDs.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
47 48

    TODO - XXX: please use base tool
Vincent Pelletier's avatar
Vincent Pelletier committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
  """
  id = 'portal_ids'
  meta_type = 'ERP5 Id Tool'
  portal_type = 'Id Tool'

  # Declarative Security
  security = ClassSecurityInfo()

  #
  #   ZMI methods
  #
  manage_options = ( ( { 'label'      : 'Overview'
                       , 'action'     : 'manage_overview'
                       }
                     ,
Jean-Paul Smets's avatar
Jean-Paul Smets committed
64
                     )
Vincent Pelletier's avatar
Vincent Pelletier committed
65 66
                   + Folder.manage_options
                   )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
67

Vincent Pelletier's avatar
Vincent Pelletier committed
68 69
  security.declareProtected( Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainIdTool', _dtmldir )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
70

Vincent Pelletier's avatar
Vincent Pelletier committed
71 72 73
  # Filter content (ZMI))
  def __init__(self):
    return Folder.__init__(self, IdTool.id)
74 75 76 77 78 79 80 81 82 83 84 85 86

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getLastGeneratedId')
  def getLastGeneratedId(self,id_group=None,default=None):
    """
    Get the last id generated
    """
    if getattr(self, 'dict_ids', None) is None:
      self.dict_ids = PersistentMapping()
    last_id = None
    if id_group is not None and id_group!='None':
      last_id = self.dict_ids.get(id_group, default)
    return last_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
87
        
Jérome Perrin's avatar
Jérome Perrin committed
88
  security.declareProtected(Permissions.ModifyPortalContent,
89
                            'setLastGeneratedId')
90
  def setLastGeneratedId(self,new_id,id_group=None):
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    """
    Set a new last id. This is usefull in order to reset
    a sequence of ids.
    """
    if getattr(self, 'dict_ids', None) is None:
      self.dict_ids = PersistentMapping()
    if id_group is not None and id_group!='None':
      l = threading.Lock()
      l.acquire()
      try:
        self.dict_ids[id_group] = new_id
      finally:
        l.release()
        
  security.declareProtected(Permissions.AccessContentsInformation,
                            'generateNewId')
Vincent Pelletier's avatar
Vincent Pelletier committed
107 108
  def generateNewId(self, id_group=None, default=None, method=None):
    """
Jean-Paul Smets's avatar
Jean-Paul Smets committed
109
      Generate a new Id
Vincent Pelletier's avatar
Vincent Pelletier committed
110
    """
111 112
    
    if getattr(self, 'dict_ids', None) is None:
Vincent Pelletier's avatar
Vincent Pelletier committed
113 114 115 116 117 118 119 120 121
      self.dict_ids = PersistentMapping()

    new_id = None
    if id_group is not None and id_group!='None':
      # Getting the last id
      last_id = None
      l = threading.Lock()
      l.acquire()
      try:
122 123 124 125 126 127 128 129 130
        class Dummy:
          pass
        dummy = Dummy()
        last_id = self.dict_ids.get(id_group, dummy)
        if last_id is dummy:
          if default is None:
            new_id=0
          else:
            new_id=default
Vincent Pelletier's avatar
Vincent Pelletier committed
131 132

        else:
133 134 135 136 137
          # Now generate a new id
          if method is not None:
            new_id = method(last_id)
          else:
            new_id = last_id + 1
Vincent Pelletier's avatar
Vincent Pelletier committed
138 139 140 141 142 143 144
 
        # Store the new value
        self.dict_ids[id_group] = new_id
      finally:
        l.release()

    return new_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
145

146 147 148 149 150 151 152 153
  security.declareProtected(Permissions.AccessContentsInformation,
                            'generateNewLongId')
  def generateNewLongId(self, **kw):
    """
      Returns the ZODB transation id to be used as an identifier.
      It's a 64bits number, so it can look ugly and/or huge to users.
    """
    tid = get_transaction()._id;
Jérome Perrin's avatar
Jérome Perrin committed
154 155 156
    # It's a 64 bits number, but sometimes it returns as a negative int... so
    # make it positive again and add 2**63.
    return (tid < 0) and (2**63 - tid) or tid;
157 158

  security.declareProtected(Permissions.AccessContentsInformation,
Jérome Perrin's avatar
Jérome Perrin committed
159
                            'getDictLengthIdsItems')
160 161 162 163 164 165 166 167 168 169 170
  def getDictLengthIdsItems(self):
    """
      Return a copy of dict_length_ids.
      This is a workaround to access the persistent mapping content from ZSQL
      method to be able to insert initial tuples in the database at creation.
    """
    if getattr(self, 'dict_length_ids', None) is None:
      self.dict_length_ids = PersistentMapping()
    return self.dict_length_ids.items()

  security.declareProtected(Permissions.AccessContentsInformation,
171 172
                            'generateNewLengthIdList')
  def generateNewLengthIdList(self, id_group=None, id_count=1, default=1):
173
    """
174 175
      Generates a list of Ids.
      The ids are generated using mysql and then stored in a Length object in a
176
      persistant mapping to be persistent.
177
      We use MySQL to generate IDs, because it is atomic and we don't want
178
      to generate any conflict at zope level. The possible downfall is that
179 180 181
      some IDs might be skipped because of failed transactions.
      "Length" is because the id is stored in a python object inspired by
      BTrees.Length. It doesn't have to be a length.
182 183
    """
    if getattr(self, 'dict_length_ids', None) is None:
184 185
      # Length objects are stored in a persistent mapping: there is one
      # Length object per id_group.
186 187 188
      self.dict_length_ids = PersistentMapping()

    new_id = None
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
    if id_group in (None, 'None'):
      raise ValueError, '%s is not a valid group Id.' % (repr(id_group), )
    if not isinstance(id_group, str):
      id_group = repr(id_group)
    if not isinstance(default, int):
      default = 1
    # FIXME: A skin folder should be used to contain ZSQLMethods instead of
    # default catalog, like activity tool (anyway, it uses activity tool
    # ZSQLConnection, so hot reindexing is not helping here).
    portal_catalog = getToolByName(self, 'portal_catalog').getSQLCatalog()
    query = getattr(portal_catalog, 'z_portal_ids_generate_id')
    commit = getattr(portal_catalog, 'z_portal_ids_commit')
    if None in (query, commit):
      raise AttributeError, 'Error while generating Id: ' \
        'z_portal_ids_generate_id and/or z_portal_ids_commit could not ' \
        'be found.'
205 206 207 208
    try:
      result = query(id_group=id_group, id_count=id_count, default=default)
    finally:
      commit()
209
    new_id = result[0]['LAST_INSERT_ID()']
210 211
    if self.dict_length_ids.get(id_group) is None:
      self.dict_length_ids[id_group] = Length(new_id)
212 213 214 215 216 217 218 219 220 221 222
    self.dict_length_ids[id_group].set(new_id)
    return range(new_id - id_count, new_id)

  security.declareProtected(Permissions.AccessContentsInformation,
                            'generateNewLengthId')
  def generateNewLengthId(self, id_group=None, default=None):
    """
      Generates an Id.
      See generateNewLengthIdList documentation for details.
    """
    return self.generateNewLengthIdList(id_group=id_group, id_count=1, default=default)[0]
223

Jean-Paul Smets's avatar
Jean-Paul Smets committed
224
InitializeClass(IdTool)