From 5c6c7ab1d173d24ea0c72ff4337a08c313bd9761 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Mon, 6 Oct 2008 15:54:46 +0200 Subject: [PATCH] The activity registry is now private to the shell. Changed the activityfactory to take a bundle instead of a bundle_id so that it doesn't depend on the registry. --- src/sugar/activity/Makefile.am | 3 +- src/sugar/activity/__init__.py | 3 - src/sugar/activity/activityfactory.py | 137 +++++++++---------- src/sugar/activity/registry.py | 189 -------------------------- src/sugar/bundle/activitybundle.py | 68 +-------- 5 files changed, 70 insertions(+), 330 deletions(-) delete mode 100644 src/sugar/activity/registry.py diff --git a/src/sugar/activity/Makefile.am b/src/sugar/activity/Makefile.am index 26a6782a..ca1fa613 100644 --- a/src/sugar/activity/Makefile.am +++ b/src/sugar/activity/Makefile.am @@ -6,5 +6,4 @@ sugar_PYTHON = \ activityhandle.py \ activityservice.py \ bundlebuilder.py \ - main.py \ - registry.py + main.py diff --git a/src/sugar/activity/__init__.py b/src/sugar/activity/__init__.py index 8a984ad8..8d3ef2bb 100644 --- a/src/sugar/activity/__init__.py +++ b/src/sugar/activity/__init__.py @@ -53,6 +53,3 @@ class. This class allows for querying the ID of the root window, requesting sharing across the network, and basic "what type of application are you" queries. """ -from sugar.activity.registry import ActivityRegistry -from sugar.activity.registry import get_registry -from sugar.activity.registry import ActivityInfo diff --git a/src/sugar/activity/activityfactory.py b/src/sugar/activity/activityfactory.py index b9b2a441..8ba639b9 100644 --- a/src/sugar/activity/activityfactory.py +++ b/src/sugar/activity/activityfactory.py @@ -23,7 +23,6 @@ import gobject from sugar.presence import presenceservice from sugar.activity.activityhandle import ActivityHandle -from sugar.activity import registry from sugar import util from sugar import env @@ -84,9 +83,9 @@ def create_activity_id(): def get_environment(activity): environ = os.environ.copy() - bin_path = os.path.join(activity.path, 'bin') + bin_path = os.path.join(activity.get_path(), 'bin') - activity_root = env.get_profile_path(activity.bundle_id) + activity_root = env.get_profile_path(activity.get_bundle_id()) if not os.path.exists(activity_root): os.mkdir(activity_root) @@ -102,19 +101,19 @@ def get_environment(activity): if not os.path.exists(tmp_dir): os.mkdir(tmp_dir) - environ['SUGAR_BUNDLE_PATH'] = activity.path - environ['SUGAR_BUNDLE_ID'] = activity.bundle_id + environ['SUGAR_BUNDLE_PATH'] = activity.get_path() + environ['SUGAR_BUNDLE_ID'] = activity.get_bundle_id() environ['SUGAR_ACTIVITY_ROOT'] = activity_root environ['PATH'] = bin_path + ':' + environ['PATH'] #environ['RAINBOW_STRACE_LOG'] = '1' - if activity.path.startswith(env.get_user_activities_path()): - environ['SUGAR_LOCALEDIR'] = os.path.join(activity.path, 'locale') + if activity.get_path().startswith(env.get_user_activities_path()): + environ['SUGAR_LOCALEDIR'] = os.path.join(activity.get_path(), 'locale') - if activity.bundle_id in [ 'org.laptop.WebActivity', - 'org.laptop.GmailActivity', - 'org.laptop.WikiBrowseActivity' - ]: + if activity.get_bundle_id() in [ 'org.laptop.WebActivity', + 'org.laptop.GmailActivity', + 'org.laptop.WikiBrowseActivity' + ]: environ['RAINBOW_CONSTANT_UID'] = 'yes' return environ @@ -123,8 +122,8 @@ def get_command(activity, activity_id=None, object_id=None, uri=None): if not activity_id: activity_id = create_activity_id() - command = activity.command.split(' ') - command.extend(['-b', activity.bundle_id]) + command = activity.get_command().split(' ') + command.extend(['-b', activity.get_bundle_id()]) command.extend(['-a', activity_id]) if object_id is not None: @@ -139,7 +138,7 @@ def get_command(activity, activity_id=None, object_id=None, uri=None): def open_log_file(activity): i = 1 while True: - path = env.get_logs_path('%s-%s.log' % (activity.bundle_id, i)) + path = env.get_logs_path('%s-%s.log' % (activity.get_bundle_id(), i)) try: fd = os.open(path, os.O_EXCL | os.O_CREAT \ | os.O_SYNC | os.O_WRONLY, 0644) @@ -164,10 +163,10 @@ class ActivityCreationHandler(gobject.GObject): create call. """ - def __init__(self, service_name, handle): + def __init__(self, bundle, handle): """Initialise the handler - service_name -- the service name of the bundle factory + bundle -- the ActivityBundle to launch activity_handle -- stores the values which are to be passed to the service to uniquely identify the activity to be created and the sharing @@ -190,15 +189,16 @@ class ActivityCreationHandler(gobject.GObject): """ gobject.GObject.__init__(self) - self._service_name = service_name + self._bundle = bundle + self._service_name = bundle.get_bundle_id() self._handle = handle self._use_rainbow = os.path.exists('/etc/olpc-security') - if service_name in [ 'org.laptop.JournalActivity', - 'org.laptop.Terminal', - 'org.laptop.Log', - 'org.laptop.Analyze' - ]: + if self._service_name in [ 'org.laptop.JournalActivity', + 'org.laptop.Terminal', + 'org.laptop.Log', + 'org.laptop.Analyze' + ]: self._use_rainbow = False bus = dbus.SessionBus() @@ -233,49 +233,46 @@ class ActivityCreationHandler(gobject.GObject): reply_handler=self._no_reply_handler, error_handler=self._notify_launch_error_handler) - activity_registry = registry.get_registry() - activity = activity_registry.get_activity(self._service_name) - if activity: - environ = get_environment(activity) - (log_path, log_file) = open_log_file(activity) - command = get_command(activity, self._handle.activity_id, - self._handle.object_id, - self._handle.uri) + environ = get_environment(self._bundle) + (log_path, log_file) = open_log_file(self._bundle) + command = get_command(self._bundle, self._handle.activity_id, + self._handle.object_id, + self._handle.uri) - if not self._use_rainbow: - # use gobject spawn functionality, so that zombies are - # automatically reaped by the gobject event loop. - def child_setup(): - # clone logfile.fileno() onto stdout/stderr - os.dup2(log_file.fileno(), 1) - os.dup2(log_file.fileno(), 2) - # close all other fds - _close_fds() - # we need to sanitize and str-ize the various bits which - # dbus gives us. - gobject.spawn_async([str(s) for s in command], - envp=['%s=%s' % (k, str(v)) - for k, v in environ.items()], - working_directory=str(activity.path), - child_setup=child_setup, - flags=(gobject.SPAWN_SEARCH_PATH | - gobject.SPAWN_LEAVE_DESCRIPTORS_OPEN)) - log_file.close() - else: - log_file.close() - system_bus = dbus.SystemBus() - factory = system_bus.get_object(_RAINBOW_SERVICE_NAME, - _RAINBOW_ACTIVITY_FACTORY_PATH) - factory.CreateActivity( - log_path, - environ, - command, - environ['SUGAR_BUNDLE_PATH'], - environ['SUGAR_BUNDLE_ID'], - timeout=30, - reply_handler=self._create_reply_handler, - error_handler=self._create_error_handler, - dbus_interface=_RAINBOW_ACTIVITY_FACTORY_INTERFACE) + if not self._use_rainbow: + # use gobject spawn functionality, so that zombies are + # automatically reaped by the gobject event loop. + def child_setup(): + # clone logfile.fileno() onto stdout/stderr + os.dup2(log_file.fileno(), 1) + os.dup2(log_file.fileno(), 2) + # close all other fds + _close_fds() + # we need to sanitize and str-ize the various bits which + # dbus gives us. + gobject.spawn_async([str(s) for s in command], + envp=['%s=%s' % (k, str(v)) + for k, v in environ.items()], + working_directory=str(self._bundle.get_path()), + child_setup=child_setup, + flags=(gobject.SPAWN_SEARCH_PATH | + gobject.SPAWN_LEAVE_DESCRIPTORS_OPEN)) + log_file.close() + else: + log_file.close() + system_bus = dbus.SystemBus() + factory = system_bus.get_object(_RAINBOW_SERVICE_NAME, + _RAINBOW_ACTIVITY_FACTORY_PATH) + factory.CreateActivity( + log_path, + environ, + command, + environ['SUGAR_BUNDLE_PATH'], + environ['SUGAR_BUNDLE_ID'], + timeout=30, + reply_handler=self._create_reply_handler, + error_handler=self._create_error_handler, + dbus_interface=_RAINBOW_ACTIVITY_FACTORY_INTERFACE) def _no_reply_handler(self, *args): pass @@ -315,18 +312,18 @@ class ActivityCreationHandler(gobject.GObject): logging.error("Datastore find failed %s" % err) self._create_activity() -def create(service_name, activity_handle=None): +def create(bundle, activity_handle=None): """Create a new activity from its name.""" if not activity_handle: activity_handle = ActivityHandle() - return ActivityCreationHandler(service_name, activity_handle) + return ActivityCreationHandler(bundle, activity_handle) -def create_with_uri(service_name, uri): +def create_with_uri(bundle, uri): """Create a new activity and pass the uri as handle.""" activity_handle = ActivityHandle(uri=uri) - return ActivityCreationHandler(service_name, activity_handle) + return ActivityCreationHandler(bundle, activity_handle) -def create_with_object_id(service_name, object_id): +def create_with_object_id(bundle, object_id): """Create a new activity and pass the object id as handle.""" activity_handle = ActivityHandle(object_id=object_id) - return ActivityCreationHandler(service_name, activity_handle) + return ActivityCreationHandler(bundle, activity_handle) diff --git a/src/sugar/activity/registry.py b/src/sugar/activity/registry.py deleted file mode 100644 index da2eb272..00000000 --- a/src/sugar/activity/registry.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (C) 2006-2007 Red Hat, Inc. -# Copyright (C) 2007 One Laptop Per Child -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -import logging - -import dbus -import gobject - -_ACTIVITY_REGISTRY_SERVICE_NAME = 'org.laptop.ActivityRegistry' -_ACTIVITY_REGISTRY_IFACE = 'org.laptop.ActivityRegistry' -_ACTIVITY_REGISTRY_PATH = '/org/laptop/ActivityRegistry' - -def _activity_info_from_dict(info_dict): - if not info_dict: - return None - return ActivityInfo(info_dict['name'], info_dict['icon'], - info_dict['bundle_id'], info_dict['version'], - info_dict['path'], info_dict['show_launcher'], - info_dict['command'], info_dict['favorite'], - info_dict['installation_time'], - info_dict['position_x'], info_dict['position_y']) - -class ActivityInfo(object): - def __init__(self, name, icon, bundle_id, version, path, show_launcher, - command, favorite, installation_time, position_x, position_y): - self.name = name - self.icon = icon - self.bundle_id = bundle_id - self.version = version - self.path = path - self.command = command - self.show_launcher = show_launcher - self.favorite = favorite - self.installation_time = installation_time - self.position = (position_x, position_y) - -class ActivityRegistry(gobject.GObject): - __gsignals__ = { - 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'activity-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - def __init__(self): - gobject.GObject.__init__(self) - - bus = dbus.SessionBus() - - # NOTE: We need to follow_name_owner_changes here - # because we can not connect to a signal unless - # we follow the changes or we start the service - # before we connect. Starting the service here - # causes a major bottleneck during startup - bus_object = bus.get_object(_ACTIVITY_REGISTRY_SERVICE_NAME, - _ACTIVITY_REGISTRY_PATH, - follow_name_owner_changes = True) - self._registry = dbus.Interface(bus_object, _ACTIVITY_REGISTRY_IFACE) - self._registry.connect_to_signal('ActivityAdded', - self._activity_added_cb) - self._registry.connect_to_signal('ActivityRemoved', - self._activity_removed_cb) - self._registry.connect_to_signal('ActivityChanged', - self._activity_changed_cb) - - # Two caches fo saving some travel across dbus. - self._service_name_to_activity_info = {} - self._mime_type_to_activities = {} - - def _convert_info_list(self, info_list): - result = [] - - for info_dict in info_list: - result.append(_activity_info_from_dict(info_dict)) - - return result - - def get_activities(self): - info_list = self._registry.GetActivities() - return self._convert_info_list(info_list) - - def _get_activities_cb(self, reply_handler, info_list): - result = [] - for info_dict in info_list: - result.append(_activity_info_from_dict(info_dict)) - - reply_handler(result) - - def _get_activities_error_cb(self, error_handler, e): - if error_handler: - error_handler(e) - else: - logging.error('Error getting activities async: %s' % str(e)) - - def get_activities_async(self, reply_handler=None, error_handler=None): - if not reply_handler: - logging.error('Function get_activities_async called' \ - 'without a reply handler. Can not run.') - return - - self._registry.GetActivities( - reply_handler=lambda info_list: \ - self._get_activities_cb(reply_handler, info_list), - error_handler=lambda e: \ - self._get_activities_error_cb(error_handler, e)) - - def get_activity(self, service_name): - if self._service_name_to_activity_info.has_key(service_name): - return self._service_name_to_activity_info[service_name] - - info_dict = self._registry.GetActivity(service_name) - activity_info = _activity_info_from_dict(info_dict) - - self._service_name_to_activity_info[service_name] = activity_info - return activity_info - - def find_activity(self, name): - info_list = self._registry.FindActivity(name) - return self._convert_info_list(info_list) - - def get_activities_for_type(self, mime_type): - if self._mime_type_to_activities.has_key(mime_type): - return self._mime_type_to_activities[mime_type] - - info_list = self._registry.GetActivitiesForType(mime_type) - activities = self._convert_info_list(info_list) - - self._mime_type_to_activities[mime_type] = activities - return activities - - def add_bundle(self, bundle_path): - result = self._registry.AddBundle(bundle_path) - # Need to invalidate here because get_activity could be called after - # add_bundle and before we receive activity-added, causing a race. - self._invalidate_cache() - return result - - def _activity_added_cb(self, info_dict): - logging.debug('ActivityRegistry._activity_added_cb: invalidating cache') - self._invalidate_cache() - self.emit('activity-added', _activity_info_from_dict(info_dict)) - - def _invalidate_cache(self): - self._service_name_to_activity_info.clear() - self._mime_type_to_activities.clear() - - def remove_bundle(self, bundle_path): - self._invalidate_cache() - return self._registry.RemoveBundle(bundle_path) - - def _activity_removed_cb(self, info_dict): - logging.debug('ActivityRegistry._activity_removed_cb: flushing caches') - self._invalidate_cache() - self.emit('activity-removed', _activity_info_from_dict(info_dict)) - - def _activity_changed_cb(self, info_dict): - logging.debug('ActivityRegistry._activity_changed_cb: flushing caches') - self._invalidate_cache() - self.emit('activity-changed', _activity_info_from_dict(info_dict)) - - def set_activity_favorite(self, bundle_id, version, favorite): - self._registry.SetActivityFavorite(bundle_id, version, favorite) - - def set_activity_position(self, bundle_id, version, x, y): - self._registry.SetActivityPosition(bundle_id, version, x, y) - -_registry = None - -def get_registry(): - global _registry - if not _registry: - _registry = ActivityRegistry() - return _registry diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activitybundle.py index 7097794b..206eeb00 100644 --- a/src/sugar/bundle/activitybundle.py +++ b/src/sugar/bundle/activitybundle.py @@ -259,7 +259,6 @@ class ActivityBundle(Bundle): return command - def get_mime_types(self): """Get the MIME types supported by the activity""" return self._mime_types @@ -268,22 +267,7 @@ class ActivityBundle(Bundle): """Get whether there should be a visible launcher for the activity""" return self._show_launcher - def is_installed(self): - if activity.get_registry().get_activity(self._bundle_id): - return True - else: - return False - - def need_upgrade(self): - """Returns True if installing this activity bundle is meaningful - - that is, if an identical version of this activity is not - already installed. - - Until we have cryptographic hashes to check identity, returns - True always. See http://dev.laptop.org/ticket/7534.""" - return True - - def unpack(self, install_dir, strict_manifest=False): + def install(self, install_dir, strict_manifest=False): self._unzip(install_dir) install_path = os.path.join(install_dir, self._zip_root_dir) @@ -352,35 +336,7 @@ class ActivityBundle(Bundle): os.path.basename(info_file))) return install_path - def install(self): - activities_path = env.get_user_activities_path() - act = activity.get_registry().get_activity(self._bundle_id) - if act is not None and act.path.startswith(activities_path): - raise AlreadyInstalledException - - install_dir = env.get_user_activities_path() - install_path = self.unpack(install_dir) - - if not activity.get_registry().add_bundle(install_path): - raise RegistrationException - - def uninstall(self, force=False): - if self._zip_file is None: - install_path = self._path - else: - if not self.is_installed(): - raise NotInstalledException - - act = activity.get_registry().get_activity(self._bundle_id) - if not force and act.version != self._activity_version: - logging.warning('Not uninstalling, different bundle present') - return - elif not act.path.startswith(env.get_user_activities_path()): - logging.warning('Not uninstalling system activity') - return - - install_path = act.path - + def uninstall(self, install_path, force=False): xdg_data_home = os.getenv('XDG_DATA_HOME', os.path.expanduser('~/.local/share')) @@ -404,23 +360,3 @@ class ActivityBundle(Bundle): os.remove(path) self._uninstall(install_path) - - if not activity.get_registry().remove_bundle(install_path): - raise RegistrationException - - def upgrade(self): - act = activity.get_registry().get_activity(self._bundle_id) - if act is None: - logging.warning('Activity not installed') - elif act.path.startswith(env.get_user_activities_path()): - try: - self.uninstall(force=True) - except Exception, e: - logging.warning('Uninstall failed (%s), still trying ' \ - 'to install newer bundle', e) - else: - logging.warning('Unable to uninstall system activity, ' \ - 'installing upgraded version in user activities') - - self.install() -