יום רביעי, 19 בדצמבר 2012

MyCloud Guide




כולם היום מדברים על עננים אבל לא על אלה שבשמיים, כל חברה בתחום המחשבים וההייטק צורכת או מייצרת שירותים שנגמרים במילה ענן שנהפכה בשנים האחרונות לפופולרית מאוד, בזכות העננים חברות לא צריכות להחזיק שרתים פיזיים והם יכולים לרכוש מחשבים וירטואלים על בסיס צורך ולפי עלות חודשית.

נושא העננים זה לא משהו חדש , חשבו עליו בשנת 1967, מהנדס מ IBM בשם  Gene Amdahl שפיתח את ארכיטקטורת ה Clustering , אבל מימוש Cluster באותה תקופה לא היה אפשרי לכל אחד, נדרשו משאבים רבים ועלויות כבדות, עכשיו אחרי 45 שנה אוטוטו 46 העולם נראה קצת אחרת הטכנולוגיה התקדמה מאוד והדולר נחלש מה שמאפשר לקנות חומרה טובה במחיר טוב ולנצל אותה במלואה בעזרת תוכנות מתוחכמות ומערכות Storage מתקדמות, בעקבות מהפכת הוירטואליזציה העסק הופך לזול וזמין.

Cluster

פעם כשדיברו על Cluster דיברו על סוג של מחשב על , מחשב שמחוברים אליו אלפי מחשבים שתופסים חדר ענק, אם תרשמו ב Google את המילה Cluster תבינו על מה אני מדבר, זה היה עסק מסורבל ויקר שמעטים מבינים בו אבל לאחר ש Linux נכנסה לשוק רשמית בשנת 1991,העסק התחיל לקבל תפנית והיום יש המון פתרונות מבוססי Linux ובחינם.

אז מה זה Cluster?

Cluster (אשכול) זה מספר מחשבים שנותנים שירותים ומתנהגים לפעמים כמחשב אחד,  החיבור בניהם מתבצע בעזרת רשת פנימית והרעיון שיש מחשב שמוגדר כ Master ושאר המחשבים מחוברים אליו כ Slaves , בעזרת שכבה וירטואלית שעוטפת את המחשבים ניתן לייצר כמות אינסופית של תחנות וירטואליות, כאשר רואים שאחד העבדים מתעייף מעבירים את התחנות הוירטואליות לעבד אחר או שמוסיפים עבד חדש.




הפתרונות בשוק מרובות, החברה הפופלרית ביותר בתעשייה היא VmWare שבהחלט פיתחו מערכת יפה, ניתן להוריד גרסה חינמית שאפשר להפעיל על שרת פיזי בודד, אם רוצים יותר ממחשב בודד צריכים לקנות רישיון ופה העסק קצת יקר, קיים פתרון נוסף שנקרא Proxmox שהרישיון שלו יותר גמיש ומאפשר לנו לחבר כמה עבדים שרוצים , התשלום עבור התמיכה בלבד.

נראה לי שחפרתי מספיק, קדימה לעבודה, דבר ראשון יש להיכנס לאתר של Proxmox ולהוריד את הגרסה האחרונה של המוצר Proxmox Ve, לצרוב על דיסק ולהכניס למחשב שאותו אנחנו רוצים להפוך ל Master של ה Cluster שלנו.


ההתקנה מאוד פשוטה, בסיום ההתקנה צריכים להגדיר את הרשת שלנו וזהו, עכשיו נתחיל להגדיר את ה Cluster, ניתן לנהל את ה Cluster דרך אתר מקומי או בעזרת SSH, יש פונקציות מסויימות שניתן לבצע רק דרך ה Console  כמו יצירת Cluster ,הוספת עבדים או מחיקת תחנות וירטואליות.

נכון שמדובר ב Master אבל הוא יכול להתנהג כעבד כמו שאר העבדים וניתן להתקין עליו תחנות וירטואליות למרות שזה לא כל כך מומלץ אחרי הכל הוא ה Master והוא המחשב שמתפעל לנו את כל ה Cluster לכן חשוב לשחרר ממנו משימות מיותרות אז כמו שאמרתי הדרך היחידה לעשות זאת היא דרך SSH.

#:pvecm create <cluster name>
מייצר Cluster על המחשב הנוכחי, מאותו רגע המחשב נהפך ל Master.

#:pvecm add <master address>
לאחר שהגדרנו את ה Master נבנה את העבדים, עושים את אותו תהליך ההתקנה כמו שעשינו עם ה Master רק שכאן אנחנו מצרפים את העבד ל Master.


אחרי שהגדרנו את המחשבים הפיזיים בעזרת ה SSH, מרבית העבודה שלנו תתמקד בממשק ה Web הנחמד שעשו לנו, שם נוכל לעקוב אחרי ה Cluster, לייצר מכונות וירטואליות חדשות , ליזום גיבויים, להעביר תחנות מעבד לעבד ע"פ מצב ה Cluster.


לפני שנעבור לשלב ההתקנות של המכונות הוירטואלית נושא אחסון המידע הוא קריטי וחיוני מאוד ל Cluster לכן צריך להתכונן בהתאם, לצורך הדוגמה יש ברשותי HD חיצוני שמכיל בתוכו כרטיס רשת של חברת WD משהו מאוד פשוט אבל מספיק טוב לדוגמה, לא חובה להשתמש ב Storage חיצוני ניתן להשתמש גם בכוננים שבמכונות הפיזיות.




ב Proxmox קיימות המון דרכים לחבר מערכות אחסון ל Cluster , ניתן להשתמש במערכות NFS, או ISCSI , אני אתמקד בהוספת Directory כ Storage בעזרת פרוטוקול CIFS, גם פה הדרך היחידה לבצע זאת היא דרך ה SSH, ניצור תיקייה חדשה בתיקיית ה Mnt ב Master ונעשה Mount.

#:mkdir /mnt/wdmybook
#:/mount //192.168.1.2/share -t cifs /mnt/wdmybook


בממשק ה Web יש ללכת לתגית Storage וללחוץ על Add -> Directory.



נכניס את הנתיב לתיקייה שעשינו Mount ונבחר איזה סוג של Content ה Storage מאחסן (דיסקים וירטואלים, ISO, גיבויים), אפשר לשלב כמה סוגים של Contents.


על מנת לבנות תחנות וירטואליות צריך קבצי ISO של מערכות ההפעלה, יש ללחוץ על המכונה הרצויה וללחוץ על אחד הכוננים, בתגית Content יש אפשרות להעלות את קבצי ISO לשטח האחסון.

יש 2 סוגי חשבונות במערכת, חשבון ה Root מבוסס על חשבון מסוג Linux PAM standard authentication שמנהל את ה Cluster , מוסיף משתמשים ועבדים למערכת, אבל הוא אינו יכול לגשת למכונות הוירטואליות יש לייצר משתמש חדש עם חשבון מסוג Proxmox VE authentication server ולהכניס לו שם משתמש וסיסמא, אפשר להגדיר גם את ההרשאות של המשתמש.

שלב התקנת המכונות הוירטואליות מאוד דומה לשאר המערכות שקיימות בשוק, בעצם מתחיל Wizard שבו מאפיינים את המכונה, מעבדים,כמות זכרון, כונן קשיח ורשת.

בחירת מערכת ההפעלה.



בחירת ה ISO של ההתקנה שמאוחסן בכונן החיצוני


פה הסיפור קצת מעניין לצורך הדוגמה השתמשתי ב Distribution נחמד של Linux בשם SliTaz, בעצם מדובר על מערכת Linux לכל דבר בגודל 35 Mb בלבד וממשק KDE.

נפח כונן הקשיח.




הגדרת זיכרון.



הגדרות רשת.



זהו, מה שנשאר זה להריץ את המכונה ויש לכם Cluster.




סיכום:

עננים משנים את התפיסה שהתרגלנו להכיר בעשור האחרון, זה תחום חדש שיביא לעולם הרבה חברות חדשות אבל יהרוג חברות ותיקות, כנראה שהעתיד שלנו יהיה בענן ולא יותר אצלנו בבית, בעזרת קווי אינטרנט מהירים והטכנולוגיה עולם המחשבים מתקרב לפריצת דרך רצינית ולמרות שכבר היום ניתן למצוא המון שירותים מבוססי ענן הפוטנציאל הוא אדיר ואנחנו רק בתחילתו של התהליך.

יש עננים?

יום שבת, 1 בדצמבר 2012

Process Network Analyzer .Net


דמיינו מצב שאתם מנהלים שרת אינטרנט וההייתם רוצים לדעת כמה משתמשים יש על השרת באותו רגע, וכמובן באיזה שירותים הם משתמשים, אחת הדרכים הנפוצות לעשות זו היא בעזרת שימוש בכלים קיימים ב Windows כמו ה Task Manager ו NetStat אבל זה תהליך מסורבל ומבלבל (נעבור עליו מיד) , חשבתי לעצמי איך ניתן לכתוב כלי כזה בעצמי ממקום להתחיל להתערבב עם הכלים ולזכור כל מיני מספרים...

השיטה הקלאסית:

נפעיל את חברנו הטוב - Task Manager.


חשוב לזכור שבהגדרות ה Default של טבלת ה Task Manager לא מופיעה העמודה של ה PID - Process Identifier שהיא חשובה לנו בהמשך, על מנת להפעיל את העמודה יש ללחוץ על View -> Select Columns ולסמן את ה CheckBox.


לאחר מכן נפתח Cmd ונרשום netstat -n -a -o ונקבל את רשימת החיבורים הפעילים שבמחשב ובנוסף ה PID של ה Process שאליו קשור החיבור.


עכשיו נקשר בין ה PID שמופיע ב NetStat לבין ה PID שמופיע ב Task Manager  ונדע כמה חיבורים יש לכל Process וכבר אפשר לראות שזה ממש לא נוח וצריכים כלי שיעשה זאת בשבילנו.

Process Analyzer

בסה"כ מדובר על כלי מאוד פשוט, קיימות מחלקות מוכנות ב .Net לניהול Processes שבעזרתם נקבל את רשימת ה Processes שרצים על המערכת, החלק היותר בעייתי איך מוצאים את כל החיבורים עבור כל Process, בעזרת Invoke (קריאה) ל פונקציות ומבנים ב Api נמצא את מה שאנחנו צריכים, אבל זה קצת טריקי, קיימת בעייה לגשת מתוכנית .Net שמוגדרת Managed Code לפונקציות API של Windows שמוגדרים כ UnManaged Code, אתר Pinvoke מכיל חתימות ידועות של פונקציות ב API של Windows שמאפשר לקרוא לפונקציה ישירות מ Net.

יש 2 פונקציות מרכזיות שאותן צריך להפעיל ב API של Windows הראשונה GetExtendedTcpTable והשנייה GetExtendedUdpTable, כל אחת מהפונקציות עובדת עם מבנים קבועים לצורך הדוגמה הפונקציה GetExtendedTcpTable צריכה לקבל מבנים מסוג MIB  שמיוחסים לפרוטוקול TCP כמו MIB_TCPROW_OWNER_PID MIB_TCPROW, MIB_TCPTABLE_OWNER_PID וכו',  בסיום הפונקציה מתמלא Buffer של טבלת החיבורים הפעילים וחוזר Pointer לאותו Buffer, מסביב כל פונקציה יש מעטפת שמכילה בתוכה את כל המבנים הדרושים.

tcpTavble.cs - מבוסס על הפונקציה שרשומה ב Pinvoke.

 public class tcpTable
    {
        //sort values by declaration order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_TCPROW_OWNER_PID
        {
            public uint state;
            public uint localAddr;
            public byte localPort1;
            public byte localPort2;
            public byte localPort3;
            public byte localPort4;
            public uint remoteAddr;
            public byte remotePort1;
            public byte remotePort2;
            public byte remotePort3;
            public byte remotePort4;
            public int owningPid;
        }

        //sort values by declaration order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_TCPROW
        {
            public uint dwState;
            public uint dwLocalAddr;
            public uint dwLocalPort;
            public uint dwRemoteAddr;
            public uint dwRemotePort;

        }

        //sort values by declartion order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_TCPTABLE_OWNER_PID
        {
            public uint dwNumEntries;
            MIB_TCPROW_OWNER_PID table;
        }

        //sort values by declaration order in memory
        //enum of type for tcptable
        enum TCP_TABLE_CLASS
        {
            TCP_TABLE_BASIC_LISTENER,
            TCP_TABLE_BASIC_CONNECTIONS,
            TCP_TABLE_BASIC_ALL,
            TCP_TABLE_OWNER_PID_LISTENER,
            TCP_TABLE_OWNER_PID_CONNECTIONS,
            TCP_TABLE_OWNER_PID_ALL,
            TCP_TABLE_OWNER_MODULE_LISTENER,
            TCP_TABLE_OWNER_MODULE_CONNECTIONS,
            TCP_TABLE_OWNER_MODULE_ALL
        }

        //enum of the state of single row in the table
        public enum TCP_ROW_STATE
        {
            CLOSED,
            LISTEN,
            SENT,
            RCVD,
            ESTAB,
            FIN_WAIT1,
            FIN_WAIT2,
            CLOSE_WAIT,
            CLOSING,
            LAST_ACK,
            TIME_WAIT,
            DELETE_TCB
        }


        //expose signature, importing ip helper api
        [DllImport("iphlpapi.dll", SetLastError = true)]
        static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_CLASS tblClass, int reserved);

        //return the MIB_TCPROW_OWNER_PID array as connection 
        public MIB_TCPROW_OWNER_PID[] GetAllTcpConnections()
        {
            MIB_TCPROW_OWNER_PID[] tTable;
            int AF_INET = 2;    // IP_v4
            int buffSize = 0;

            // what the size of the memory we need to allocate for the table
            uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0);
            //set pointer to buffer
            IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

            try
            {
                //getting the buffer
                ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0);
                if (ret != 0)
                {
                    return null;
                }

                //convert pointer to MIB_TCPTABLE_OWNER_PID pointer
                MIB_TCPTABLE_OWNER_PID tab = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_TCPTABLE_OWNER_PID));
                IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries));

                tTable = new MIB_TCPROW_OWNER_PID[tab.dwNumEntries];
 
                //reading the row from buffer using next position pointer
                //size of MIB_TCPROW_OWNER_PID
                for (int i = 0; i < tab.dwNumEntries; i++)
                {
                    //convert pointer to MIB_TCPROW_OWNER_PID pointer
                    MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID));
                    //save row in table
                    tTable[i] = tcpRow;
                    //go to the next entry.
                    rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow));
                }

            }
            finally
            {
                // clear buffer
                Marshal.FreeHGlobal(buffTable);
            }

            return tTable;
        }
    }





הפונקציה GetExtendedUdpTable אומנם לא חשופה ב Pinvoke אבל היא מאוד דומה ל GetExtendedTcpTable.

udpTable.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace processNetwork
{
    public class udpTable
    {
        //sort values by declartion order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_UDPROW_OWNER_PID
        {
            public uint LocalAddr;
            public byte localPort1;
            public byte localPort2;
            public byte localPort3;
            public byte localPort4;
            public int owningPid;
        }


        //sort values by declartion order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_UDPTABLE_OWNER_PID
        {
            public uint dwNumEntries;
            MIB_UDPROW_OWNER_PID table;
        }



        enum UDP_TABLE_CLASS
        {
            UDP_TABLE_BASIC,
            UDP_TABLE_OWNER_PID,
            UDP_TABLE_OWNER_MODULE
        }

        //expose signature, importing ip helper api
        [DllImport("iphlpapi.dll", SetLastError = true)]
        static extern uint GetExtendedUdpTable(IntPtr pUddpTable, ref int dwOutBufLen, bool sort, int ipVersion, UDP_TABLE_CLASS tblClass, int reserved);

        //return the MIB_UDPROW_OWNER_PID array
        public MIB_UDPROW_OWNER_PID[] GetAllUdpConnections()
        {


            MIB_UDPROW_OWNER_PID[] tTable;
         
            int AF_INET = 2;    // IP_v4
            int buffSize = 0;


            // what the size of the memory we need to allocate for the table?
            uint ret = GetExtendedUdpTable(IntPtr.Zero, ref buffSize, true, AF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID, 0);
            //set pointer to buffer
            IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

            try
            {
                //getting the buffer
                ret = GetExtendedUdpTable(buffTable, ref buffSize, true, AF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID, 0);
                if (ret != 0)
                {
                    return null;
                }


                //convert pointer to MIB_UDPTABLE_OWNER_PID pointer
                MIB_UDPTABLE_OWNER_PID tab = (MIB_UDPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_UDPTABLE_OWNER_PID));
             
                IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries));
         
                tTable = new MIB_UDPROW_OWNER_PID[tab.dwNumEntries];

                //reading the row from buffer using next position pointer
                //size of MIB_UDPROW_OWNER_PID     
                for (int i = 0; i < tab.dwNumEntries; i++)
                {
                    //convert pointer to MIB_UDPROW_OWNER_PID pointer
                    MIB_UDPROW_OWNER_PID udpRow = (MIB_UDPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_UDPROW_OWNER_PID));
                    //save row in table
                    tTable[i] = udpRow;
                    //go to the next entry.
                    rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(udpRow));
                }

            }
            finally
            {
                // clear buffer
                Marshal.FreeHGlobal(buffTable);
            }

            return tTable;
        }
    }

}


הטבלה שחוזרת לנו מהפונקציה צריכה לעבור מספר המרות, יש להמיר Ip Number ל Ip Address, ולהוציא את ה Port מהאובייקט.

Utils.cs

 public static class utils
 {
        //convert uint to ip
        public static string convert_uint_to_ip(uint ipnum)
        {
            string ipAddress = new IPAddress(BitConverter.GetBytes(ipnum)).ToString();
            return ipAddress;
        }

        //convert bytes to ushort -> port
        public static ushort convert_bytes_to_port(byte p0, byte p1, byte p2, byte p3)
        {
            ushort port;
            byte[] arr = new byte[4];

            //check if the system is litte endian, or big endian and sort the array
            //reading the first 2 bytes
            if (BitConverter.IsLittleEndian)
            {
                arr[0] = p3;
                arr[1] = p2;
                arr[2] = p1;
                arr[3] = p0;
                port = BitConverter.ToUInt16(arr, 2);
            }
            else
            {
                arr[0] = p0;
                arr[1] = p1;
                arr[2] = p2;
                arr[3] = p3;
                port = BitConverter.ToUInt16(arr,0);
            }


            return port;


        }
    }



GUI




הפונקציה שהכי חשובה ב Gui היא init_processStack שאוספת את המידע ממחלקות ה Api שהצגתי וממחלקת ה Process הפנימית של .Net ומחברת בניהם על פי ה Process id ,שאר הפונקציות משמשות לעבודה מול הפקדים ב Gui לכן לא ארחיב עליהם. (פירוט מלא בקוד).

        /// collecting all the processes and the active connections
        private void init_processStack()
        {
            //create table of all the tcp connections
            tcpTable tcptbl = new tcpTable();
            tcpTable.MIB_TCPROW_OWNER_PID[] tcpRow = tcptbl.GetAllTcpConnections();

            //create table of all the udp connections
            udpTable udptbl = new udpTable();
            udpTable.MIB_UDPROW_OWNER_PID[] udpRow = udptbl.GetAllUdpConnections();

            //getting the processes from the machine
            Process[] pro = Process.GetProcesses();

            //add rows from tcp and udp tables inside sysProcess array
            //compate the process id inside the process array and the process id
            //inside the connections tables
            if (pro != null)
            {

                    prcs = new SysProcess[pro.Length];
          


                for (int i = 0; i < pro.Length; i++)
                {
                    prcs[i] = new SysProcess(pro[i]);

                    if (tcpRow != null)
                    {
                        for (int z = 0; z < tcpRow.Length; z++)
                        {
                            if (prcs[i].prc.Id == tcpRow[z].owningPid)
                                prcs[i].tcprows.Add(tcpRow[z]);
                        }
                    }

                    if (udpRow != null)
                    {
                        for (int z = 0; z < udpRow.Length; z++)
                        {
                            if (prcs[i].prc.Id == udpRow[z].owningPid)
                                prcs[i].udpRows.Add(udpRow[z]);
                        }
                    }

                }
            }

        }

סרט דוגמה:





קוד:
https://sourceforge.net/projects/processnetwork/

סיכום:

למדנו להכיר טוב יותר את התהליכים שרצים לנו על המחשב וכיצד ניתן לעקוב אחריהם, קיימים המון כלים בתחום , Mark Russinovich היה בין הראשונים שלקח את ניתוח המערכות של מיקרוספט לקצה וניתן למצוא המון ספרים שלו בתחום, בנוסף אני חייב להזכיר את הפרויקט Process Hacker שבקוד פתוח ב .Net שניתן ללמוד ממנו המון.

תתחילו לעקוב...


יום שבת, 10 בנובמבר 2012

Android Http Widget



עבר הרבה זמן מהמאמר האחרון שלי בנושא Android, והגיע הזמן לרענן את הנושא, אחד הדברים שתמיד אהבתי זה את כל ה Widgets המגניבים שעל שולחן העבודה שהופכים אותו ליותר מעניין מאשר קבוצה של אייקונים סטטים מיושנים, הרעיון להפוך את שולחן העבודה הקר לאינטרקטיבי יותר שיזרים מידע באופן שוטף למשתמש, אבל חשוב לזכור ש Widget אומנם מתנהג כתוכנה לכל דבר אבל קיימות עליו מגבלויות תיכנותיות ועיצוביות, ולא הכל אפשרי לביצוע, אחד האתגרים ב Widget הוא שילוב עם האינטרנט שזה משהו לא טריוויאלי, בקיצור כאב ראש לא קטן.

אופי הפרויקט מאוד פשוט, כל כמה שניות ה Widget ניגש לאינטרנט ,מקבל את השעה של השרת המרוחק ומציג אותו למשתמש.

יצירת פרויקט חדש



שימו לב: הפרויקט מבוסס על API 10.


שימו לב: לבטל את ה Create Activity.



Widget Code

לאחר שנוצר הפרויקט נבנה את ה Class המרכזי עבור ה Widget.


 ה Class צריך לירוש את AppWidgetProvider:

package proxytype.widget.example;
import android.appwidget.AppWidgetProvider;

public class Main extends AppWidgetProvider {

}

צריך לעשות Override לפונקציה onUpdate, לחיצה על הכפתור הימני  Source -> Override/Implement Methods:



package proxytype.widget.example;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;

public class Main extends AppWidgetProvider {

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[])
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);
}

}

אחד החסרונות הרציינים ב Widget ב Android הוא שלא ניתן להשתמש ב Webview, וזה מאוד מאכזב, Webview  היה מושלם למשימה ואני לא יודע מה בדיוק הסיבה שמפתחי גוגל מונעים מאיתנו לעבוד איתו , עדיין ניתן לבצע בקשות לאינטרנט אבל במקרה שנרצה לעדכן את ה Widget באופן קבוע צריכים ליצור Service שיבצע את הבקשה בכל פרק זמן מסויים ובסוף התהליך יעדכן את ה Widget, ניצור Class חדש שיורש מהמחלקה של Service.


package proxytype.widget.example;

import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.StrictMode;
import android.util.Log;
import android.widget.RemoteViews;

public class UpdateService extends Service {

/* (non-Javadoc)
* @see android.app.Service#onBind(android.content.Intent)
*/
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}

/* (non-Javadoc)
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();

//chage thread policy running network inside the main
//thread, this is exmaple better use AsyncTask Class
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
       StrictMode.setThreadPolicy(policy);
}

/* (non-Javadoc)
* @see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
update_widget();
return super.onStartCommand(intent, flags, startId);
}


private void update_widget()
{
//connecting to reomote Activity (widget main.xml)
                RemoteViews view = new RemoteViews(getPackageName(), R.layout.main);
     
               //update text box inside the widget
               view.setTextViewText(R.id.textView1, getPage());
     
              //update the widget with the new data
              ComponentName thisWidget = new ComponentName(this, Main.class);
              AppWidgetManager manager = AppWidgetManager.getInstance(this);
              manager.updateAppWidget(thisWidget, view);

}

//create the http call
private String getPage() {
    String str = "***";

        try
    {
    HttpClient hc = new DefaultHttpClient();
    HttpPost post = new HttpPost("http://192.168.1.8:443/default.aspx");
   
    //wating for response from request
    HttpResponse rp = hc.execute(post);
   
                //oblivious bad requests
    if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
    {
    str = EntityUtils.toString(rp.getEntity());
    }
    }catch(IOException e){
    Log.e("ERROR", e.getMessage());
    }
   
    return str;
    }
}

נחזור ל Class שממנו הכל מתחיל ונכניס בו את השינויים הבאים, אנחנו צריכים שעון שיפעיל את ה Service בשביל לעדכן את ה Widget כל כמה שניות, נוכל לממש זאת בעזרת הספרייה AlarmManager.

package proxytype.widget.example;

import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;

public class Main extends AppWidgetProvider {

private PendingIntent service = null; 
/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[])
*/ 
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);
//system RTC to schedule mission
final AlarmManager alarmManager = (AlarmManager) 
               context.getSystemService(Context.ALARM_SERVICE);
//create instance to service
final Intent ServiceShow = new Intent(context, UpdateService.class);
//set the alarm clock to zero
final Calendar alarmTime = Calendar.getInstance();
alarmTime.set(Calendar.MINUTE, 0);
alarmTime.set(Calendar.SECOND, 0);
alarmTime.set(Calendar.MILLISECOND, 0);
        
        if (service == null)
        {
        //calling others application in this case to the service 
        //using the intent show to start the service,
        //and allow it to update this application
            service = PendingIntent.getService(context, 0, ServiceShow, 
                          PendingIntent.FLAG_CANCEL_CURRENT);
        }
        //set the RTC clock to update the widget every 3 seconds
        alarmManager.setRepeating(AlarmManager.RTC, alarmTime.getTime().getTime(), 3000, service);
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onDeleted(android.content.Context, int[])
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onDeleted(context, appWidgetIds);
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onDisabled(android.content.Context)
*/
@Override
public void onDisabled(Context context) {
// TODO Auto-generated method stub
super.onDisabled(context);
 
final AlarmManager m = (AlarmManager) 
                context.getSystemService(Context.ALARM_SERVICE);  
               if (service != null)
             {
        //disable the clock
                 m.cancel(service);
             }
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onEnabled(android.content.Context)
*/
@Override
public void onEnabled(Context context) {
// TODO Auto-generated method stub
super.onEnabled(context);
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onReceive(android.content.Context, android.content.Intent)
*/
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
super.onReceive(context, intent);
}
}

Widget Setup

אחרי שהכנו את הקוד יש תהליך בירוקרטי עבור יצירת Widget, לכל Widget חייב להיות Provider על מנת לעבוד, ה Provider הוא קובץ הגדרות בשפת XML, שנמצא בתקיית Xml בפרויקט, חשוב לייצר תיקייה כזו במקרה שהיא לא קיימת, ניצור בספרייה קובץ בשם provider.xml.



נרשום בתוכו את השורות הבאות.

<?xml version="1.0" encoding="utf-8" ?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="146dp"
    android:initialLayout="@layout/main"
    android:updatePeriodMillis="1000"
    android:minHeight="144dp"/>



צריך להכניס לקובץ ה Manifest של התוכנית מספר שינויים, הראשון הוא להצהיר שהתוכנית היא Widget ולחבר לה את ה Provider ,אח"כ להוסיף את ה Service ולסיום טיפול בהרשאות :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="proxytype.widget.example"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />
     
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        
        <receiver android:name=".Main" android:label="Server Clock">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/provider" />
        </receiver>

        <service android:permission="android.permission.INTERNET" android:enabled="true" android:name=".UpdateService" />

    </application>
 <uses-permission
        android:name="android.permission.INTERNET"></uses-permission>
</manifest>


קובץ ה Layout של התוכנית main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />

</LinearLayout>

תוצאה סופית:



סיכום:

Widget היא דרך מעניינת להזרים מידע באופן שוטף לשולחן העבודה של המשתמש, המידע חייב להיות מתומצת וה Widget צריך להיות פשוט, אם התוכנית שלכם מסובכת עדיף שתבנו אפליקציה ותחסכו לעצמכם הרבה כאב ראש.

למידע מקיף עבור Widgets ב Android לחץ כאן.

בהצלחה...

יום שבת, 3 בנובמבר 2012

Arduino Joystick Guide


אחרי שהבנו איך עובדים עם פוטנציומטרים וכפתורים, כשמשלבים אותם ביחד מקבלים ג'ויסטיק, את שלי רכשתי באתר DX במחיר של 4 דולר,  הג'ויסטיק דומה מאוד לג'ויסטיק הקיים בשלטי PlayStation , ומאפשר לנו לשכלל את חווית המשתמש בפרויקט , אז חבל על הדיבורים וקדימה לעבודה.

דרישות:
  • Arduino Uno
  • Arduino Compatible Joystick
  • Wires




בסה"כ מדובר 2 פוטנציומטרים , עבור הצירים X ו ה Y, בנוסף קיים כפתור לחיצה שפועל כאשר לוחצים על משטח הג'ויסטיק, החיבור ל Arduino מאוד פשוט, נחבר את החיבורים של הפוטנציומטרים ושל הכפתור לכניסות ה Analog שע"ג ה Arduino ונדגום אותם.



מבנה סופי:



קוד:
//analog input pin X,Y,SW
const byte PIN_ANALOG_X = 1;
const byte PIN_ANALOG_Y = 2;
const byte PIN_SWITCH =3;

int x_position;
int y_position;
int sw =0;

void setup()
{
   Serial.begin(9600);
}


void loop()
{
  x_position = analogRead(PIN_ANALOG_X);
  y_position = analogRead(PIN_ANALOG_Y);
  sw = analogRead(PIN_SWITCH);
 
   if(sw == 0)
   {
     Serial.print("button press \n");
   }
   
  Serial.print( x_position);
  Serial.print(" -");
  Serial.print(y_position);
  Serial.print( "\n");
  delay(1);
}

בהצלחה...


יום רביעי, 24 באוקטובר 2012

REST Web Service Asp.Net



עד עכשיו כתבתי על SOAP והגיע הזמן להכיר את השיטה המתחרה REST - representational state transfer , ההבדל העיקרי הוא ש SOAP הוא פרוטוקול, יש בו חוקים ברורים שבעזרתם עוטפים את המידע שברצוננו לשלוח, ב REST העסק שונה, REST היא ארכיטקטורה שמתבססת על HTTP Protocol וכל אחד יכול לממש אותה אחרת.

ב SOAP אנחנו עובדים מול קובץ WSDL שמכיל בתוכו את המעטפת (אלמנטים וקריאות לפונקציות) שמאוד קשה לתחזק  וכשמדובר על Service מורכב העסק עלול להפוך למסורבל, REST נותן פתרון פשוט שמתבסס על HTTP ,בעזרת פונקציות שקיימות בפרוטוקול (Get,Put,Post,Delete) ננתב את הבקשות.

שימו לב! המאמר מציג שיטה למימוש Rest ע"ג IIS 7.

שלב א - צד השרת:

תחילה נבנה פרויקט חדש של Web Site ב Visual Studio.

ניצור Class חדש שיורש את ה Interface של IHttpHandler , עלינו לממש את המאפיין IsReusable ופונקציית ProcessRequest כפי שניתן לראות בדוגמה הבאה:


public class Service: IHttpHandler
    {
        bool IHttpHandler.IsReusable
        {
            get { return false; }
        }

        void IHttpHandler.ProcessRequest(HttpContext context)
        {
            //implementation of request by method...
        }

}

CURD (create , update, read, delete) Method

פרוטוקל HTTP מכיל בתוכו פונקציות מובנות, מי שבונה אתרי אינטרנט מכיר בעיקר את ה GET ו POST המפורסמים שמאפשרים לנו להעביר מידע בין טפסים, אבל קיימות פונקציות נוספות כמו PUT ו DELETE שבעזרתם ניתן לנתב את הבקשות ל Service שלנו.

GET
בקשות למידע, לדוגמה קריאות מה Database.

POST
הוספת אובייקט ל Database בעזרת המידע שנשלח ב Body.

PUT
דומה ל POST , עדכון אובייקט קיים בעזרת המידע שנשלח ב Body.

DELETE
מחיקת אובייקט

כשנשלחת בקשת GET אנחנו פונים ל Database ומחזירים את הנתונים ללקוח על בסיס פרמטרים שעוברים ב Url, כאשר הלקוח שולח בקשת Post יוצרים אובייקט חדש ב Database שאת המידע אנחנו אוספים מה Body של החבילה.

כאשר מתבצעת קריאה ל Service הפונקציה Process Request היא בעצם ה Entry Point וכל בקשה תפעיל את הפונקציה הזאת ודרכה ננתב את הבקשות של המשתמש למשאבים המתאימים כפי שניתן לראות בדוגמה הבאה:

  void IHttpHandler.ProcessRequest(HttpContext _context)
        {
            try
            {
                string url = Convert.ToString(_context.Request.Url);
                //handling CURD (CREATE UPDATE READ DELETE)
                switch (_context.Request.HttpMethod)
                {
                    case "GET":
                        //return data from database             
                        READ(_context);
                        break;
                    case "POST":
                        //create new element in database
                        CREATE(_context);
                        break;
                    case "PUT":
                        //update element in database
                        UPDATE(_context);
                        break;
                    case "DELETE":
                        //delete element from the database
                        DELETE(_context);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            { _context.Response.Write(ex.Message);}
        }

אחרי שלכדנו את השם של הפונקציה שנשלחה מהמשתמש , עלינו לנתב את הבקשה לפונקציה המתאימה, נבנה 4 פונקציות נוספות ב Class:


//getting object from request using GET method
        private void READ(HttpContext _context)
        {
            string serialized = "";
         
            //getting guides parameters from request
            if (_context.Request["type"].ToString() == "person")
            {
                //properly the point to call database
                //get data and parse to xml back to client
                person _person = new person();
                _person.FirstName = "HELLO";
                _person.LastName = "WORLD";
                serialized = Serialize(_person);
            }

            //depend what language you want to use
            _context.Response.ContentType = "text/xml";
            HttpContext.Current.Response.Write(serialized);
        }

        //insert new object using the POST method
        private void CREATE(HttpContext _context)
        {
            //reading the body of the message
            byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
            //convert the bytes array to string as utf8
            string str = Encoding.UTF8.GetString(bodybytes);
            // deserialize xml into person
            person _person = person.deSerialize(bodybytes);
            //insert database...
        }

        //update object using the POST method
        private void UPDATE(HttpContext _context)
        {
            //reading the body of the message
            byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
            //convert the bytes array to string as utf8
            string str = Encoding.UTF8.GetString(bodybytes);
            // deserialize xml into person
            person _person = person.deSerialize(bodybytes);
            //update database...
        }

        //delete element using the DELETE method
        private void DELETE(HttpContext _context)
        {

            if (_context.Request["type"].ToString() == "person")
            {
                if (_context.Request["id"] != null)
                {
                    int i = 0;
                    if (int.TryParse(_context.Request["id"].ToString(), out i))
                    {
                        //continue to database...
                    }
                }
            }
        }

אחד היתרונות ב REST שניתן לעבוד עם סוגי מבנים שונים, בניגוד ל SOAP שמבוסס על XML ב REST אפשר לבחור מה שבא לנו XML , JSON או להמציא בעצמנו את המבנה, במאמר זה נעבוד עם XML , לכן חשוב לייצר 2 פונקציות נוספות שימירו לנו את המידע מ XML לאובייקט .Net ומ .Net ל XML.

קוד מלא:

using System;
using System.Collections.Generic;
using System.Web;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Xml;

namespace restLand
{
    public class rest : IHttpHandler
    {
        bool IHttpHandler.IsReusable
        {
            get { return false; }
        }

        void IHttpHandler.ProcessRequest(HttpContext _context)
        {
            try
            {
                string url = Convert.ToString(_context.Request.Url);

                //handling CURD (CREATE UPDATE READ DELETE)
                switch (_context.Request.HttpMethod)
                {
                    case "GET":
                        //return data from database          
                        READ(_context);
                        break;
                    case "POST":
                        //create new element in database
                        CREATE(_context);
                        break;
                    case "PUT":
                        //update element in database
                        UPDATE(_context);
                        break;
                    case "DELETE":
                        //delete element from the database
                        DELETE(_context);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            { _context.Response.Write(ex.Message);}
        }

        //getting object from request using GET method
        private void READ(HttpContext _context)
        {
            string serialized = "";
         
            //getting guides parameters from request
            if (_context.Request["type"].ToString() == "person")
            {
                //properly the point to call database
                //get data and parse to xml back to client
                person _person = new person();
                _person.FirstName = "HELLO";
                _person.LastName = "WORLD";
                serialized = Serialize(_person);
            }

            //depend what language you want to use
            _context.Response.ContentType = "text/xml";
            HttpContext.Current.Response.Write(serialized );
        }

        //insert new object using the POST method
        private void CREATE(HttpContext _context)
        {
            //reading the body of the message
            byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
            //convert the bytes array to string as utf8
            string str = Encoding.UTF8.GetString(bodybytes);
            // deserialize xml into person
            person _person = person.deSerialize(bodybytes);
            //insert database...
        }

        //update object using the POST method
        private void UPDATE(HttpContext _context)
        {
            //reading the body of the message
            byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
            //convert the bytes array to string as utf8
            string str = Encoding.UTF8.GetString(bodybytes);
            //deserialize xml into person
            person _person = person.deSerialize(bodybytes);
            //update database...
        }

        //delete element using the DELETE method
        private void DELETE(HttpContext _context)
        {

            if (_context.Request["type"].ToString() == "person")
            {
                if (_context.Request["id"] != null)
                {
                    int i = 0;
                    if (int.TryParse(_context.Request["id"].ToString(), out i))
                    {
                        //continue to database...
                    }
                }
            }
        }

        private String Serialize(object _class)
        {
            try
            {
                //output string
                String xmlstring = null;

                //parser to xml from object using polymorphism
                XmlSerializer seriailizor = null;
                if (_class.GetType().ToString() == "restLand.person")
                    seriailizor = new XmlSerializer(typeof(person));
             
                //memory buffer
                MemoryStream stream = new MemoryStream();
             
                //xml writer be sure using utf8 encoding
                XmlTextWriter textwriter = new XmlTextWriter(stream, Encoding.UTF8);

                //the moment of true, parsing object
                seriailizor.Serialize(textwriter, _class);
             
                //fill the buffer
                stream = (MemoryStream)textwriter.BaseStream;
             
                //Convert to array to string 
                xmlstring = UTFbytesToString(stream.ToArray());
                return xmlstring;
            }
            catch (Exception ex)
            {
                return "";
            }

        }

        //convert utf8 bytes array to string
        private string UTFbytesToString(Byte[] array)
        {
            UTF8Encoding utf8encoder = new UTF8Encoding();
            string convertedString = utf8encoder.GetString(array);
            return convertedString;
        }

    }


    public class person
    {
        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }

        //create deserialize function for each class
        public static person deSerialize(byte[] xmlbyte)
        {
           try
           {
               XmlSerializer seriailizor = new XmlSerializer(typeof(person));
               MemoryStream stream = new MemoryStream(xmlbyte);
               person _person = new person();
               //parsing the bytes to person
               _person = (person)seriailizor.Deserialize(stream);
               return _person;
           }
           catch (Exception ex)
           {throw;}
        }
    }
}

חשוב מאוד! להגדיר את ה Handler ב Web.config:

</system.web>
 <httpHandlers>
        <add type="restLand.rest" verb="*" path="service" />
  </httpHandlers>
 </system.web>


  <system.webServer>
     <handlers accessPolicy="Read, Script">
            <add name="service" path="service" verb="*" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv2.0,bitness32" />
     </handlers>
   </system.webServer>




שלב ב - צד הלקוח

החלק הזה מאוד פשוט, ניצור פרוייקט חדש מסוג Console Application, ונבצע בקשות ל Url של ה Service בעזרת אובייקט HttpRequest, ונקבל את התשובה בעזרת HttpResponse.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;

namespace resttest
{
    class Program
    {
        //service url
        static string url = "http://192.168.1.8:443/service?type=person";
        
        static void Main(string[] args)
        {
            GET();
            POST();
        }

        //GET method
        private static void GET()
        {
           
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            
            //selected method
            request.Method = "GET";

            Console.WriteLine("send GET request");
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream stream = response.GetResponseStream();
            StreamReader reader = new StreamReader(stream);
            Console.WriteLine("get response");
            Console.WriteLine(reader.ReadToEnd());
            Console.WriteLine("end GET request");
        }

        //create xml stream of person
        private static byte[] create_person(string firstname, string lastname)
        {
            //create person xml 
            MemoryStream stream = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8);
            writer.Formatting = Formatting.Indented;
            writer.WriteStartDocument();
            writer.WriteStartElement("person");
            writer.WriteStartElement("FirstName");
            writer.WriteString(firstname);
            writer.WriteEndElement();
            writer.WriteStartElement("LastName");
            writer.WriteString(lastname);
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Flush();
            writer.Close();

            return stream.ToArray();
        }

        //POST method, PUT and DELETE like this method
        private static void POST()
        {

            byte[] _arr = create_person("WORLD", "HELLO");

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            Console.WriteLine("send POST request");
            
            //selected method
            request.Method = "POST";
            request.ContentType = "text/xml";
            request.KeepAlive = false;
            request.Timeout = 5000;
            request.ContentLength = _arr.Length;

            Stream stream = request.GetRequestStream();
            // write xml to stream
            stream.Write(_arr, 0, _arr.Length);

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            //write the response from server
            Console.WriteLine(reader.ReadToEnd().ToString());
            Console.WriteLine("end POST request");
        }
       
    }
}

סיכום:
קשה מאוד להגיד איזו שיטה טובה יותר, אבל האמת היא ש REST מבוססת על תקשורת בסיסית של HTTP בניגוד ל SOAP שלא תמיד נתמך ולפעמים יש להשתמש בספריות צד שלישי בשביל לעבוד איתו, אבל חשוב מאוד לזכור שאחריות רבה מונחת על המפתח בזמן ההתעסקות עם REST, בגלל הגמישות שלו אנחנו חסרי הגנה וחשוב לבצע בדיקות רבות לפני שאנחנו משחררים אותו לעולם.

כולל שירות...