#
#--
# Copyright (c) 2006-2008, Nicolas Modryzk and John Mettraux, OpenWFE.org
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# . Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# . Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# . Neither the name of the "OpenWFE" nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#++
#
#
# "made in Japan"
#
# Nicolas Modrzyk at openwfe.org
# John Mettraux at openwfe.org
#
require 'fileutils'
require 'rufus/lru'
require 'openwfe/service'
require 'openwfe/flowexpressionid'
require 'openwfe/rudefinitions'
module OpenWFE
#
# This module contains the observe_expool method which binds the
# storage to the expression pool.
# It also features a to_s method for the expression storages including
# it.
#
module ExpressionStorageBase
def observe_expool
get_expression_pool.add_observer(:update) do |channel, fei, fe|
ldebug { ":update for #{fei}" }
self[fei] = fe
end
get_expression_pool.add_observer(:remove) do |channel, fei|
ldebug { ":remove for #{fei}" }
self.delete fei
end
end
#
# a human readable representation of the content of the expression
# storage.
#
# Warning : this will display the content of the real storage,
# (especially when called against a cache).
#
def to_s
s = "\n\n==== #{self.class} ===="
find_expressions.each do |fexp|
s << "\n"
if fexp.kind_of?(RawExpression)
s << "*raw"
else
s << " "
end
s << fexp.fei.to_s
end
s << "\n==== . ====\n"
s
end
#
# This method is used by the various implementations of
# find_expressions()
#
def does_match? (options, fexp_or_fei)
wfid = options[:wfid]
wfid_prefix = options[:wfid_prefix]
parent_wfid = options[:parent_wfid]
wfname = options[:wfname]
wfrevision = options[:wfrevision]
ic = options[:include_classes]
ec = options[:exclude_classes]
ic = Array(ic) if ic
ec = Array(ec) if ec
cs = options[:consider_subprocesses]
ap = options[:applied]
fexp, fei = if fexp_or_fei.is_a?(FlowExpressionId)
[ nil, fexp_or_fei ]
else
[ fexp_or_fei, fexp_or_fei.fei ]
end
#
# let's make it verbose...
if fexp
return false if (ap == true and not fexp.apply_time)
return false if (ap == false and fexp.apply_time)
return false unless class_accepted?(fexp, ic, ec)
end
return false \
if wfname and fei.wfname != wfname
return false \
if wfrevision and fei.wfrevision != wfrevision
return false \
if cs and fei.sub_instance_id != ""
return false \
if wfid and fei.wfid != wfid
return false \
if wfid_prefix and not fei.wfid.match("^#{wfid_prefix}")
return false \
if parent_wfid and not fei.parent_wfid == parent_wfid
true
end
#
# Returns true if the given expression is in the list of included
# classes or false if it's in the list of excluded classes...
#
# include_classes has precedence of exclude_classes.
#
def class_accepted? (fexp, include_classes, exclude_classes)
return false if include_classes and (not include_classes.find do |klazz|
fexp.is_a?(klazz)
end)
return false if exclude_classes and exclude_classes.find do |klazz|
fexp.is_a?(klazz)
end
true
end
end
#
# This cache uses a LruHash (Least Recently Used) to store expressions.
# If an expression is not cached, the 'real storage' is consulted.
# The real storage is supposed to be the service named
# "expressionStorage.1"
#
class CacheExpressionStorage
include ServiceMixin
include OwfeServiceLocator
include ExpressionStorageBase
#
# under 20 stored expressions, the unit tests for the
# CachedFilePersistedEngine do fail because the persistent storage
# behind the cache hasn't the time to flush its work queue.
# a min size limit has been set to 77.
#
MIN_SIZE = 77
DEFAULT_SIZE = 5000
def initialize (service_name, application_context)
super()
service_init(service_name, application_context)
size = @application_context[:expression_cache_size] || DEFAULT_SIZE
size = MIN_SIZE unless size > MIN_SIZE
linfo { "new() size is #{size}" }
@cache = LruHash.new(size)
@real_storage = nil
observe_expool
end
def [] (fei)
#ldebug { "[] size is #{@cache.size}" }
#ldebug { "[] (sz #{@cache.size}) for #{fei.to_debug_s}" }
fe = @cache[fei.hash]
return fe if fe
#ldebug { "[] (reload) for #{fei.to_debug_s}" }
fe = get_real_storage[fei]
unless fe
#ldebug { "[] (reload) miss for #{fei.to_debug_s}" }
return nil
end
@cache[fei.hash] = fe
fe
end
def []= (fei, fe)
ldebug { "[]= caching #{fei}" }
@cache[fei.hash] = fe
end
def delete (fei)
@cache.delete fei.hash
end
def length
@cache.length
end
alias :size :length
def clear
@cache.clear
end
alias :purge :clear
#
# This implementations of find_expressions() immediately passes
# the call to the underlying real storage.
#
def find_expressions (options={})
get_real_storage.find_expressions options
end
#
# Attempts at fetching the root expression of a given process
# instance.
#
def fetch_root (wfid)
#
# at first, look in the cache
@cache.each do |hashed_fei, fexp|
return fexp \
if fexp.fei.wfid == wfid and fexp.is_a?(DefineExpression)
end
get_real_storage.fetch_root wfid
end
protected
#
# Returns the "real storage" i.e. the storage that does the real
# persistence behind this "cache storage".
#
def get_real_storage
@application_context[:s_expression_storage__1]
end
end
#
# Memory consuming in-memory storage.
# No memory limit, puts everything in a Hash
#
class InMemoryExpressionStorage < Hash
include ServiceMixin
include OwfeServiceLocator
include ExpressionStorageBase
def initialize (service_name, application_context)
service_init service_name, application_context
observe_expool
end
alias :purge :clear
#
# Finds expressions matching the given criteria (returns a list
# of expressions).
#
# This methods is called by the expression pool, it's thus not
# very "public" (not used directly by integrators, who should
# just focus on the methods provided by the Engine).
#
# :wfid ::
# will list only one process,
# :wfid => '20071208-gipijiwozo'
# :parent_wfid ::
# will list only one process, and its subprocesses,
# :parent_wfid => '20071208-gipijiwozo'
# :consider_subprocesses ::
# if true, "process-definition" expressions
# of subprocesses will be returned as well.
# :wfid_prefix ::
# allows your to query for specific workflow instance
# id prefixes. for example :
# :wfid_prefix => "200712"
# for the processes started in December.
# :include_classes ::
# excepts a class or an array of classes, only instances of these
# classes will be returned. Parent classes or mixins can be
# given.
# :includes_classes => OpenWFE::SequenceExpression
# :exclude_classes ::
# works as expected.
# :wfname ::
# will return only the expressions who belongs to the given
# workflow [name].
# :wfrevision ::
# usued in conjuction with :wfname, returns only the expressions
# with a given workflow revision.
# :applied ::
# if this option is set to true, will only return the expressions
# that have been applied (exp.apply_time != nil).
#
def find_expressions (options={})
values.find_all do |fexp|
does_match? options, fexp
end
end
#
# Attempts at fetching the root expression of a given process
# instance.
#
def fetch_root (wfid)
find_expressions(
:wfid => wfid,
:include_classes => DefineExpression)[0]
end
end
end