پس از آشنا شدن با مراحل ساخت یک پروژه ی ساده در دو IDE مشهورِ Eclipse و Netbeans، اینک نوبت به آن میرسد که دست به کُد شویم! :) طبق روال جا افتاده در دنیای برنامه نویسی، کار خود را با برنامهی Hello World در جاواکارت آغاز میکنیم. همهی کاری که این اپلت انجام میدهد این است که در پاسخ به هر دستوری که به کارت ارسال میشود، عبارت "Hello World" را بازگردانی میکند.
سلام دنیا!
کار خود را، با افزودن خطوطی به برنامهی قالبی ای که Eclipse یا Netbeans در مراحل قبل در اختیار ما قرار دادند آغاز میکنیم. با فرض این که شما در مراحل ایجاد پروژه، اسم پروژه را "HelloWorld" و اسم پکیج را "helloWorldPackage" انتخاب کرده باشید، پس از حذف Commentهای اتوماتیکی که IDE اضافه میکند، تکه کدی، مشابه کد زیر خواهید داشت:
همانطور که در تصویر بالا مشاهده می کنید، برنامه به سه قسمت کلی تقسیم شده است. خط اول، نام Package میباشد. قبلا نیز اشاره کرده بودیم که حتما به Package اپلت خود،یک نام اختصاص دهید و هیچگاه فیلد آن را خالی مگذارید، چرا که خالی گذاشتن این فیلد، خصوصا در محیط Eclipse، هنگام Compile برنامه، خطاهایی عجیبی نمایان میکند! پکیج در واقع، فایلی است که درون آن اپلت قرار گرفته است، و بسته به نوع کارت، پس از نصب اپلت میتوان نام پکیج را نیز در کارت مشاهده کرد.
قسمت دوم که شامل یک مجموعه خطوط است که با کلمهی import شروع شده اند، قسمت افزودن Libraryهای ضروری به برنامه است. با توجه به کارکردی که یک برنامهی جاواکارت قرار است داشته باشد، اوراکل، کتابخانههای مختلفی تهیه کرده است و همراه JCDK در اختیار برنامهنویس قرار داده است. تابع های این کتابخانهها(ماژول ها)، با توجه به ارتباط و کاربردشان به دستههای مختلف تقسیم شده اند. برنامه نویس هنگامی که نیاز به استفاده از یک تابع دارد، ابتدا باید ماژولی که این تابع در آن قرار دارد را import کند. البته IDE به صورت خودکار، در صورتی که شما تابعی استفاده کنید که ماژول آن import نشده است، با نمایش یک خطا شما را آگاه میسازد و پیشنهاد import کردن آن ماژول را میدهد. اصلی ترین ماژول ها و تابع ها که به صورت پیش فرض توسط IDE در آغاز ساخت پروژه، import می شوند، ماژول های زیرمجموعه ی javacard.framework میباشند.
و اما قسمت سوم و قسمت اساسی اپلت، همان "کلاس" آن است.
همانند زبان جاوا، در زبان جاواکارت نیز، هر اپلت متشکل از یک کلاس است که توابع ما (Methods/Functions) داخل این کلاس قرار میگیرند. یکی از نکات حائز اهمیتی که اپلتهای جاواکارت را از برنامههای نوشته شده به زبان جاوا متمایز میکند این است که، همهی اپلتهای جاواکارت باید از کلاس Applet مشتق بشوند (به عبارت دیگر همگی باید فرزند کلاس Applet باشند- لطفا این کلاس را با نام برنامه های جاواکارت اشتباه نگیرید!). کلاس Applet، یکی از کلاس های موجود در کتابخانهی javacard.frameword است که ما در ابتدای برنامه import کردیم. این عمل مشتق شدن(به ارث بردن) با عبارت extends Applet در خط شمارهی 5 برنامه انجام شده است.
در زبان جاوا/جاواکارت و سایر زبانهای برنامه نویسی شیء گرا، وقتی یک کلاس از کلاس دیگری مشتق میشود، دو اتفاق میافتد:
- به کلاس فرزند(کلاس که به ارث میبرد=کلاسی که قبل از کلمهی extends میآید)، فیلدها (Fields = Variables = متغیرها) و متدهای کلاس والد اضافه میشوند (بجز آنچه که در کلاس والد از نوع Private تعریف شده است) و برنامه نویس میتواند از آنها استفاده کند.
- برنامه نویس مجبور به پیاده سازی متدهای Abstract کلاس والد میشود.(متدی که در کلاس والد تنها prototype آن[= خط تعریف] نوشته شده است و پیاده سازی آن به کلاس های به ارث برنده واگذار شده است).
در مورد کلاس Applet نیز به همین صورت است. با مشتق شدن اپلت ما از این کلاس، مجبور میشویم متد های Abstract آن را پیاده سازی کنیم. تنها متد Abstract این کلاس، متد process است(خط 16). پیش از آن که به توصیف عملکرد این کلاس بپردازیم تعریف کلّی دو متد پیشین، یعنی install و helloWorld و ذکر یک مقدمه خالی از فایده نیست.
فرض کنیم که اپلت خود را تکمیل کرده ایم و قصد داریم روی کارت از آن استفاده کنیم. برای این منظور، نیاز به انجام دو مرحله داریم:
- بارگذاری پکیج و اپلت درون آن (بله، یک یا چند اپلت، درون یک پکیج قرار میگیرند و روی کارت بارگذاری میشوند) روی کارت.
- نصب اپلت بارگذاری شده.
از آنجا که به انجام دو مرحلهی بالا در اصطلاح Install کردن اپلت گفته میشود، برای تمییز آنها از یکدیگر، از دو عبارت Install for Load و Install for Install استفاده میشود.
نکتهی بعدی این است که، ما میتوانیم چند بار اپلت خود را با نامهای مختلف(یعنی AIDهای مختلف) روی کارت نصب کنیم (مشابه این که چند بار Microsoft Office را با نامهای مختلف در آدرس های مختلف رایانه نصب کنیم). برای این منظور، تنها یکبار عملیات Install for Load را انجام میدهیم و پس از آن عملیات Install for Install را به تعداد دلخواه تکرار میکنیم.
اتفاقی که در علمیات Install for Load میافتد این است که فایل cap. از داخل رایانه به داخل حافظهی کارت منتقل شده و آنجا Extract میشود. سپس AID پکیج و اپلت درون آن در جدول محتویات کارت قرار ثبت میشود.
و اتفاقی که در عملیات Install for Install رخ میدهد این است که متد install داخل اپلت (خط شمارهی 8) توسط Java Card Runtime Environment (با کمی اغماض میتوانید به جای JCRE بخوانید Card Manager) فراخوانی میشود. نکتهی حائز اهمیت در مورد این متد این است که، برای هر اپلت نصب شده این متد یکبار و فقط یکبار، آن هم فقط و فقط توسط JCRE فراخوانی میشود. در واقع هیچ موجودیت(اپلت) دیگری روی کارت، دسترسی کافی برای فراخوانی آن را ندارد.
در مورد پارامترهایی که جلوی متد install به عنوان ورودی این تابع معرفی شده اند، به این توصیف بسنده میکنیم که این پارامترها، همان AID اپلت در حال نصب میباشند. یعنی، وقتی قرار است اپلت بارگذاری شده را چند بار نصب کنیم، این متد را با ورودی های مختلف که همان AIDهای مختلف هستند، فراخوانی میکنیم. در آیندهی این کار را به صورت عملی خواهیم دید.
متد بعدی که نام آن باید با نام اپلت یکی باشد، اصطلاحا Instructor یا سازنده نامیده میشود. کلیهی برنامههای نوشته شده به زبان جاوا/جاواکارت نیازمند وجود این متدهای Instructor میباشند. متد register که درون این متد فراخوانی شده است، وظیفهی ثبت AID اپلت در Registry Table کارت را دارد. (جدولی که لیست AIDهای نصب شده روی کارت را در بر دارد).
و در نهایت، نوبت به سومین متد، یعنی متد process است. تقریبا، همهی آنچه ما با آن کار داریم این متد است. اما چرا؟
پاسخ:
پس از آن که اپلت ما با موفقیت هر دو مرحلهی Install for Load و Install for Install را پشت سر گذاشت، AID مربوط به آن، درون جدول رجیستری JCRE ثبت میشود. حال برای ارتباط با این اپلت نصب شده ما به این صورت عمل میکنیم که ابتداءََ با یک دستور(دستور SELECT)، به Card Manager اطلاع میدهیم که قصد ارتباط به فلان اپلت را داریم. Card Manager پس از این که وجود آن اپلت را بررسی کرد، با یک جواب مشخص، امکان ارتباط یا عدم امکان ارتباط را به ما گزارش میدهد. [این دستورها و این پاسخها، همانطور که پیشتر گفته ایم، APDU Command و APDU Response نامیده میشوند و متشکل از یک سری عدد هگزادسیمال هستند].
در صورتی که جواب Card Manager به ما، حاکی از وجود امکان ارتباط با اپلت مورد نظرمان بود، شروع به ارسال دستورات دلخواهمان به کارت میکنیم. چیزی که در پس این ارتباط رخ میدهد این است که:
- Card Manager این دستورات را از ما دریافت میکند و داخل یک بافر به نام APDU Buffer قرار میدهد.
- بررسی میکند که آیا این دستور، دستور SELECT اپلت دیگری نباشد.
- بافر را به عنوان پارامتر، به متد process اپلتی که انتخاب شده است ارسال میکند و منتظر دریافت پاسخ از وی میماند.
- اپلت ما، پس از خواندن دستور دریافت شده درون بافر، پردازش های خود را انجام داده و پاسخ خود را مجدد در APDU Buffer قرار میدهد و آن را به Card Manager باز میگرداند.
- Card Manager پاسخ دریافت کرده از متد process را به ما باز پس میدهد.
- بازگشت به مرحلهی 1
همانطور که مشاهده میکنید، ارتباط ما با اپلت نصب شده روی کارت، همواره با یک واسطهی Card Manager است. ارتباط با یک اپلت، تا زمانی برقرار است که یا دستور SELECT دیگری ارسال کنیم یا این که کارت را از کارتخوان خارج کنیم. در صورتی که در مرحلهی 2، کارتخوان متوجه دستور SELECT شود، به جای رفتن به مرحلهی 3، اپلت فعلی را از انتخاب خارج میکند و در جدول رجیستری، به دنبال اپلت جدید میگردد و ...
بنابرین، کاربرد متد process نیز مشخص شد. همچنین عبارت قرمز شده در مرحلهی 3 بدین معناست که با ارسال هر دستور به کارت، باید انتظار یک پاسخ -هر چند کوتاه- از وی داشت و نمیتوان چند دستور ارسال کرد و یکباره پاسخ گرفت. (چیزی مانند ارتباط یک به یک). نکتهی آخر این که، کارت نمیتواند آغازگر ارتباط باشد، بلکه تمام وقت، به عنوان Slave در یک ارتباط Master-Slave منتظر دریافت دستور است.
و اما برنامهی HelloWorld!
تا اینجای کار، نسبت به اتفاقاتی که حین ارتباط با اپلت رخ میدهد آشنا شدیم و اینک نوبت آن است که به کد قالبی ارائه شده توسط IDE خطوطی اضافه کنیم که در پاسخ به دستورات ارسالی ما عبارت "Hello World" را بازگردانی کند. طبق توضیحات بالا، میدانیم قسمت عمده ای از آنچه که باید دستخوش تغییر یا افزودن شود، تابع process است.
ساده ترین حالت برنامهی ما به صورت زیر خواهد بود:
برای آشنایی با توابعی که اضافه کرده ایم، میتوانید به Java Card API Specification (یک سند pdf داخل JCDK) مراجعه کنید. اما به صورت اجمالی، در خطوط 15 و 16 یک آرایه از نوع بایت به نام HELLO_WORLD تعریف کردیم و حروفی که میخواهیم در پاسخ به دستورات دریافتی به کاربر ارسال شود را در آن قرار دادیم. در خط 17، از تابع getbuffer روی شیء apdu استفاده کردیم تا بتوانیم به بافر APDU دست پیدا کنیم. این تابع، در واقع یک آرایهی بایتی از APDU Buffer باز میگرداند، بنابراین با این خط ما متغیر buffer را به APDU Buffer ارجاع دادیم.
در خط 18 با استفاده از تابع arrayCopyNonAtomic محتویات متغیر HELLO_WORLD را درون متغیر buffer کپی کردیم(که منجر به تغییر APDU Buffer میشود) و در آخرین خط برنامه، با استفاده از تابع setOutgoingAndSend بافر APDU را که حاوی پاسخ است، به Card Manager بازگرداندیم که به کاربر بیرون کارت بازگرداند.
هرچند برنامهی فوق به صورت صحیح به فایل cap. تبدیل میشود و به صورت صحیح هم بارگذاری و اجرا میشود، اما دارای نقاط ضعفی است که باید حتما رفع شوند. در قسمت بعدی با رفع این نقاط ضعف و اجرای اپلت نهایی در محیط Simulator آشنا خواهیم شد.
قسمت بعدی: استفاده از Simulator های جاواکارت (به زودی!)
شهر خالیست ز عشّاق، مگر کز طرفی
دستی از غیب برون آید و کاری بکند ...
#حافظ_دوست_داشتنی