Telepathy

معمارية تطبيقات المصادر المفتوحة (المجلد الأول)Telepathy

معمارية تطبيقات المصادر المفتوحة (المجلد الأول) Telepathy

Danielle Madeley

إذا استمتعت بهذه الكتب، فقد يعجبك أيضًا Software Design by Example in Python وSoftware Design by Example in JavaScript.

Telepathy[1] إطار عمل معياري للاتصالات الآنية (real-time) يتناول الصوت والفيديو والنص ونقل الملفات وما إلى ذلك. وليس ما يميّز Telepathy أنه يُخفي تفاصيل بروتوكولات المراسلة الفورية المختلفة، بل أنه يقدّم فكرة «الاتصالات كخدمة»، تمامًا كما أن الطباعة خدمة متاحة للعديد من التطبيقات في الوقت نفسه. ولتحقيق ذلك يستخدم Telepathy بكثافة ناقل رسائل D-Bus وتصميمًا معياريًا.

الاتصالات كخدمة مفيدة للغاية لأنها تتيح لنا نزع الاتصالات من داخل تطبيق مفرد. وهذا يتيح حالات استخدام كثيرة ومثيرة: مثل رؤية حضور جهة اتصال في تطبيق البريد لديك، ثم بدء التواصل معها؛ أو إطلاق عملية نقل ملف إلى جهة اتصال مباشرةً من متصفّح الملفات؛ أو توفّر التعاون بين جهات الاتصال داخل التطبيقات، وهو ما يسمّيه Telepathy الأنابيب (Tubes).

أنشأ Robert McQueen مشروع Telepathy عام 2005، ومنذ ذلك الحين جرى تطويره وصيانته من قِبل عدة شركات ومساهمين أفراد، من بينها Collabora، وهي الشركة التي شاركت McQueen في تأسيسها.

ناقل رسائل D-Bus

D-Bus ناقل رسائل غير متزامن للتواصل بين العمليات (IPC)، يشكّل العمود الفقري لمعظم أنظمة GNU/Linux، بما فيها بيئتا سطح المكتب GNOME وKDE. وD-Bus بنية ناقل مشترك في المقام الأول: تتصل التطبيقات بناقل (يُحدَّد بعنوان مقبس) وتستطيع إمّا أن ترسل رسالة موجَّهة إلى تطبيق آخر على الناقل، أو أن تبثّ إشارة إلى جميع أعضاء الناقل. و للتطبيقات الموجودة على الناقل عنوان ناقل شبيه بعنوان IP، ويمكنها أن تطلب عددًا من الأسماء المعروفة (well-known names)، مثل أسماء DNS، ومنها مثلًا org.freedesktop.Telepathy.AccountManager. وتتواصل جميع العمليات عبر البرنامج الخفي (daemon) الخاص بـ D-Bus، وهو المسؤول عن تمرير الرسائل وتسجيل الأسماء.

من منظور المستخدم، يتوفّر على كل نظام ناقلان. فناقل النظام (system bus) هو ناقل يتيح للمستخدم التواصل مع مكوّنات النظام كاملةً (الطابعات، والبلوتوث، وإدارة الأجهزة، وغيرها)، ويشاركه جميع مستخدمي النظام. أما ناقل الجلسة (session bus) فهو خاص بذلك المستخدم—أي أن لكل مستخدم مسجَّل الدخول ناقل جلسة خاص به—ويُستخدم ليتواصل تطبيقات المستخدم فيما بينها. وعندما يُنقل حجم كبير من البيانات عبر الناقل، من الممكن أيضًا للتطبيقات أن تنشئ ناقلًا خاصًّا بها، أو تنشئ ناقلًا من نظير إلى نظير بلا وسيط تحكّم ومن دون dbus-daemon.

تنفّذ عدة مكتبات بروتوكول D-Bus وتستطيع الاتصال بالبرنامج الخفي الخاص به، ومنها libdbus وGDBus وQtDBus وpython-dbus. وهذه المكتبات مسؤولة عن إرسال رسائل D-Bus واستقبالها، وتحويل الأنواع من نظام أنواع اللغة إلى صيغة أنواع D-Bus، ونشر الكائنات على الناقل. وعادةً ما تقدّم المكتبات كذلك واجهات برمجية مريحة لتعداد التطبيقات المتصلة والتطبيقات القابلة للتفعيل، ولطلب الأسماء المعروفة على الناقل. وعلى مستوى D-Bus، يتم كل ذلك عبر إجراء استدعاءات أساليب (method calls) على كائن ينشره dbus-daemon نفسه.

للمزيد من المعلومات عن D-Bus، راجع http://www.freedesktop.org/wiki/Software/dbus.

20.1. مكوّنات إطار عمل Telepathy

Telepathy معياري، وتتواصل كل وحدة مع غيرها عبر ناقل رسائل D-Bus، وغالبًا عبر ناقل جلسة المستخدم. وهذا التواصل موصَّل بالتفصيل في مواصفة Telepathy[2]. وتظهر مكوّنات إطار عمل Telepathy في الشكل 20.1:

  • مدير الاتصال (Connection Manager) يوفّر الواجهة بين Telepathy وخدمات الاتصال الفردية. فمثلًا هناك مدير اتصال لـ XMPP، وآخر لـ SIP، وآخر لـ IRC، وهكذا. وإضافة دعم بروتوكول جديد إلى Telepathy ليست سوى مسألة كتابة مدير اتصال جديد.
  • خدمة مدير الحسابات (Account Manager) مسؤولة عن تخزين حسابات اتصالات المستخدم وإنشاء اتصال مع كل حساب عبر مدير الاتصال المناسب عند الطلب.
  • دور موزّع القنوات (Channel Dispatcher) هو الاستماع إلى القنوات الواردة التي يشير إليها كل مدير اتصال، وتوزيعها على العملاء الذين يبدون قدرتهم على معالجة ذلك النوع من القنوات، مثل النص والصوت والفيديو ونقل الملفات والأنابيب. كما يوفّر موزّع القنوات خدمة تتيح للتطبيقات، وأهمّها التطبيقات التي ليست عملاء لـ Telepathy، أن تطلب قنوات صادرة وتُعالَج محليًا بواسطة العميل المناسب. وهذا يتيح لتطبيق ما، مثل تطبيق بريد إلكتروني، أن يطلب محادثة نصية مع جهة اتصال، ثم يعرض عميل المراسلة الفورية لديك نافذة محادثة.
  • عملاء Telepathy يعالجون قنوات الاتصال أو يراقبونها. وهم يشملون كلاً من واجهات المستخدم مثل عملاء المراسلة الفورية و VoIP، وخدمات مثل مسجِّل المحادثات. ويقوم العملاء بتسجيل أنفسهم لدى موزّع القنوات، معطّين قائمة بأنواع القنوات التي يرغبون في معالجتها أو مراقبتها.

داخل التنفيذ الحالي لـ Telepathy، يوفّر مدير الحسابات وموزّع القنوات معًا عملية واحدة تُعرف بـ Mission Control.

مثال مكوّنات Telepathy

الشكل 20.1: مثال مكوّنات Telepathy

هذا التصميم المعياري استند إلى فلسفة Doug McIlroy، «اكتب برامج تفعل شيئًا واحدًا وتفعله جيدًا»، وهو يجلب عدة مزايا مهمة:

  • المتانة (Robustness): لا يؤدّي خلل في أحد المكوّنات إلى إسقاط الخدمة كاملة.
  • سهولة التطوير: يمكن استبدال المكوّنات ضمن نظام قيد التشغيل دون التأثير على غيرها. ومن الممكن اختبار نسخة تطويرية من وحدة ما مقابل نسخة أخرى معروفة بأنها سليمة.
  • استقلالية اللغة: يمكن كتابة المكوّنات بأي لغة تتوفّر لها روابط لـ D-Bus. فإذا كانت أفضل تنفيذات بروتوكول اتصال ما مكتوبة بلغة معيّنة، فبإمكانك كتابة مدير الاتصال بتلك اللغة، ويظل متاحًا لجميع عملاء Telepathy. وبالمثل، إن أردت تطوير واجهة المستخدم بلغة معيّنة، فلديك إمكانية الوصول إلى جميع البروتوكولات المتاحة.
  • استقلالية الرخصة: يمكن أن تخضع المكوّنات لتراخيص برمجية مختلفة كانت لتصبح غير متوافقة لو كان كل شيء يعمل كعملية واحدة.
  • استقلالية الواجهة: يمكن تطوير واجهات مستخدم متعددة فوق المكوّنات نفسها من Telepathy. وهذا يتيح واجهات أصلية لبيئات سطح المكتب وأجهزة العتاد (مثل GNOME وKDE وMeego وSugar).
  • الأمن: تعمل المكوّنات في مساحات عناوين منفصلة وبصلاحيات محدودة للغاية. فمثلًا لا يحتاج مدير الاتصال النمطي إلا إلى الوصول إلى الشبكة وناقل جلسة D-Bus، مما يجعل من الممكن استخدام شيء مثل SELinux لتقييد ما يمكن للمكوّن الوصول إليه.

يدير مدير الاتصال عددًا من الاتصالات (Connections)، حيث يمثّل كل اتصال ارتباطًا منطقيًا بخدمة اتصال. ويوجد اتصال واحد لكل حساب مُهيّأ. وسيحتوي كل اتصال على عدّة قنوات. والقنوات هي الآلية التي تجري عبرها الاتصالات. وقد تكون القناة محادثة مراسلة فورية، أو مكالمة صوتية أو فيديو، أو نقل ملف، أو أي عملية أخرى ذات حالة. وتناقش الاتصالات والقنوات بالتفصيل في القسم 20.3.

20.2. كيف يستخدم Telepathy D-Bus

تتواصل مكوّنات Telepathy عبر ناقل رسائل D-Bus، وهو عادةً ناقل جلسة المستخدم. ويوفّر D-Bus مزايا شائعة في كثير من أنظمة التواصل بين العمليات (IPC): فكل خدمة تنشر كائنات تملك مسار كائن (object path) مُوَقَّعًا في نطاق أسماء صارم، مثل /org/freedesktop/Telepathy/AccountManager[3]. وينفّذ كل كائن عددًا من الواجهات. وهي أيضًا مُعرَّفة في نطاق أسماء صارم، بصيغ مثل org.freedesktop.DBus.Properties وofdT.Connection. وتوفّر كل واجهة أساليب وإشارات وخصائص يمكنك استدعاؤها، أو مراقبتها، أو طلبها.

التمثيل التصوّري للكائنات التي تنشرها خدمة D-Bus

الشكل 20.2: التمثيل التصوّري للكائنات التي تنشرها خدمة D-Bus

نشر كائنات D-Bus

يتم التوفيق بين نشر كائنات D-Bus بالكامل عبر مكتبة D-Bus المستخدَمة. وبالمعنى الفعلي فإن ذلك ربط (mapping) بين مسار كائن D-Bus والكائن البرمجي الذي ينفّذ تلك الواجهات. وتُعرَض مسارات الكائنات التي تنشرها خدمة عبر الواجهة الاختيارية org.freedesktop.DBus.Introspectable.

عندما تتلقّى خدمة استدعاء أسلوب واردًا بمسار وجهة معيّن (مثل /ofdT/AccountManager)، تتولّى مكتبة D-Bus مهمّة تحديد الكائن البرمجي الذي يوفّر كائن D-Bus هذا، ثم إجراء استدعاء الأسلوب المناسب على ذلك الكائن.

الواجهات والأساليب والإشارات والخصائص التي يوفّرها Telepathy موصَّلة بالتفصيل في لغة تعريف واجهات (IDL) قائمة على XML، وقد وُسِّعت هذه اللغة لتتضمّن معلومات أكثر. ويمكن تحليل المواصفة لتوليد التوثيق وروابط اللغات.

تنشر خدمات Telepathy عددًا من الكائنات على الناقل. فينشر Mission Control كائنات مدير الحسابات وموزّع القنوات بحيث يمكن الوصول إلى خدمتيهما. وينشر العملاء كائن عميل يمكن لموزّع القنوات الوصول إليه. وأخيرًا ينشر مديرو الاتصال عددًا من الكائنات: كائن خدمة يمكن لمدير الحسابات استخدامه لطلب اتصالات جديدة، وكائنًا لكل اتصال مفتوح، وكائنًا لكل قناة مفتوحة.

رغم أن كائنات D-Bus لا تملك نوعًا (فقط واجهات)، فإن Telepathy يحاكي الأنواع بعدة طرق. فمسار الكائن يخبرنا ما إذا كان الكائن اتصالًا أم قناة أم عميلًا وهكذا، وإن كنت عادةً تعرف ذلك مسبقًا عندما تطلب وكيلًا (proxy) له. وينفّذ كل كائن الواجهة الأساسية لذلك النوع، مثل ofdT.Connection أو ofdT.Channel. وفي حالة القنوات، يشبه ذلك نوعًا ما صنفًا أساسيًا تجريديًا (abstract base class). ثم تملك كائنات القنوات بدورها صنفًا ملموسًا (concrete class) يحدّد نوع قناتها. ومرة أخرى، يمثَّل ذلك بواجهة D-Bus. ويمكن معرفة نوع القناة بقراءة الخاصية ChannelType على واجهة القناة.

وأخيرًا، ينفّذ كل كائن عددًا من الواجهات الاختيارية (والتي تُمثَّل، كما هو متوقَّع، بواجهات D-Bus أيضًا)، وهي تعتمد على قدرات البروتوكول ومدير الاتصال. والواجهات المتاحة على كائن معيّن تُتاح عبر الخاصية Interfaces الموجودة على الصنف الأساسي للكائن.

بالنسبة إلى كائنات الاتصال من نوع ofdT.Connection، فإن للواجهات الاختيارية أسماء مثل ofdT.Connection.Interface.Avatars (إذا كان البروتوكول يملك مفهوم الصور الرمزية)، وodfT.Connection.Interface.ContactList (إذا كان البروتوكول يوفّر قائمة جهات اتصال—وليست كل البروتوكولات تفعل)، وodfT.Connection.Interface.Location (إذا كان بروتوكول ما يوفّر معلومات تحديد الموقع الجغرافي). أما كائنات القناة، من نوع ofdT.Channel، فللأصنافها الملموسة أسماء واجهات من الصيغة ofdT.Channel.Type.Text وodfT.Channel.Type.Call وodfT.Channel.Type.FileTransfer. ومثل الاتصالات، تحمل الواجهات الاختيارية أسماء مثل odfT.Channel.Interface.Messages (إذا كانت القناة تستطيع إرسال الرسائل النصية واستقبالها) وodfT.Channel.Interface.Group (إذا كانت القناة لمجموعة تتضمّن عدة جهات اتصال، مثل محادثة متعددة المستخدمين). إذن، تنفّذ القناة النصية مثلًا الواجهات ofdT.Channel وofdT.Channel.Type.Text وChannel.Interface.Messages على الأقل. وإذا كانت محادثة متعددة المستخدمين، فستنفّذ أيضًا odfT.Channel.Interface.Group.

لماذا خاصية Interfaces بدل استكشاف D-Bus؟

قد تتساءل لماذا ينفّذ كل صنف أساسي الخاصية Interfaces، بدلًا من الاعتماد على قدرات الاستكشاف (introspection) في D-Bus لإخبارنا بالواجاجهات المتاحة. والجواب أن كائنات القنوات والاتصال المختلفة قد تقدّم واجهات مختلفة بعضها لبعض، تبعًا لقدرات القناة أو الاتصال، لكن معظم تطبيقات استكشاف D-Bus تفترض أن جميع الكائنات المنتمية إلى الصنف نفسه لها الواجهات نفسها. فمثلًا، في telepathy-glib، تُسترجَع واجهات D-Bus التي يسردها الاستكشاف من واجهات الكائنات التي ينفّذها الصنف، وهي مُعرَّفة بشكل ساكن وقت الترجمة. ونتفادى ذلك بجعل استكشاف D-Bus يوفّر بيانات عن جميع الواجهات التي يمكن أن توجد على كائن، ونستخدم الخاصية Interfaces للإشارة إلى الواجهات الموجودة فعلًا.

رغم أن D-Bus نفسه لا يوفّر أي فحص اتّساق بأن كائنات الاتصال لا تحمل سوى واجهات متعلقة بالاتصال وما شابه (إذ لا يملك D-Bus مفهوم الأنواع، بل واجهات ذات أسماء اعتباطية)، يمكننا استخدام المعلومات الواردة في مواصفة Telepathy لتوفير فحص اتّساق داخل روابط لغة Telepathy.

لماذا وكيف وُسِّعت لغة تعريف المواصفة

تعرّف لغة تعريف واجهات D-Bus الحالية أسماء الأساليب والخصائص والإشارات، ووسائطها، وضوابط الوصول إليها، وتوقيعات أنواع D-Bus. وهي لا تقدّم أي دعم للتوثيق أو تلميحات الربط أو الأنواع المسمّاة.

لمعالجة هذه القيود، أُضيف نطاق أسماء XML جديد لتوفير المعلومات المطلوبة. وقد صُمِّم هذا النطاق ليكون عامًّا بحيث يمكن أن تستخدمه واجهات D-Bus البرمجية الأخرى. وأُضيفت عناصر جديدة تتضمّن توثيقًا مُضمَّنًا، وأسبابًا (rationales)، وإصدارات الإدخال والإهمال، واستثناءات محتملة من الأساليب.

توقيعات أنواع D-Bus هي التدوين منخفض المستوى للنوع الذي يُسلسَل عبر الناقل. وقد تبدو توقيعات أنواع D-Bus مثل (ii) (وهي بنية تحتوي على عدد صحيح int32 اثنين)، أو قد تكون أعقد من ذلك. فمثلًا، a{sa(usuu)} هي خريطة (map) من سلسلة نصية إلى مصفوفة من البنى تحتوي كل منها على uint32 وسلسلة نصية وuint32 وuint32 (الشكل 20.3). وهذه الأنواع، وإن كانت تصف صيغة البيانات، فلا توفّر أي معنى دلالي للمعلومات التي يحتوي عليها النوع.

سعيًا إلى توفير وضوح دلالي للمبرمجين، وتعزيز الأنواع في روابط اللغات، أُضيفت عناصر جديدة لتسمية الأنواع البسيطة، والبنى، والخرائط، والتعدادات، والأعلام، مع توفير توقيع نوعها، فضلًا عن التوثيق. وأُضيفت عناصر أيضًا من أجل محاكاة وراثة الكائنات في كائنات D-Bus.

أنواع D-Bus (ii) و a{sa(usuu)}

الشكل 20.3: أنواع D-Bus (ii) و a{sa(usuu)}

20.2.1. المُعرِّفات (Handles)

تُستخدم المُعرِّفات (handles) في Telepathy لتمثيل المعرّفات (identifiers)، مثل جهات الاتصال وأسماء الغرف. وهي قيمة عدد صحيح غير سالب يعيّنها مدير الاتصال، بحيث تشير المجموعة (الاتصال، نوع المُعرِّف، المُعرِّف) بشكل فريد إلى جهة اتصال أو غرفة بعينها.

لأن بروتوكولات الاتصال المختلفة تطبّع المعرّفات بطرق مختلفة (مثل التمييز في حالة الأحرف، والموارد)، توفّر المُعرِّفات طريقةً للعملاء كي يحدّدوا ما إذا كان مُعرّفان هما نفسه. إذ يمكنهم طلب المُعرِّف الخاص بمُعرّفين مختلفين، وإذا تطابق رقم المُعرِّف فإن المُعرّفَين يشيران إلى جهة الاتصال أو الغرفة نفسها.

قواعد تطبيع المعرّفات تختلف لكل بروتوكول، لذا فإن مقارنة سلاسل المعرّفات الحرفية هو خطأ يقع فيه العملاء. فمثلًا، escher@tuxedo.cat/bed وescher@tuxedo.cat/litterbox هما نسختان من جهة الاتصال نفسها (escher@tuxedo.cat) في بروتوكول XMPP، ولذلك لهما المُعرِّف نفسه. ومن الممكن للعملاء طلب القنوات بواسطة المعرّف أو بواسطة المُعرِّف، لكن ينبغي ألّا يستخدموا المُعرِّفات إلا في المقارنة.

20.2.2. اكتشاف خدمات Telepathy

بعض الخدمات، مثل مدير الحسابات وموزّع القنوات، موجودة دائمًا، لها أسماء معروفة (well-known names) معرَّفة في مواصفة Telepathy. غير أن أسماء مديري الاتصال والعملاء ليست معروفة، ويجب اكتشافها.

لا توجد في Telepathy خدمة مسؤولة عن تسجيل مديري الاتصال والعملاء قيد التشغيل. وبدلًا من ذلك، ينتظر الأطراف المهتمة على D-Bus الإعلان عن خدمة جديدة. فيرسل البرنامج الخفي للناقل إشارة كلما ظهرت خدمة D-Bus مسمّاة جديدة على الناقل. وتبدأ أسماء العملاء ومديري الاتصال ببادئات معروفة، تحدّدها المواصفة، ويمكن مطابقة الأسماء الجديدة عليها.

ميزة هذا التصميم أنه عديم الحالة تمامًا. فعند بدء تشغيل أحد مكوّنات Telepathy، يمكنه أن يسأل البرنامج الخفي للناقل (الذي لديه قائمة مرجعية مبنية على اتصالاته المفتوحة) ما الخدمات التي قيد التشغيل حاليًا. فمثلًا، إذا انهار مدير الحسابات، فإنه يستطيع النظر في الاتصالات التي قيد التشغيل، وإعادة ربطها بكائنات الحسابات الخاصة به.

الاتصالات خدمات أيضًا

إلى جانب مديري الاتصال أنفسهم، يُعلَن الاتصالات أيضًا كخدمات D-Bus. وهذا يسمح من الناحية النظرية بأن يتفرّع مدير الاتصال كل اتصال ليصبح عملية منفصلة، لكن لم يُنفَّذ أي مدير اتصال من هذا النوع حتى اليوم. والأكثر عمليّة أنه يتيح اكتشاف جميع الاتصالات قيد التشغيل بالاستعلام من البرنامج الخفي للناقل عن جميع الخدمات التي تبدأ بـ ofdT.Connection.

كما يستخدم موزّع القنوات هذه الطريقة لاكتشاف عملاء Telepathy. وتبدأ هذه الأسماء بـ ofdT.Client، مثل ofdT.Client.Logger.

20.2.3. تقليل حركة D-Bus

أدت النسخ الأصلية من مواصفة Telepathy إلى إنشاء كمّية مفرطة من حركة D-Bus على صورة استدعاءات أساليب تطلب معلومات يريدها مستهلكون كثيرون على الناقل. وعالجت النسخ الأحدث من Telepathy ذلك عبر عدد من التحسينات.

استُبدلت استدعاءات الأساليب المفردة بخصائص D-Bus. فقد تضمّنت المواصفة الأصلية استدعاءات أساليب منفصلة لخصائص الكائن: GetInterfaces وGetChannelType وغيرها. وكان طلب كل خصائص كائن يتطلب عدة استدعاءات أساليب، لكل منها كلفته الخاصة. وباستخدام خصائص D-Bus، يمكن طلب كل شيء دفعة واحدة باستخدام الأسلوب القياسي GetAll.

علاوةً على ذلك، فإن عددًا كبيرًا من خصائص القناة لا يتغيّر طوال عمر القناة. وتشمل هذه نوع القناة، وواجهاتها، ومن تتّصل به، ومن قدّم الطلب. أما قناة نقل الملفات، مثلًا، فتضمّ كذلك حجم الملف ونوع محتواه.

أُضيفت إشارة جديدة تعلن إنشاء القنوات (الواردة وكذلك استجابةً للطلبات الصادرة) وتتضمّن جدول تجزئة (hash table) للخصائص الثابتة. ويمكن تمريرها مباشرةً إلى مُنشئ كائن القناة الوكيل (انظر القسم 20.4)، مما يُغني العملاء المهتمين عن طلب هذه المعلومات كلٍّ على حدة.

تُنقل الصور الرمزية للمستخدمين عبر الناقل على هيئة مصفوفات بايتات. ورغم أن Telepathy كان يستخدم الرموز (tokens) للإشارة إلى الصور الرمزية، مما يتيح للعملاء معرفة الوقت الذي يحتاجون فيه إلى صورة رمزية جديدة، وتجنّب تنزيل الصور غير المطلوبة، كان على كل عميل أن يطلب الصورة الرمزية على حدة عبر أسلوب RequestAvatar الذي كانت يعود الصورة الرمزية كردٍّ له. وهكذا، عندما يكون مدير الاتصال قد أشار إلى أن جهة اتصال حدّثت صورتها الرمزية، تصلك عدّة طلبات فردية للصورة الرمزية، مما يستلزم نقل الصورة الرمزية عبر ناقل الرسائل عدّة مرّات.

عولج ذلك بإضافة أسلوب جديد لا يعود الصورة الرمزية (بل لا يعود شيئًا). وبدلًا من ذلك، يضع الصورة الرمزية في طابور طلبات. وكان جلب الصورة الرمزية من الشبكة يؤدي إلى إشارة، هي AvatarRetrieved، يستطيع جميع العملاء المهتمين مراقبتها. وهذا يعني أن بيانات الصورة الرمزية لا يلزم نقلها عبر الناقل إلا مرة واحدة، وستكون متاحة لجميع العملاء المهتمين. وبعد أن يكون طلب العميل قد وُضع في الطابور، يمكن تجاهل أي طلبات لاحقة من العملاء إلى أن تُصدر إشارة AvatarRetrieved.

عندما يلزم تحميل عدد كبير من جهات الاتصال (أي عند تحميل قائمة جهات الاتصال)، فإن قدرًا كبيرًا من المعلومات يحتاج إلى الطلب: الألقاب المستعارة، والصور الرمزية، والقدرات، وانتماءات المجموعات، وربما الموقع والعنوان وأرقام الهواتف. وكان هذا في Telepathy يتطلب استدعاء أسلوب واحد لكل مجموعة معلومات (معظم استدعاءات واجهة البرمجة، مثل GetAliases، كانت تأخذ قائمة جهات اتصال أصلًا)، مما ينتج عنه ستة استدعاءات أو أكثر.

لحل ذلك، قُدِّمت واجهة Contacts. وقد سمحت بإرجاع معلومات من واجهات متعددة عبر استدعاء أسلوب واحد. ووُسِّعت مواصفة Telepathy لتضمّن «سمات جهات الاتصال» (Contact Attributes): خصائص مُعرَّفة في نطاق أسماء أعادها أسلوب GetContactAttributes، تحلّ محل استدعاءات الأساليب التي كانت تُستخدم لاسترجاع معلومات جهة الاتصال. يستدعي العميل GetContactAttributes مع قائمة من جهات الاتصال والواجهات التي يهمّه، ويعيد خريطة من جهات الاتصال إلى خريطة من سمات جهة الاتصال إلى قيمها.

وستوضّح مقتطف الشيفرة التالي ذلك. ويبدو الطلب كالتالي:

connection[CONNECTION_INTERFACE_CONTACTS].GetContactAttributes(
  [ 1, 2, 3 ], # contact handles
  [ "ofdT.Connection.Interface.Aliasing",
    "ofdT.Connection.Interface.Avatars",
    "ofdT.Connection.Interface.ContactGroups",
    "ofdT.Connection.Interface.Location"
  ],
  False # don't hold a reference to these contacts
)

ويبدو الرد كالتالي:

{ 1: { 'ofdT.Connection.Interface.Aliasing/alias': 'Harvey Cat',
       'ofdT.Connection.Interface.Avatars/token': hex string,
       'ofdT.Connection.Interface.Location/location': location,
       'ofdT.Connection.Interface.ContactGroups/groups': [ 'Squid House' ],
       'ofdT.Connection/contact-id': 'harvey@nom.cat'
     },
  2: { 'ofdT.Connection.Interface.Aliasing/alias': 'Escher Cat',
       'ofdT.Connection.Interface.Avatars/token': hex string,
       'ofdT.Connection.Interface.Location/location': location,
       'ofdT.Connection.Interface.ContactGroups/groups': [],
       'ofdT.Connection/contact-id': 'escher@tuxedo.cat'
     },
  3: { 'ofdT.Connection.Interface.Aliasing/alias': 'Cami Cat',
        ⋮    ⋮    ⋮
     }
}

20.3. الاتصالات والقنوات والعملاء

20.3.1. الاتصالات

ينشئ مدير الاتصال كائن الاتصال (Connection) لإنشاء ارتباط مع بروتوكول/حساب واحد. فمثلًا، سيؤدي الاتصال بالحسابين escher@tuxedo.cat وcami@egg.cat في XMPP إلى اتصالين، يمثّل كلٌّ منهما كائن D-Bus. وإعداد الاتصالات يتم عادةً بواسطة مدير الحسابات، للحسابات المفعّلة حاليًا.

يوفّر الاتصال بعض الوظائف الإلزامية لإدارة حالة الاتصال ومراقبتها ولطلب القنوات. ويمكنه أيضًا أن يوفّر عددًا من الميزات الاختيارية، بحسب ميزات البروتوكول. وتُقدَّم هذه كواجهات D-Bus اختيارية (كما ناقشنا في القسم السابق) وتُدرَج في قائمة الخاصية Interfaces الخاصة بالاتصال.

عادةً ما يدير مدير الحسابات الاتصالات، ويُنشئها انطلاقًا من خصائص الحسابات المعنية. كما يزامن مدير الحسابات حضور المستخدم في كل حساب مع الاتصال الموافق له، ويمكن أن يُطلب منه مسار الاتصال الخاص بحساب معيّن.

20.3.2. القنوات

القنوات هي الآلية التي تجري عبرها الاتصالات. والقناة عادةً ما تكون محادثة مراسلة فورية، أو مكالمة صوتية أو فيديو، أو نقل ملف، لكن تُستخدم القنوات أيضًا لتوفير اتصال ذي حالة مع الخادم نفسه، (مثلًا للبحث عن غرف محادثة أو جهات اتصال). ويمثّل كل قناة كائن D-Bus.

تقع القنوات عادةً بين مستخدمين اثنين أو أكثر، أحدهم أنت. وولها عادةً معرّف هدف، وهو إمّا جهة اتصال أخرى في حالة الاتصال الثنائي، أو معرّف غرفة في حالة الاتصال متعدد المستخدمين (مثل غرفة محادثة). وتكشف القنوات متعددة المستخدمين واجهة Group التي تتيح لك تتبّع جهات الاتصال الموجودة حاليًا في القناة.

تنتمي القنوات إلى اتصال، وتُطلب من مدير الاتصال، عادةً عبر موزّع القنوات؛ أو تُنشئها استجابةً لحدث شبكي (مثل محادثة واردة) بواسطة الاتصال، ثم تُسلَّم إلى موزّع القنوات لتوزيعها.

يحدَّد نوع القناة بالخاصية ChannelType الخاصة بها. أما الميزات الأساسية والأساليب والخصائص والإشارات اللازمة لنوع القناة هذا (مثل إرسال الرسائل النصية واستقبالها) فتُعرَّف في واجهة D-Bus المناسبة من نوع Channel.Type، ومنها مثلًا Channel.Type.Text. وقد تنفّذ بعض أنواع القنوات ميزات اختيارية إضافية (مثل التشفير) تظهر كواجهات إضافية تُدرَج في قائمة الخاصية Interfaces الخاصة بالقناة. وقد يكون لقناة نصية تربط المستخدم بغرفة محادثة متعددة المستخدمين الواجهات المبيّنة في الجدول 20.1.

الخاصية الغرض
odfT.Channel ميزات مشتركة بين جميع القنوات
odfT.Channel.Type.Text نوع القناة، ويتضمّن ميزات مشتركة بين القنوات النصية
odfT.Channel.Interface.Messages مراسلة النص المنسّق
odfT.Channel.Interface.Group تعداد أعضاء هذه القناة وتتبّعهم ودعوتهم والموافقة عليهم
odfT.Channel.Interface.Room قراءة وضبط خصائص مثل موضوع غرفة المحادثة

الجدول 20.1: مثال على قناة نصية

قنوات قوائم جهات الاتصال: خطأ

في النسخ الأولى من مواصفة Telepathy، كانت قوائم جهات الاتصال تُعدّ نوعًا من القنوات. وكانت هناك عدّة قوائم لجهات الاتصال يعرّفها الخادم (المشتركون، ومن يُنشر لهم، والمستخدمون المحظورون)، يمكن طلبها من كل اتصال. ثم كانت أعضاء القائمة يُكتشفون باستخدام واجهة Group، كما في محادثة متعددة المستخدمين.

وكان هذا يعني أصلًا أن لا يُنشأ أي ارتباط بالقناة إلا بعد استرجاع قائمة جهات الاتصال، وهو ما يستغرق وقتًا لدى بعض البروتوكولات. وكان بإمكان العميل طلب القناة في أي وقت، فتسلَّم له حين تصبح جاهزة، لكن بالنسبة للمستخدمين الذين لديهم جهات اتصال كثيرة كان ذلك يعني أن الطلب ينتهي مهله من حين لآخر. وكان تحديد حالة الاشتراك/النشر/الحظر لعميل ما يتطلب فحص ثلاث قنوات.

كما كُشفت مجموعات جهات الاتصال (مثل الأصدقاء) كقنوات، بقناة لكل مجموعة. وتبيّن أن التعامل معها صعب للغاية على مطوّري العملاء. فعمليات مثل الحصول على قائمة المجموعات التي تنتمي إليها جهة اتصال تتطلّب قدرًا كبيرًا من الشيفرة داخل العميل. فضلًا عن ذلك، ولأن المعلومات كانت متاحة عبر القنوات وحدها، لم يكن بالإمكان نشر خصائص مثل مجموعات جهة اتصال أو حالة اشتراكها عبر واجهة Contacts.

استُبدل نوعا القناة هذان لاحقًا بواجهات على الاتصال نفسه، تكشف معلومات قائمة جهات الاتصال بطرق أنفع لكاتّاب العملاء، ومنها حالة اشتراك جهة الاتصال (وهي تعداد)، والمجموعات التي تنتمي إليها، وجهات الاتصال الموجودة في مجموعة. وتشير إشارة إلى حين تكون قائمة جهات الاتصال قد جُهِّزت.

20.3.3. طلب القنوات وخصائصها وتوزيعها

تُطلب القنوات باستخدام خريطة من الخصائص التي تريد أن تتّصف بها القناة المطلوبة. وعادةً ما يتضمّن طلب القناة نوع القناة، ونوع مُعرِّف الهدف (جهة اتصال أو غرفة)، والهدف نفسه. غير أن طلب القناة قد يتضمّن أيضًا خصائص مثل اسم الملف وحجمه في عمليات نقل الملفات، أو ما إذا كان يجب تضمين الصوت والفيديو مبدئيًا في المكالمات، أو أي القنوات القائمة التي ينبغي ضمّها في مكالمة جماعية، أو أي خادم جهات اتصال يُجرى البحث عنده عن جهة اتصال.

الخصائص في طلب القناة هي خصائص تعرّفها واجهات مواصفة Telepathy، مثل الخاصية ChannelType (الجدول 20.2). وهي مُنقَّطة بنطاق اسم الواجهة التي جاءت منها. أما الخصائص التي يمكن تضمينها في طلبات القنوات فهي موسومة بأنها قابلة للطلب (requestable) في مواصفة Telepathy.

الخاصية القيمة
ofdT.Channel.ChannelType ofdT.Channel.Type.Text
ofdT.Channel.TargetHandleType Handle_Type_Contact (1)
ofdT.Channel.TargetID escher@tuxedo.cat

الجدول 20.2: أمثلة على طلبات القنوات

يطلب المثال الأكثر تعقيدًا في الجدول 20.3 قناة نقل ملف. ولاحظ كيف تكون الخصائص المطلوبة مُنقَّطة بالواجهة التي جاءت منها. (ولأجل الاختصار، لا تُعرض كل الخصائص المطلوبة.)

الخاصية القيمة
ofdT.Channel.ChannelType ofdT.Channel.Type.FileTransfer
ofdT.Channel.TargetHandleType Handle_Type_Contact (1)
ofdT.Channel.TargetID escher@tuxedo.cat
ofdT.Channel.Type.FileTransfer.Filename meow.jpg
ofdT.Channel.Type.FileTransfer.ContentType image/jpeg

الجدول 20.3: طلب قناة نقل ملف

يمكن إمّا إنشاء القنوات أو تأمينها. وتأمين القناة يعني إنشاءها فقط إذا لم تكن موجودة أصلًا. أما طلب إنشاء قناة فسيؤدي إمّا إلى إنشاء قناة جديدة تمامًا منفصلة، أو إلى توليد خطأ إذا تعذّر وجود نسخ متعددة من تلك القناة. وعادةً ما ترغب في تأمين القنوات النصية والمكالمات (أي أنك تحتاج محادثة واحدة مفتوحة فقط مع شخص ما، بل إن كثيرًا من البروتوكولات لا تدعم محادثات منفصلة متعددة مع جهة الاتصال نفسها)، بينما ترغب في إنشاء قنوات نقل الملفات والقنوات ذات الحالة.

تُعلَن القنوات المُنشأة حديثًا (السواء كانت مطلوبة أو غير ذلك) عبر إشارة من الاتصال. وتتضمّن هذه الإشارة خريطة من خصائص القناة الثابتة (immutable). وهذه هي الخصائص المضمون ألّا تتغيّر طوال عمر القناة. أما الخصائص التي تُعدّ ثابتة فهي موسومة كذلك في مواصفة Telepathy، لكنها عادةً ما تشمل نوع القناة، ونوع مُعرِّف الهدف، والهدف، والبادئ (من أنشأ القناة)، وواجهاتها. أما خصائص مثل حالة القناة فلا تتضمّنها بطبيعة الحال.

الطريقة القديمة لطلب القنوات

كانت القنوات في الأصل تُطلب ببساطة بالنوع، ونوع المُعرِّف، ومُعرِّف الهدف.

لم تكن هذه الطريقة مرنة بما يكفي، إذ لا تملك كل القنوات هدفًا (مثل قنوات البحث عن جهات الاتصال)، وبعض القنوات تتطلّب معلومات إضافية تُدرَج في طلب القناة الأوّلي (مثل نقل الملفات، وطلب الرسائل الصوتية، وقنوات رسائل SMS).

كما اكتُشف أيضًا أنه قد يُرغَب في سلوكين مختلفين عند طلب قناة (إمّا إنشاء قناة مضمونفرادها، أو ببساطة ضمان وجود قناة)، وإلى ذلك الحين كان الاتصال هو المسؤول عن تحديد أيّهما سيحدث. ومن ثم حُلّت الطريقة القديمة بالطرق الأحدث، والأكثر مرونةً، والأوضح.

إعادة خصائص القناة الثابتة عند إنشاء القناة أو تأمينها تجعل إنشاء كائن وكيل للقناة أسرع بكثير. وهذه معلومات لم نضطر إلى طلبها بعد الآن. وتبيّن الخريطة في الجدول 20.4 الخصائص الثابتة التي قد تُدرَج عندما نطلب قناة نصية (أي باستخدام طلب القناة في الجدول 20.3). وقد استُبعدت بعض الخصائص (ومنها TargetHandle وInitiatorHandle) لأجل الاختصار.

الخاصية القيمة
ofdT.Channel.ChannelType Channel.Type.Text
ofdT.Channel.Interfaces {[} Channel.Interface.Messages, Channel.Interface.Destroyable, Channel.Interface.ChatState {]}
ofdT.Channel.TargetHandleType Handle_Type_Contact (1)
ofdT.Channel.TargetID escher@tuxedo.cat
ofdT.Channel.InitiatorID danielle.madeley@collabora.co.uk
ofdT.Channel.Requested True
ofdT.Channel.Interface.Messages.SupportedContentTypes {[} text/html, text/plain {]}

الجدول 20.4: مثال على الخصائص الثابتة التي تعيدها قناة جديدة

يطلب البرنامج الطالب عادةً قناة من موزّع القنوات، معطّيًا الحساب الذي يخصّه الطلب، وطلب القناة، واختياريًا اسم المُعالِج المطلوب (وهذا مفيد إن كان البرنامج يريد معالجة القناة بنفسه). وتمرير اسم حساب بدلًا من اتصال يعني أن بوسع موزّع القنوات أن يطلب من مدير الحسابات إحضار الحساب إلى حالة الاتصال إذا لزم الأمر.

بعد اكتمال الطلب، إمّا يسلّم موزّع القنوات القناة إلى المُعالِج المسمّى بالاسم، أو يبحث عن مُعالِج مناسب (انظر المناقشة أدناه عن المُعالِجات والعملاء الآخرين). ويجعل اسم المُعالِج المطلوب اختياريًا من الممكن للبرامج التي لا تهتم بقنوات الاتصال سوى الطلب الأوّلي أن تطلب قنوات وتُعالَج بواسطة أفضل برنامج متاح (مثل إطلاق محادثة نصية من عميل البريد لديك).

طلب القناة وتوزيعها

الشكل 20.4: طلب القناة وتوزيعها

يُجري البرنامج الطالب طلب قناة إلى موزّع القنوات، الذي يمرّر الطلب بدوره إلى الاتصال المناسب. ويُصدر الاتصال إشارة NewChannels التي يلتقطها موزّع القنوات، ثم يبحث عن العميل المناسب لمعالجة القناة. أما القنوات الواردة التي لم يُطلبها أحد فتُوزَّع بنحو مشابه جدًّا، عبر إشارة من الاتصال يلتقطها موزّع القنوات، لكن من دون الطلب الأوّلي من البرنامج بطبيعة الحال.

20.3.4. العملاء

يعالج العملاء قنوات الاتصال الواردة والصادرة أو يراقبونها. والعميل هو أي شيء مسجَّل لدى موزّع القنوات. وهناك ثلاثة أنواع من العملاء (مع أن عميلًا واحدًا قد يكون نوعين أو الأنواع الثلاثة جميعًا إن رغب المطوّر في ذلك):

  • المراقبون (Observers): يراقبون القنوات دون التفاعل معها. ويميل استخدام المراقبين إلى تسجيل المحادثات والنشاط (مثل مكالمات VoIP الواردة والصادرة).
  • المعتمدون (Approvers): مسؤولون عن إتاحة فرصة للمستخدم ليقبل قناة واردة أو يرفضها.
  • المعالجون (Handlers): يتفاعلون فعليًا مع القناة. وقد يكون ذلك الإقرار بالرسائل النصية وإرسالها، أو إرسال ملف أو استلامه، إلخ. ويميل المُعالِج إلى الارتباط بواجهة مستخدم.

تقدّم العملاء خدمات D-Bus بما يصل إلى ثلاث واجهات: Client.Observer وClient.Approver وClient.Handler. وتوفّر كل واجهة أسلوبًا يستطيع موزّع القنوات استدعاؤه لإعلام العميل بقناة يجب مراقبتها أو اعتمادها أو معالجتها.

يوزّع موزّع القنوات القناة على كل مجموعة من العملاء على التوالي. أولًا، تُوزَّع القناة على جميع المراقبين المناسبين. وبعد أن يعودوا جميعًا، تُوزَّع القناة على جميع المعتمدين المناسبين. وبعد أن يعتمد أول معتمد القناة أو يرفضها، تُبلَّغ بقية المعتمدين، ثم تُوزَّع القناة في النهاية على المُعالِج. ويتم التوزيع على مراحل لأن المراقبين قد يحتاجون إلى وقت للإعداد قبل أن يبدأ المُعالِج في تعديل القناة.

يكشف العملاء خاصية مرشِّح القناة، وهي قائمة مرشِّحات يقرأها موزّع القنوات ليعرف أنواع القنوات التي يهتم بها العميل. ويجب أن يتضمّن المرشِّح على الأقل نوع القناة، ونوع مُعرِّف الهدف (جهة اتصال أو غرفة) الذي يهتم به العميل، لكنه قد يحوي خصائص إضافية. ويجري المطابقة مقابل الخصائص الثابتة للقناة، باستخدام المساواة البسيطة للمقارنة. والمرشِّح في الجدول 20.5 يطابق كل القنوات النصية الثنائية.

الخاصية القيمة
ofdT.Channel.ChannelType Channel.Type.Text
ofdT.Channel.TargetHandleType Handle_Type_Contact (1)

الجدول 20.5: مثال على مرشِّح قناة

يمكن اكتشاف العملاء عبر D-Bus لأنهم ينشرون خدمات تبدأ بالاسم المعروف ofdT.Client (مثل ofdT.Client.Empathy.Chat). ويمكنهم كذلك، اختياريًا، تثبيت ملف يقرأه موزّع القنوات ويحدّد مرشِّحات القنوات. ويتيح ذلك لموزّع القنوات أن يشغّل عميلًا إن لم يكن قيد التشغيل أصلًا. ويجعل اكتشاف العملاء بهذه الطريقة اختيار واجهة المستخدم قابلًا للتهيئة والتغيير في أي وقت دون الحاجة إلى استبدال أي جزء آخر من Telepathy.

الكل أو لا شيء

من الممكن توفير مرشِّح يشير إلى أنك مهتم بكل القنوات، لكن عمليًا لا يكون ذلك مفيدًا إلا كمثال على مراقبة القنوات. فالعملاء الحقيقيون يحتويون شيفرة خاصة بأنواع القنوات.

ويشير المرشِّح الفارغ إلى أن المُعالِج غير مهتم بأي نوع من أنواع القنوات. غير أنه ما زال من الممكن توزيع قناة على هذا المُعالِج إن فعلت ذلك بالاسم. وتستخدم المُعالِجات المؤقتة التي تُنشأ عند الحاجة لمعالجة قناة بعينها مرشِّحًا من هذا النوع.

20.4. دور روابط اللغات

وبما أن Telepathy واجهة برمجية فوق D-Bus، يمكن قياده بأي لغة برمجة تدعم D-Bus. وروابط اللغات ليست مطلوبة لـ Telepathy، لكنها يمكن أن توفّر طريقة مريحة لاستخدامه.

يمكن تقسيم روابط اللغات إلى مجموعتين: روابط منخفضة المستوى تضمّ شيفرة مولَّدة من المواصفة، وثوابت، وأسماء أساليب، إلخ؛ وروابط عالية المستوى، وهي شيفرة مكتوبة يدويًا تجعل من الأسهل على المبرمجين أداء أمورهم باستخدام Telepathy. ومن أمثلة الروابط عالية المستوى روابط GLib وQt4. ومن أمثلة الروابط منخفضة المستوى روابط Python وروابط libtelepathy C الأصلية، مع أن روابط GLib وQt4 تتضمّن ربطًا منخفض المستوى.

20.4.1. البرمجة اللامتزامنة

داخل روابط اللغات، كل استدعاءات الأساليب التي تُجري طلبات عبر D-Bus لامتزامنة: يُرسل الطلب، ثم يأتي الرد في دالة استدعاء (callback). وهذا ضروري لأن D-Bus نفسه لامتزامني."

وكما في معظم برمجة الشبكات وواجهات المستخدم، يتطلّب D-Bus استخدام حلقة أحداث (event loop) لتوزيع دوال الاستدعاء الخاصة بالإشارات الواردة وقيم الأساليب العائدة. ويتكامل D-Bus جيدًا مع الحلقة الرئيسة (mainloop) في GLib المستخدَمة بواسطة حزمتي GTK+ وQt.

توفّر بعض روابط D-Bus اللغوية (مثل dbus-glib) واجهة برمجية شبه متزامنة (pseudo-synchronous)، حيث تُحجَز الحلقة الرئيسة حتى يعود ردّ الأسلوب. وكان هذا معروضًا في الماضي عبر روابط واجهة telepathy-glib. للأسف، اتضح أن استخدام الواجهة شبه المتزامنة مليء بالمشكلات، وأُزيل في النهاية من telepathy-glib.

لماذا لا تعمل استدعاءات D-Bus شبه المتزامنة

الواجهة شبه المتزامنة التي تقدّمها dbus-glib وروابط D-Bus أخرى منفَّذة عبر تقنية «طلب ثم حجب» (request-and-block). وأثناء الحجب، لا يُستقصى إلا مقبس D-Bus بحثًا عن إدخال/إخراج جديد، وتُصفّ أي رسائل D-Bus ليست ردًّا على الطلب في طابور لمعالجتها لاحقًا.

وهذا يسبّب عدة مشكلات جوهرية لا مفرّ منها:

  • يبقى المُنادي محجوبًا أثناء انتظاره إجابة الطلب. وهو (وواجهته للمستخدم، إن وُجدت) لن يستجيب إطلاقًا. فإذا كان الطلب يتطلب الوصول إلى الشبكة فإن ذلك يستغرق وقتًا؛ وإذا كان المُندعى قد تجمّد فسيبقى المُنادي بلا استجابة حتى تنتهي مهلة الاستدعاء. والخيوط ليست حلًّا هنا لأن الخيوط ليست سوى طريقة أخرى لجعل استدعاءاتك لامتزامنة. وبدلًا من ذلك يمكنك أن تجعل الاستدعاءات لامتزامنة حيث تصل الاستجابات عبر حلقة الأحداث القائمة أصلًا.
  • قد يُعاد ترتيب الرسائل. فأي رسائل تُستقبل قبل الرد الذي ننتظره ستوضع في طابور وتُسلَّم للعميل بعد الرد. ويؤدّي هذا إلى مشكلات في الحالات التي تُستقبل فيها إشارة تشير إلى تغيّر في الحالة (أي أن الكائن قد دُمِّر) بعد فشل استدعاء الأسلوب على ذلك الكائن (أي باستثناء UnknownMethod). وفي هذه الحالة يصعب معرفة الخطأ الذي يُعرض على المستخدم. أما إذا استقبلنا الإشارة أولًا، فيمكننا إلغاء استدعاءات D-Bus المعلّقة أو تجاهل استجاباتها.
  • قد تؤدّي استدعاءات شبه حاجبة متبادلة بين عمليتين إلى حالة تعارض مميت (deadlock)، ينتظر كلٌّ منهما استجابة الآخر لسؤاله. وقد يحدث هذا السيناريو مع عمليات تكون في الوقت نفسه خدمة D-Bus وتستدعي خدمات D-Bus أخرى (مثل عملاء Telepathy). فموزّع القنوات يستدعي أساليب على العملاء لتوزيع القنوات، لكن العملاء أيضًا يستدعون أساليب على موزّع القنوات لطلب فتح قنوات جديدة (أو يستدعون بدلًا من ذلك مدير الحسابات، وهو جزء من العملية نفسها).

كانت استدعاءات الأساليب في روابط Telepathy الأولى، المولَّدة بلغة C، تستخدم ببساطة دوال استدعاء معرَّفة بـ typedef. وكان يتطلّب الأمر من دالة الاستدعاء أن تنفّذ توقيع النوع نفسه فحسب.

typedef void (*tp_conn_get_self_handle_reply) (
    DBusGProxy *proxy,
    guint handle,
    GError *error,
    gpointer userdata
);

هذه الفكرة بسيطة، وتعمل مع C، لذا جرى الاستمرار بها في الجيل التالي من الروابط.

وفي السنوات الأخيرة، طوّر الناس وسيلة لاستخدام لغات البرمجة النصية مثل Javascript وPython، فضلًا عن لغة شبيهة بـ C# اسمها Vala، تستخدم واجهات قائمة على GLib/GObject عبر أداة اسمها GObject-Introspection. للأسف، من الصعب الشديد إعادة ربط هذه الأنواع من دوال الاستدعاء إلى لغات أخرى، لذا صُمِّمت الروابط الأحدث للاستفادة من مزايا الاستدعاء اللامتزامن الذي تقدّمه اللغات وGLib.

20.4.2. جاهزية الكائن

في واجهة D-Bus بسيطة، مثل روابط Telepathy منخفضة المستوى، يمكنك أن تبدأ إجراء استدعاءات أساليب أو استقبال إشارات على كائن D-Bus بمجرد إنشاء كائن وكيل له. إنها بسيطة تمامًا: أعطِ مسار كائن واسم واجهة وابدأ.

غير أن في واجهة Telepathy عالية المستوى نريد لكائناتنا الوكيلية أن تعرف الواجهات المتاحة، ونريد استرجاع الخصائص المشتركة لنوع الكائن (مثل نوع القناة والهدف والبادئ)، ونريد تحديد حالة الكائن أو وضعه وتتبّعهما (مثل حالة الاتصال).

وعليه، فإن مفهوم الجاهزية (readiness) موجود لجميع الكائنات الوكيلية. وبإجراء استدعاء أسلوب على كائن وكيل، تستطيع أن تسترجع حالة ذلك الكائن بصورة لامتزامنة، وأن تُبلَّغ حين تُسترجع الحالة ويصبح الكائن جاهزًا للاستخدام.

ولأن العملاء ليسوا جميعًا من ينفّذ كل ميزات كائن معيّن أو يهتمّ بها، فإن جاهزية نوع كائن ما مفصولة إلى عدد من الميزات الممكنة. وينفّذ كل كائن ميزة أساسية (core) تحضّر المعلومات الجوهرية عن الكائن (أي الخاصية Interfaces والحالة الأساسية)، إضافةً إلى عدد من الميزات الاختيارية لحالة إضافية، قد تتضمّن خصائص أو تتبّع حالة إضافيين. ومن الأمثلة الملموسة للميزات الإضافية التي يمكنك تجهيزها على وكالات مختلفة معلومات جهة الاتصال، والقدرات، ومعلومات تحديد الموقع الجغرافي، وحالات المحادثة (مثل «جست تكتب…») وصور المستخدمين الرمزية.

ومثال على ذلك، تتوفّر لكائنات الاتصال الوكيلية:

  • ميزة أساسية تسترجع الواجهة وحالة الاتصال؛
  • ميزات تسترجع أصناف القنوات القابلة للطلب وتدعم معلومات جهة الاتصال؛ و
  • ميزة لإنشاء الاتصال وإرجاع حالة «جاهز» عند الاتصال.

يطلب المبرمج تجهيز الكائن، معطّيًا قائمة الميزات التي يهمّه ودالة استدعاء تُستدعى حين تصبح كل تلك الميزات جاهزة. فإذا كانت كل الميزات جاهزة أصلًا فيمكن استدعاء الدالة فورًا، وإلا فستُستدعى بعد استرجاع كل المعلومات الخاصة بتلك الميزات.

20.5. المتانة

تتمثّل إحدى المزايا الجوهرية لـ Telepathy في متانتها. والمكوّنات معيارية، لذا فإن انهيار مكوّن واحد لا ينبغي أن يُسقط النظام بأكمله. وفيما يلي بعض الميزات التي تجعل Telepathy متينًا:

  • يستطيع مدير الحسابات وموزّع القنوات استعادة حالتيهما. فعند بدء Mission Control (العملية الواحدة التي تضمّ مدير الحسابات وموزّع القنوات)، يطّلع على أسماء الخدمات المسجَّلة حاليًا على ناقل جلسة المستخدم. وأي اتصالات يعثر عليها مرتبطة بحساب معروف تُعاد ربطها بذلك الحساب (بدلًا من إنشاء اتصال جديد)، وتُسأل العملاء قيد التشغيل عن قائمة القنوات التي يعالجونها.
  • إذا اختفى عميل بينما كانت قناته التي يعالجها مفتوحة، فإن موزّع القنوات سيُعيد تشغيله ويُصدر القناة من جديد. وإذا تعطّل عميل مرارًا، فييمكن لموزّع القنوات أن يحاول تشغيل عميل مختلف إن توفّر، وإلا فسيُغلق القناة (لمنع العميل من التعطّل مرارًا بسبب بيانات لا يستطيع معالجتها). وتحتاج الرسائل النصية إلى إقرار قبل أن تختفي من قائمة الرسائل المعلّقة. ولا يُقصد بالعميل أن يقرّ برسالة إلا حين يتأكّد أن المستخدم قد رآها (أي عرض الرسالة في نافذة مركَّز عليها). وبهذه الطريقة، إذا تعطّل العميل أثناء محاولة عرض الرسالة، فستظل القناة تملك الرسالة التي لم تُعرض سابقًا في طابور الرسائل المعلّقة.
  • إذا انهار اتصال، فإن مدير الحسابات سيُعيد تشغيله. ومن البديهي أن يُفقد محتوى أي قنوات ذات حالة، لكنه لن يؤثّر إلا في الاتصالات التي تعمل داخل تلك العملية دون غيرها. ويستطيع العملاء مراقبة حالة الاتصالات ببساطة، وإعادة طلب معلومات مثل قائمة جهات الاتصال وأي قنوات عديمة الحالة.

20.6. توسيع Telepathy: السيارات الجانبية

رغم أن مواصفة Telepathy تحاول تغطية مجموعة واسعة من الميزات التي تُصدِّرها بروتوكولات الاتصال، فإن بعض البروتوكولات قابلة للتوسيع هي نفسها[4]. وأراد مطوّرو Telepathy جعل بالإمكان توسيع اتصالات Telepathy الخاصة بك للاستفادة من تلك الامتدادات دون الحاجة إلى توسيع مواصفة Telepathy نفسها. ويتحقّق ذلك من خلال ما يُعرف بـ السيارات الجانبية (sidecars).

تُنفَّذ السيارات الجانبية عادةً عبر إضافات (plugins) داخل مدير الاتصال. ويستدعي العملاء أسلوبًا يطلب سيارة جانبية تنفّذ واجهة D-Bus بعينها. فمثلًا، قد ينفّذ تنفيذيك لقوائم الخصوصية XEP-0016 واجهة اسمها com.example.PrivacyLists. ويعيد الأسلوب بعد ذلك كائن D-Bus يقدّمه الإضافة، وينفّذ تلك الواجهة (وربما غيرها). ويعيش الكائن إلى جانب كائن الاتصال الرئيسي (ولهذا جاء الاسم sidecar، كما في السيارة النارية).

تاريخ السيارات الجانبية

في الأيام الأولى لـ Telepathy، احتاج مشروع حاسوب محمول لكل طفل (One Laptop Per Child) إلى دعم امتدادات XMPP المخصّصة (XEPs) لمشاركة المعلومات بين الأجهزة. وقد أُضيفت هذه الامتدادات مباشرةً إلى Telepathy-Gabble (مدير الاتصال الخاص بـ XMPP)، وكُشفت عبر واجهات غير موثّقة على كائن الاتصال. ونظرًا لأن مزيدًا من المطوّرين أرادوا دعم XEPs بعينها لا نظير لها في بروتوكولات الاتصال الأخرى، اتُّفق على الحاجة إلى واجهة أكثر عمومية للإضافات.

20.7. لمحة سريعة داخل مدير الاتصال

تُكتب معظم مديرات الاتصال باستخدام ربط اللغة C/GLib، وقد طُوّرت عدد من الأصناف الأساسية عالية المستوى لتبسيط كتابة مدير الاتصال. وكما ناقشنا سابقًا، تُنشر كائنات D-Bus انطلاقًا من كائنات برمجية تنفّذ عددًا من الواجهات البرمجية التي تقابل واجهات D-Bus. ويوفّر Telepathy-GLib كائنات أساسية لتنفيذ كائنات مدير الاتصال والاتصال والقناة. كما يوفّر واجهة لتنفيذ مدير قناة (Channel Manager). ومديري القنوات مصانع (factories) يمكن لـ BaseConnection استخدامها لإنشاء كائنات القنوات وإدارتها لنشرها على الناقل.

كما تقدّم الروابط ما يُعرف بـ المزيجات (mixins). ويمكن إضافتها إلى صنف لتوفير وظائف إضافية، وتجريد واجهة المواصفة البرمجية، وتوفير التوافق الخلفي بين النسخ الجديدة والمهملة من الواجهة عبر آلية واحدة. وأكثر المزيجات استخدامًا هو مزيج يضيف واجهة خصائص D-Bus إلى كائن. وهناك أيضًا مزيجات لتنفيذ واجهتَي ofdT.Connection.Interface.Contacts وofdT.Channel.Interface.Group، ومزيجات تجعل من الممكن تنفيذ واجهات الحضور القديمة والجديدة، وواجهات الرسائل النصية القديمة والجديدة عبر مجموعة واحدة من الأساليب.

مثال معمارية مدير الاتصال

الشكل 20.5: مثال على معمارية مدير الاتصال

استخدام المزيجات لحل أخطاء واجهة البرمجة: أحد المواضع التي استُخدمت فيها المزيجات لحل خطأ في مواصفة Telepathy هو TpPresenceMixin. فالواجهة الأصلية التي كشفها Telepathy (odfT.Connection.Interface.Presence) كانت معقّدة إلى حدٍّ لا يُصدَّق، يصعب تنفيذها على كلٍّ من الاتصالات والعملاء، وكانت تكشف وظائف غير موجودة في معظم بروتوكولات الاتصال، ونادرة الاستخدام جدًا في غيرها. وقد استُبدلت الواجهة بواجهة أبسط كثيرًا (odfT.Connection.Interface.SimplePresence)، كشفت كل الوظائف التي كان المستخدمون يهتمّون بها والتي نُفِّذت بالفعل في مديري الاتصال.

ونفّذ مزيج الحضور الواجهتين معًا على الاتصال كي يستمر العملاء القدامى في العمل، لكن على مستوى وظائف الواجهة الأبسط فقط.

20.8. الدروس المستفادة

Telepathy مثال ممتاز على كيفية بناء واجهة برمجية معيارية ومرنة فوق D-Bus. وهو يوضّح كيف يمكنك تطوير إطار عمل قابل للتوسيع ومفكوك الارتباط فوق D-Bus، إطار لا يحتاج إلى برنامج خفي مركزي للإدارة ويتيح إعادة تشغيل مكوّناته دون فقدان بيانات في أي مكوّن آخر. ويوضّح Telepathy أيضًا كيف يمكنك استخدام D-Bus بكفاءة وفاعلية، مع تقليل حجم البيانات التي ترسلها على الناقل إلى أدنى حدّ ممكن.

كان تطوير Telepathy متكرّرًا وتحسّن، إذ تحسّن استخدامه لـ D-Bus بمرور الوقت. وقد وقعت أخطاء ودُرست دروس. وفيما يلي بعض الأمور المهمة التي تعلّمناها عند تصميم معمارية Telepathy:

  • استخدم خصائص D-Bus؛ ولا تتطلّب عشرات استدعاءات D-Bus الصغيرة للبحث عن المعلومات. لكل استدعاء أسلوب زمن ذهاب وإياب. وبدلًا من إجراء استدعاءات فردية كثيرة (مثل GetHandle وGetChannelType وGetInterfaces)، استخدم خصائص D-Bus وأعِد كل المعلومات عبر استدعاء واحد لـ GetAll.
  • قدّم أكبر قدر ممكن من المعلومات عند الإعلان عن كائنات جديدة. كان أول ما يفعله العملاء حين يعلمون بكائن جديد هو طلب كل خصائصه ليتأكّدوا ما إذا كانوا مهتمين به أصلًا. وبتضمين الخصائص الثابتة للكائن في الإشارة التي تعلنه، يستطيع معظم العملاء تحديد اهتمامهم به دون إجراء أي استدعاءات أساليب. فضلًا عن ذلك، إن كانوا مهتمين به فلا يضطرون إلى طلب أي من خصائصه الثابتة.
  • تتيح واجهة Contacts طلب المعلومات من واجهات متعددة دفعة واحدة. وبدلًا من إجراء استدعاءات GetAll عديدة لاسترجاع كل معلومات جهة الاتصال، تتيح لنا واجهة Contacts طلب كل المعلومات دفعة واحدة، مما يوفّر عددًا من رحلات الذهاب والإياب على D-Bus.
  • لا تستخدم تجريدات لا تناسب تمامًا. بدا كشف قائمة جهات الاتصال ومجموعات جهات الاتصال في صورة قنوات تنفّذ واجهة Group فكرةً جيدة لأنها استخدمت تجريدات قائمة بدلًا من إنشاء واجهات إضافية. غير أنها جعلت تنفيذ العملاء صعبًا، ولم تكن مناسبة في النهاية.
  • تأكّد من أن واجهتك البرمجية ستلبّي احتياجاتك المستقبلية. كانت واجهة طلب القنوات الأصلية جامدة تمامًا، ولا تسمح إلّا بأكثر الطلبات الأساسية. ولم تلبِّ احتياجاتنا حين احتجنا إلى طلب قنوات تتطلّب معلومات إضافية. وقد اضطررنا إلى استبدال هذه الواجهة بأخرى ذات مرونة أعلى بكثير."

الحواشي

  1. http://telepathy.freedesktop.org/، أو راجع دليل المطوّرين في http://telepathy.freedesktop.org/doc/book/
  2. http://telepathy.freedesktop.org/spec/
  3. من هنا فصاعدًا، ستُختصر التعبارتان /org/freedesktop/Telepathy/ وorg.freedesktop.Telepathy إلى ofdT اختصارًا للطول.
  4. مثلًا، بروتوكول الرسائل والحضور القابل للتوسّع (XMPP).

  1. http://telepathy.freedesktop.org/، أو انظر دليل المطوّرين في http://telepathy.freedesktop.org/doc/book/ ↩︎

  2. http://telepathy.freedesktop.org/spec/ ↩︎

  3. من هنا فصاعدًا، ستُختصر التعبارتان /org/freedesktop/Telepathy/ وorg.freedesktop.Telepathy إلى ofdT اختصارًا للطول. ↩︎

  4. مثلًا، بروتوكول الرسائل والحضور القابل للتوسّع (XMPP). ↩︎