השתמש באביב כדי ליצור מנוע זרימת עבודה פשוט

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

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

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

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

זרימת עבודה פשוטה

זרימת עבודה של דוגמנות היא נושא שנחקר כבר בשנות השבעים, ומפתחים רבים ניסו ליצור מפרט סטנדרטי של דוגמנות זרימת עבודה. דפוסי זרימת עבודה , נייר לבן מאת WHM van der Aalst et al. (יולי 2003), הצליחה לסווג מערך של דפוסי עיצוב המדגמים במדויק את תרחישי זרימת העבודה הנפוצים ביותר. בין הטריוויאליים מבין דפוסי זרימת העבודה הוא דפוס הרצף. בהתאם לקריטריונים של זרימת עבודה פשוטה, דפוס זרימת העבודה של רצף מורכב ממכלול פעילויות המבוצעות ברצף.

דיאגרמות פעילות של UML (Unified Modelling Language) משמשות בדרך כלל כמנגנון למודל זרימת העבודה. איור 1 מציג תהליך זרימת עבודה בסיסי של רצף המעוצב באמצעות תרשים פעילויות UML רגיל.

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

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

בואו נעבור על ההיגיון העסקי של זרימת העבודה של חברת התעופה. אם הפעילות הראשונה לא מוצאת משתמשים המעוניינים בהתראות על ירידה בשיעור, זרימת העבודה כולה מבוטלת. אם מתגלים משתמשים מעוניינים, הפעילויות הנותרות הושלמו. בהמשך, טרנספורמציה של XSL (Extensible Stylesheet Language) מייצרת את תוכן ההודעה, שלאחריה נרשמת מידע ביקורת. לבסוף, נעשה ניסיון לשלוח את ההודעה דרך שרת SMTP. אם ההגשה הושלמה ללא שגיאה, ההצלחה נרשמת והתהליך מסתיים. אך אם מתרחשת שגיאה במהלך התקשורת עם שרת ה- SMTP, שגרת טיפול בשגיאות מיוחדת תשתלט. קוד טיפול בשגיאות זה ינסה לשלוח מחדש את ההודעה.

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

שליטה הפוכה

Spring מאפשר לנו להסיר את האחריות לשליטה בתלות של אובייקט על ידי העברת אחריות זו למיכל Spring. העברת אחריות זו ידועה בשם היפוך שליטה (IoC) או הזרקת תלות. ראה "היפוך מיכלי בקרה ותבנית הזרקת התלות" של מרטין פאולר (martinfowler.com, ינואר 2004) לדיון מעמיק יותר בנושא הזרקת IoC והזרקת תלות. על ידי ניהול תלות בין אובייקטים, אביב מבטל את הצורך בקוד דבק , קוד שנכתב במטרה היחידה לגרום לשיעורים לשתף פעולה זה עם זה.

רכיבי זרימת עבודה כשעועית קפיץ

לפני שנתקדם יותר מדי, זה זמן טוב לעבור על המושגים העיקריים שמאחורי האביב. ApplicationContextהממשק, בירושה מן BeanFactoryהממשק, כופה את עצמו כישות שליטה בפועל או המכל בתוך האביב. ApplicationContextאחראי תדגום, תצורה, וניהול מחזור חיים של סט של שעועית המכונית שעועית האביב.ApplicationContext מוגדר על ידי החיווט עד שעועית האביב בקובץ תצורה מבוססת XML. קובץ תצורה זה מכתיב את האופי שבו שעועית ספרינג משתפת פעולה זו עם זו. לפיכך, באביב מדברים, שעועית קפיצית המתקשרת עם אחרים מכונה משתפי פעולה. כברירת מחדל, שעועית האביב קיימת כסינגלים בApplicationContext, אך ניתן להגדיר את תכונת ה- singleton כ- false, ולשנות אותם באופן יעיל כך ש- Spring מכנה מצב אב-טיפוס .

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

הטמעת רכיבי זרימת העבודה כשעועית קפיץ גורמת לשני תוצרי לוואי רצויים, נוחות לבדיקת יחידות ומידה רבה של שימוש חוזר. בדיקות יעילות של היחידות ניכרות בהתחשב באופי המכולות של IoC. באמצעות מיכל IoC כמו Spring, ניתן בקלות להחליף תלות של משתפי פעולה עם תחליפים מדומים במהלך הבדיקה. בדוגמה של חברת התעופה, Activityשעועית ספרינג כזו activity5ניתן לשלוף בקלות ממבחן עצמאי ApplicationContext. החלפת נציג SMTP מדומה activity5מאפשרת לבצע בדיקת יחידה activity5בנפרד.

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

חיווט בתהליך העבודה

ב- API המסופק (להורדה מ- Resources), Spring שולט על קבוצה קטנה של שחקנים כדי לקיים אינטראקציה באופן המהווה זרימת עבודה. ממשקי המפתח הם:

  • Activity: מקפל את ההיגיון העסקי של שלב יחיד בתהליך זרימת העבודה.
  • ProcessContext: אובייקטים מסוג ProcessContextמועברים בין פעילויות בתהליך העבודה. אובייקטים המיישמים ממשק זה אחראים על שמירת המצב כאשר זרימת העבודה עוברת מפעילות אחת לאחרת.
  • ErrorHandler: מספק שיטת התקשרות חזרה לטיפול בשגיאות.
  • Processor: מתאר שעועית המשמשת כמבצעת חוט התהליך הראשי.

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

             /property>  org.iocworkflow.test.sequence.ratedrop.RateDropContext  

SequenceProcessorבכיתה היא תת בטון כי מודלי דפוס רצף. מחובר למעבד חמש פעילויות שמעבד זרימת העבודה יבצע לפי הסדר.

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

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

public interface ProcessContext extends Serializable { public boolean stopProcess(); public void setSeedData(Object seedObject); }

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

עד כה, כל מקרי השעועית היו יחידים בהתאם ApplicationContextלהתנהגות ברירת המחדל . אך עלינו ליצור מופע חדש של RateDropContextהכיתה לכל קריאה לתהליך העבודה של חברת התעופה. כדי לטפל בדרישה זו, SequenceProcessorמוגדר, תוך לקיחת שם מחלקה מוסמך processContextClassכרכוש. עבור כל ביצוע של זרימת עבודה, SequenceProcessorמאחזר מופע חדש של ProcessContextSpring באמצעות שם המחלקה שצוין. כדי שזה יעבוד, שעועית קפיצית או סוג אב - טיפוס מסוג nonsingleton org.iocworkflow.test.sequence.simple.SimpleContextחייבת להתקיים ב- ApplicationContext(ראה rateDrop.xmlאת כל הרישום).

זריעת זרימת העבודה

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

public interface Processor { public boolean supports(Activity activity); public void doActivities(); public void doActivities(Object seedData); public void setActivities(List activities); public void setDefaultErrorHandler(ErrorHandler defaultErrorHandler); }

ברוב המקרים, תהליכי זרימת עבודה דורשים גירויים ראשוניים להתנעה. שתי אפשרויות קיימות לבעיטת מעבד: doActivities(Object seedData)השיטה או האלטרנטיבה שלה ללא ויכוח. רישום הקוד הבא הוא doAcvtivities()הטמעה עבור SequenceProcessorהכלול עם קוד הדוגמה:

 public void doActivities(Object seedData) { if (logger.isDebugEnabled()) logger.debug(getBeanName() + " processor is running.."); //retrieve injected by Spring List activities = getActivities(); //retrieve a new instance of the Workflow ProcessContext ProcessContext context = createContext(); if (seedData != null) context.setSeedData(seedData); for (Iterator it = activities.iterator(); it.hasNext();) { Activity activity = (Activity) it.next(); if (logger.isDebugEnabled()) logger.debug("running activity:" + activity.getBeanName() + " using arguments:" + context); try { context = activity.execute(context); } catch (Throwable th) { ErrorHandler errorHandler = activity.getErrorHandler(); if (errorHandler == null) { logger.info("no error handler for this action, run default error" + "handler and abort processing "); getDefaultErrorHandler().handleError(context, th); break; } else { logger.info("run error handler and continue"); errorHandler.handleError(context, th); } }