217 106 4MB
English Pages 385 Year 2001
COM and .NET Com ponent Services Dedicat ion Foreword Preface Scope of This Book Som e Assum pt ions About t he Reader Definit ions and Text Convent ions Ot her COM+ Books and Refer ences How t o Cont act Us Acknow ledgm ent s 1. COM+ Com ponent Serv ices 1.1 COM+ Com ponent Ser v ices 1.2 The Com ponent Ser vices Explor er 1.3 Hello COM+ 1.4 COM+ Configur ed Com ponent s 1.5 Applicat ions, DLLs, and Com ponent s 1.6 Configur ing COM+ Applicat ions 1.7 Debugging COM+ Applicat ions 1.8 Deploy ing COM+ Applicat ions 1.9 Sum m ary 2. COM+ Cont ext 2.1 Encapsulat ion v ia Marshaling in COM 2.2 Encapsulat ion v ia I nt ercept ion in COM+ 2.3 The Cont ext Obj ect 2.4 The Call Obj ect 2.5 Cr oss- Cont ext Manual Marshaling 2.6 Sum m ary 3. COM+ I nst ance Managem ent 3.1 Client Types 3.2 I nst ance Managem ent and Scaling 3.3 Obj ect Pooling 3.4 Just - in- Tim e Act iv at ion 3.5 Com bining JI TA w it h Obj ect Pooling 3.6 Obj ect Const r uct or St r ing 3.7 COM+ I nst ance Managem ent Pit falls 4. COM+ Transact ions 4.1 Transact ion Basics 4.2 Transact ion Pr opert ies 4.3 Transact ion Scenarios 4.4 COM+ Transact ions Archit ect ur e 4.5 Configur ing Transact ions 4.6 Vot ing on a Transact ion 4.7 Transact ional Obj ect Life Cycle 4.8 Designing Transact ional Com ponent s 4.9 Nont ransact ional Client s 4.10 Transact ions and Obj ect Pooling 4.11 Com pensat ing Tr ansact ions 4.12 Transact ion Ex ecut ion Tim e
2
4.13 4.14 4.15 4.16
Tracing Transact ions I n- Doubt Transact ions Transact ion St at ist ics COM+ Transact ions Pit falls
5. COM+ Concur r ency Model 5.1 Obj ect - Orient ed Program m ing and Mult iple Threads 5.2 Apart m ent s: The Classic COM Solut ion 5.3 Act iv it ies: The COM+ I nnovat ion 5.4 COM+ Configurat ion Set t ings 5.5 Act iv it ies and JI TA 5.6 Act iv it ies and Transact ions 5.7 Tracing Act iv it ies 5.8 The Neut ral Threaded Apart m ent 5.9 Sum m ary 6. Pr ogram m ing t he COM+ Cat alog 6.1 Why Pr ogram t he Cat alog? 6.2 The Cat alog Progr am m ing Model 6.3 Cat alog St ruct ur e 6.4 I nt eract ing w it h t he Cat alog 6.5 Feat ur es of COMAdm inCat alog 6.6 The COM+ Cat alog and Transact ions 6.7 Sum m ary 7. COM+ Secur it y 7.1 The Need for Secur it y 7.2 Basic Secur it y Ter m s 7.3 Role- Based Secur it y 7.4 Secur ing a Ser ver Applicat ion 7.5 Secur ing a Librar y Applicat ion 7.6 Pr ogram m at ic Role- Based Secur it y 7.7 Secur it y Boundar ies 7.8 Advanced COM+ Securit y 7.9 COM+ Secur it y Pit falls 7.10 Sum m ary 8. COM+ Queued Com ponent s 8.1 Maj or Benefit s of Queued Com ponent s 8.2 Queued Com ponent s Archit ect ure 8.3 Com ponent Ser vices Explor er Configurat ion 8.4 I nvoking Queued Com ponent s on t he Client Side 8.5 Designing Queued Com ponent I nt erfaces 8.6 Receiv ing Out put from a Queued Com ponent 8.7 Queued Com ponent Er ror Handling 8.8 Queued Com ponent s and Transact ions 8.9 Sy nchr onous Versus Asy nchronous Com ponent s 8.10 Queued Com ponent s Secur it y 8.11 Queued Com ponent s Pit falls 8.12 Sum m ary 9. COM+ Ev ent Serv ice 9.1 Classic COM Ev ent s 9.2 COM+ Ev ent Model 9.3 The Event Class 9.4 Subscript ion Ty pes 9.5 Deliv ering Ev ent s
3
9.6 Ev ent Filt er ing 9.7 Dist ribut ed COM+ Ev ent s 9.8 Asy nchronous Ev ent s 9.9 COM+ Ev ent s and Transact ions 9.10 COM+ Ev ent s and Securit y 9.11 COM+ Ev ent s Lim it at ion 9.12 Sum m ary 10. .NET Serv iced Com ponent s 10.1 Developing Serv iced Com ponent s 10.2 .NET Assem blies and COM+ Applicat ions 10.3 Regist er ing Assem blies 10.4 Configur ing Ser v iced Com ponent s 10.5 Applicat ion Act iv at ion Type 10.6 The Descr ipt ion At t r ibut e 10.7 Accessing t he COM+ Cont ext 10.8 COM+ Cont ext At t ribut es 10.9 COM+ Obj ect Pooling 10.10 COM+ Just - in- Tim e Act ivat ion 10.11 COM+ Const r uct or St r ing 10.12 COM+ Transact ions 10.13 COM+ Sy nchronizat ion 10.14 Pr ogram m ing t he COM+ Cat alog 10.15 COM+ Secur it y 10.16 COM+ Queued Com ponent s 10.17 COM+ Loosely Coupled Ev ent s 10.18 Sum m ary A. The COM+ Logbook A.1 Logbook Requirem ent s A.2 Log File Exam ple A.3 Using t he Logbook A.4 Configur ing t he Logbook A.5 How Does t he Logbook Work? A.6 Sum m ary B. COM+ 1.5 B.1 I m prov ed User I nt erface Usabilit y B.2 Legacy Applicat ions and Com ponent s B.3 Disabling Applicat ions and Com ponent s B.4 Pausing Applicat ions B.5 Serv ice Act iv at ion Type B.6 I m prov ed Queuing Support B.7 Applicat ion Pooling and Recycling B.8 Applicat ion Dum p B.9 Applicat ion Part it ioning B.10 Aliasing Com ponent s B.11 Configurable Transact ion I solat ion Level B.12 I m prov ed Cont ext Act ivat ion Set t ing B.13 Pr ivat e Com ponent s B.14 Web Serv ices in COM+ 1.5 B.15 Sum m ary C. I nt r oduct ion t o .NET C.1 .NET Program m ing Languages C.2 Packaging .NET Com ponent s: Assem blies C.3 Dev eloping .NET Com ponent s
4
C.4 Wr it ing .NET Client - Side Code C.5 .NET as a Com ponent Technology C.6 Com posing Assem blies Colophon
5
D e dica t ion To m y wife, Dana
6
For e w or d I fir st ran int o COM+ back in 1996. I n t hose days, I was w ork ing as a Com m on Obj ect Request Br oker Ar chit ect ure ( CORBA) consult ant and was fr esh out of I BM, wher e I had been heav ily inv olved in I BM’s or iginal CORBA im plem ent at ion. CORBA was t he first of t he ar chit ect ures t hat we m ight describe t oday as Dist ribut ed Com ponent ar chit ect ures, which set t he st age for bot h COM/ DCOM in t he Microsoft space and RMI / I I OP in t he Java space. Back t hen, I was int er est ed in a part icularly k not t y problem relat ed t o dist ribut ed com ponent archit ect ur es. Syst em s built w it h such archit ect ures had a charact er ist ic perform ance pat t ern. They could handle large num ber s of t r ansact ions, as long as t hose t ransact ions originat ed from a sm all num ber of client s. So, for ex am ple, 5,000 t r ansact ions per m inut e div ided bet ween 5 client s wor ked fine. But when t hose sam e 5,000 t r ansact ions per m inut e wer e split am ong 1,000 client s, each pr ocessing 5 t ransact ions per m inut e, t he sy st em s chok ed. This was odd, I t hought . Why should 5 client s, each processing 1,000 t ransact ions per m inut e, be fundam ent ally different t han 1,000 client s, each pr ocessing 5 t ransact ions per m inut e? What is t he difference bet ween t he first 5,000 t r ansact ions per m inut e and t he second? Dist ribut ed com ponent archit ect ures, as t hey ex ist ed in 1996, dict at ed a one- t o- one relat ionship bet ween client s and com ponent inst ances. The business logic of such archit ect ur es is in t he com ponent inst ances. And it is t he business logic t hat m ak es t r ansact ional request s of t r ansact ional r esources, such as t he dat abase. I n order t o m ake t r ansact ional request s, t he com ponent inst ances r equire expensive r esources, such as dat abase connect ions. We run out of st eam ( i.e., t r ansact ional t hroughput ) when one of t wo t hings happen: we over load t he syst em w it h t r ansact ional request s or we run out of resources ( e.g., dat abase connect ions) . Clearly , going from 5 client s, each m ak ing 1,000 t ransact ional request s per m inut e, t o 1,000 client s, each m ak ing 5 t r ansact ional request s per m inut e, has no over all im pact on t he t ransact ional t hr oughput . Therefore, t he r eason why our dist ribut ed com ponent sy st em s m ust be dying is t hat we ar e running out of resour ces. So t he answer t o get t ing lot s of client s on a dist r ibut ed com ponent archit ect ure is not going t o com e fr om increased capabilit y of t he back- end t r ansact ional resources ( e.g., dat abases) . I t will have t o com e fr om som et hing else- som et hing t hat allows r esour ce shar ing. This, t hen, is t he problem I wor ked on back in 1996. How do y ou
7
get several client s t o share resources in a dist r ibut ed com ponent archit ect ure? The solut ion t o t his problem cam e fr om an unex pect ed source. I was asked t o writ e a book on COM and DCOM. I knew very lit t le about COM and DCOM back t hen. As I look ed over t he COM/ DCOM whit e paper s on t he Microsoft web sit e, I quickly recognized it as a t y pical dist ribut ed com ponent archit ect ure and predict ed t he sam e t hr oughput problem s I had seen in ot her dist r ibut ed com ponent sy st em s. As I browsed t hrough t he whit e paper s, I not iced an obscure bet a product called Micr osoft Transact ion Serv er ( MTS) . At first , I dism issed MTS as an API used t o m anage dist ribut ed t ransact ions. But as I read m ore about MTS, I r ealized t hat it had lit t le t o do wit h t r ansact ions. I nst ead, it at t ack ed a m uch m or e int erest ing problem : how t o shar e r esources am ong client s. I n a nut shell, MTS addr essed t he very problem t hat had so v exed t he exist ing dist ribut ed com ponent syst em s- how t o support a large num ber of lowt r ansact ion generat ing client s! I did event ually w rit e t hat book, as well as m any ar t icles on t he im port ance of t he ideas int r oduced by MTS. Many of t hese ar t icles appear ed in m y Obj ect Wat ch newslet t er ( available at www.obj ect wat ch.com ) , a newslet t er t hat has, ov er t im e, becom e influent ial in it s space. Back in 1996, I predict ed t hat MTS would be a hist or ically im port ant product - one t hat w ould redefine approaches t o scalabilit y in dist ribut ed com ponent sy st em s. I n fact , t hat predict ion has com e t r ue. Today , every infrast r uct ure designed t o suppor t high scalabilit y in dist r ibut ed com ponent syst em s is based dir ect ly on t he ideas, algorit hm s, and pr incipals first int roduced by MTS in 1996. Ent erprise Jav aBeans, for exam ple, t he Jav a scalabilit y infrast r uct ure, is alm ost a direct copy of MTS. But w hat does t his have t o do wit h COM+ , you m ay ask. I t t urns out t hat COM+ and MTS are one and t he sam e. Microsoft , never know n for it s m arket ing savvy , decided t o wait unt il cust om ers finally got used t o t he nam e MTS ( it self a m isleading nam e) , and t hen it pulled a fast one- it swit ched t he nam e! And not j ust any nam e, but one t hat would be as confusing as possible! So t hey renam ed MTS as COM+ . Nat urally, cust om ers assum ed t hat COM+ was t he next release of COM. I n fact , COM+ was t he next release of MTS. Now Microsoft has announced .NET. Once again, t he br illiant Microsoft m ark et ing organizat ion has left m any cust om er s confused. I s COM+ now dead? Far fr om it —.NET is a series of int er est ing new feat ur es, none of which r eplace COM+ . COM+ is st ill t he scalable infrast r uct ure t hat suppor t s resour ce shar ing and deals w it h t he m yr iad of issues ( such as securit y and t r ansact ion boundar y
8
m anagem ent ) t hat ar e so closely relat ed t o r esource sharing and so cr ucial t o dist ribut ed applicat ions. So whet her you ar e rushing int o Micr osoft ’s new .NET t echnology plat for m or t aking a wait and see at t it ude, if y ou need t o put a lot of client s around your sy st em , y ou need t o under st and COM+ . Ther efore, t his book is very t im ely . COM+ is going t o be wit h us for a long t im e. I t s nam e m ay change again, j ust t o confuse t he innocent ; but t he ideas, algor it hm s, and principals will not . COM+ , under what ever nam e, is here t o st ay ! Roger Sessions, CEO, Obj ect Wat ch, I nc. Publisher, Obj ect Wat ch newslet t er ( www.obj ect wat ch.com ) Aut hor, COM+ and t he Bat t le for t he Middle Tier Aust in, Tex as
9
Pr e fa ce This book discusses COM+ com ponent services. Each service is covered in it s own chapt er , and each chapt er discusses a sim ilar range of issues: t he pr oblem t he serv ice addr esses, possible solut ions t o t hat problem , an in- dept h descript ion of t he COM+ solut ion, t r adeoffs, design, and im plem ent at ion guidelines, t ips, and know n pit falls. I have t ried t o prov ide useful design inform at ion and lessons I learned while applying COM+ . I also descr ibe COM+ helper classes and ut ilit ies I developed t hat will enhance your pr oduct ivit y significant ly . ( The COM+ Event s helper obj ect s and t he COM+ Logbook are prim e ex am ples.) This book focuses on t he " how t o" — t hat is, it pr ov ides pract ical inform at ion. You should read t he chapt er s in or der , since m ost chapt ers r ely on inform at ion discussed in t he pr eceding chapt er s. The book also aim s t o ex plain COM+ st ep by st ep. A soft ware engineer alr eady fam iliar wit h COM who want s t o k now what COM+ is and how t o use it can read t his book and st ar t developing COM+ applicat ions im m ediat ely .
Scope of Th is Book Her e is a brief sum m ary of t he chapt ers and appendixes in t his book: •
•
•
Chapt er 1 int roduces t he Com ponent Serv ices Ex plorer and basic COM+ t er m inology. This chapt er deliberat ely holds your hand as you develop your fir st " Hello World" COM+ com ponent . Subsequent chapt ers do m uch less handholding and assum e y ou ar e fam iliar w it h t he COM+ env ir onm ent . I f you alr eady have experience w it h basic COM+ developm ent, feel fr ee t o sk ip t his chapt er. Chapt er 2 dem yst ifies t he COM+ cont ext by present ing it as t he key m echanism for providing com ponent services using call int ercept ion. Generally , y ou need not be concerned wit h cont ext s at all. How ever, t he COM+ cont ext underlies t he way COM+ serv ices are im plem ent ed. Chapt er 3 describes t wo scalabilit y - enabling m echanism s t hat COM+ provides for a m odern ent erprise applicat ion: obj ect pooling and Just - in- Tim e Act iv at ion ( JI TA) . The discussion of inst ance m anagem ent , and especially JI TA, is independent of t r ansact ions. Ear ly COM+ docum ent at ion and books t ended t o couple inst ance m anagem ent and t r ansact ions. How ever, I found t hat not only can you use inst ance m anagem ent independent ly of t ransact ions, but it is easier t o explain it t hat
10
•
•
•
•
•
•
way. Besides explaining how t o best use obj ect pooling and JI TA, Chapt er 3 describes ot her act iv at ion and inst ance m anagem ent COM+ serv ices such as t he const ruct or st ring. Chapt er 4 explains t he difficult , yet com m on, problem s t hat t r ansact ions address, and provides you wit h a dist illed overv iew of t r ansact ion pr ocessing and t he t r ansact ion program m ing m odel. The difficult par t of wr it ing t his chapt er was finding a way t o convey t he right am ount of t r ansact ion processing t heory . I want t o help y ou under st and and accept t he result ing program m ing m odel, but not bur y you in t he det ails of t heory and COM+ plum bing. This chapt er focuses on COM+ t r ansact ion ar chit ect ure and t he r esult ing design considerat ions you have t o be aware of. Chapt er 5 first explains t he need in t he com ponent world for a concurr ency m odel and t he lim it at ions of t he classic COM solut ion. I t t hen descr ibes how t he COM+ solut ion, act ivit ies, im pr oves deficiencies of apar t m ent s. Chapt er 6 shows how t o access com ponent and applicat ion configurat ion inform at ion pr ogr am m at ically using t he COM+ Cat alog int er faces and obj ect s. Pr ogr am m at ic access is requir ed when using som e adv anced COM+ serv ices and t o aut om at e set up and dev elopm ent t ask s. This chapt er pr ov ides you wit h com pr ehensiv e cat alog st ruct ure diagram s, plent y of sam ple code, and a handy ut ilit y. Chapt er 7 explains how t o secure a m odern applicat ion using t he rich and power ful ( y et easy t o use) securit y infrast r uct ure provided by COM+ . This chapt er defines basic securit y concept s and shows you how t o design securit y int o y our applicat ion from t he ground up. You can design t his secur it y by using COM+ declar at ive secur it y via t he Com ponent Serv ices Explorer and by using advanced program m at ic securit y . Chapt er 8 explains what COM+ queued com ponent s ar e and how t o use t hem t o dev elop asy nchronous, pot ent ially disconnect ed applicat ions and com ponent s. I n addit ion t o showing you how t o configur e queued com ponent s, t his chapt er addresses required changes t o t he program m ing m odel. I f y ou have ever had t o dev elop an asynchronous m et hod invocat ion opt ion for y our com ponent s, you w ill lov e COM+ queued com ponent s. Chapt er 9 covers COM+ loosely coupled ev ent s, why t her e is a need for such a ser vice, and how t he ser vice t ies int o ot her COM+ serv ices described in ear lier chapt er s ( such as t r ansact ions, securit y , and queued com ponent s) . Many people consider COM+ event s t heir fav or it e ser vice. I f you hav e had t o confront COM connect ion point s, you will appreciat e COM+ Event s.
11
•
•
•
•
Chapt er 10 shows how .NET com ponent s can t ake adv ant age of t he com ponent services descr ibed in t he previous chapt ers. I f you ar e not fam iliar wit h .NET, I suggest you read Appendix C fir st —it cont ains an int roduct ion t o .NET and C# . Chapt er 10 repeat s in C# m any of t he C+ + or VB 6.0 code sam ples found in earlier chapt er s, showing you how t o im plem ent t hem in .NET. Appendix A helps you develop a useful and im port ant ut ilit y — a flight r ecorder t hat logs m et hod calls, er rors, and event s in your applicat ion. Logging is an essent ial part of ev ery applicat ion and is especially im por t ant in an ent er prise env ironm ent . The logbook is also an ex cellent ex am ple of t he sy nergies ar riv ed at by com bining m ult iple COM+ serv ices. I t is also a good represent at ion of t he design approaches you m ay consider when com bining ser vices. Appendix B describes t he changes, im provem ent s, and enhancem ent s int roduced t o COM+ in t he next r elease of Windows, Windows XP. I nst ead of writ ing t he book as if Windows XP were av ailable now ( as of t his w rit ing it is only in bet a) , I chose t o wr it e t he book for t he developer w ho has t o deliver applicat ions t oday, using Windows 2000. When y ou st ar t using Window s XP, all y ou need t o do is read Appendix B—it cont ains t he addit ional inform at ion you need. Appendix C describes t he essent ial elem ent s of t he .NET fr am ework, such as t he r unt im e, assem blies, and how t o dev elop .NET com ponent s. The appendix allows a reader who is not fam iliar wit h .NET t o follow Chapt er 10.
Som e Assu m pt ion s Abou t t h e Re a der I assum e t hat you are an experienced COM developer who feels com for t able wit h COM basics such as int erfaces, CoClasses, and apart m ent s. This book is about COM+ com ponent services, not t he com ponent t echnology used t o dev elop a COM/ DCOM or .NET com ponent . You can st ill r ead t he book wit hout t his experience, but you will benefit m or e by having COM under your belt . I assum e y ou dev elop your com ponent s m ost ly in C+ + and ATL and t hat you writ e occasional, sim ple client code in Visual Basic. I also use t rivial C# in Chapt er 10 t o dem onst rat e how .NET t akes adv ant age of COM+ serv ices, but you don't need t o know C# t o r ead t hat chapt er . A .NET developer should also find t his book useful: r ead and under st and t he ser vices in Chapt er 1 t hr ough Chapt er 9, and t hen use Chapt er 10 as a refer ence guide for t he sy nt ax of .NET at t r ibut es.
12
D ef in it ion s a n d Te x t Con ve n t ion s The following definit ions and conv ent ions apply t hr oughout t his book: •
•
•
•
A com ponent is an im plem ent at ion of a set of int er faces. A com ponent is what y ou m ark in your I DL file ( or t ype libr ary ) wit h CoClass or a class in C# . An obj ect is an inst ance of a com ponent . You can creat e obj ect s by calling CoCreateInstance( ) in C+ + , specify ing t he class I D ( t he t ype) of t he obj ect you want t o creat e. I f you use Visual Basic 6.0, you can cr eat e obj ect s using new or CreateObject( ). A C# client uses new t o creat e a new inst ance of a com ponent . I use t he following t erm s in t he book: CoCr eat ing refers t o calling CoCreateInstance() in C+ + , or new or CreateObject( ) in Visual Basic. Query ing an obj ect for an int er face r efer s t o calling IUnknown::QueryInterface( ) on t he obj ect . Releasing an obj ect refers t o calling IUnknown::Release( ) on t he obj ect . The graphical not at ions in Figur e P- 1 are used in alm ost ev ery design diagram in t he book. The " lollipop" denot es an int er face, and a m et hod call on an int er face is r epresent ed by an ar row beginning wit h a full circle. Figu r e P- 1 . I n t e rfa ce a n d m e t h od call g ra ph ica l n ot a t ion s
•
Err or handling in t he code sam ples is r udim ent ar y. The code sam ples serve t o dem onst r at e a design or a t echnical point , and clut t er ing t hem wit h t oo m uch er ror handing would m iss t he point . I n a pr oduct ion envir onm ent , y ou should ver ify t he ret ur ned HRESULT of every COM call, cat ch and handle except ions in C# , and assert every assum pt ion.
I use t he following font convent ions in t his book: • •
I t alic is used for new t erm s, cit at ions, online links, filenam es, dir ect ories, and pat hnam es. Constant width is used t o indicat e com m and- line com put er out put and code exam ples, as well as classes, const ant s, funct ions, int erfaces, m et hods, variables, and flow- cont r olled st at em ent s.
13
• •
Constant-width bold is used for code em phasis and user input . Constant-width italic is used t o indicat e r eplaceable elem ent s in code st at em ent s. This icon indicat es a not e or t ip.
This icon indicat es a war ning.
Ot h er COM + Book s a n d Refe r e n ce s This book describes how t o use COM+ com ponent serv ices in y our applicat ion. I t focuses on how t o apply t he t echnology , how t o av oid specific pit falls, and design guidelines. I f you want t o know m or e about COM+ in general and t he nat ure of com ponent t echnology , I recom m end t he following t wo books t hat helped m e a great deal in m y at t em pt t o gr asp COM+ . COM+ and t he Bat t le for t he Middle Tier by Roger Sessions ( John Wiley & Sons, 2000) is hands down t he best " why" COM+ book. I t explains in det ail, w it h excellent exam ples and in plain language, t he need for soft ware com ponent s and com ponent services. For exam ple, inst ead of t he page or t wo t his book includes on t he m ot ivat ion for using t r ansact ions, Sessions devot es t wo fascinat ing chapt er s t o t he t opic. The book goes on t o com par e ex ist ing com ponent t echnologies ( such as COM, CORBA, and Jav a) and t heir corr esponding suit es of com ponent serv ices. I t also cont ains a few case st udies fr om r eal- life syst em s t hat use COM+ . Roger Sessions also has a unique way of eloquent ly nam ing t hings—pr ov iding t he m ost appr opr iat e t erm , which is oft en not t he nam e Microsoft uses. Whenever it m akes sense, t his book uses Sessions' t er m inology, such as " inst ance m anagem ent " inst ead of t he Microsoft t er m " act ivat ion." Underst anding COM+ by David S. Plat t ( Micr osoft Press, 1999) is probably t he best " what " COM+ book . The book describes t he serv ices available by COM+ and provides sidebar sum m aries for t he busy reader . I t is one of t he first COM+ books, and Plat t worked on it closely wit h t he COM+ t eam . I also used t he MSDN Librar y ext ensively, especially t he " Com ponent Services" sect ion, w hile writ ing t his book. Alt hough t he infor m at ion in t his libr ary t ends t o be t erse, t he over all st r uct ure is good. Use t his book t o learn how t o apply COM+ product ively and
14
effect ively , and use t he MSDN Librar y as a reference for t echnical det ails and a sour ce for addit ional infor m at ion.
H ow t o Con t a ct Us We have t est ed and v erified t he inform at ion in t his book t o t he best of our abilit y, but y ou m ay find t hat feat ures have changed ( or even t hat we hav e m ade m ist akes! ) . Please address com m ent s and quest ions concer ning t his book t o t he publisher: O’Reilly & Associat es, I nc. 101 Morris St reet Sebast opol, CA 95472 ( 800) 998- 9938 ( in t he Unit ed St at es or Canada) ( 707) 829- 0515 ( int ernat ional/ local) ( 707) 829- 0104 ( fax) The web sit e for t he book list s exam ples, err at a, and plans for fut ure edit ions. You can access t his page at : ht t p: / / www.or eilly .com / cat alog/ com dot net svs To ask t echnical quest ions or com m ent on t his book, send em ail t o: bookquest ions@or eilly .com Or t o m e dir ect ly: j uval.low y@com ponent w are.net For m or e inform at ion about our books, conferences, soft ware, resource cent ers, and t he O’Reilly Net work, see our web sit e: ht t p: / / www.or eilly .com
Ack now le dgm e n t s A book is by no m eans t he pr oduct of j ust t he aut hor’s work. I t is t he result of m any ev ent s and individuals, like links in a chain. I cannot possibly nam e everyone, ranging from m y parent s t o m y fr iends. I am especially gr at eful for m y t wo friends and colleagues, Marcus Pellet ier and Chris W. Rea. Marcus wor ked wit h m e on large COM+ proj ect s, and t oget her we confront ed t he unknown. Marcus’s t horoughness and t echnical expert ise is a m odel for ev ery program m er . Chris’s com m ent s and insight int o a r eader ’s m ind hav e cont ribut ed great ly t o t his book ’s accuracy, int egrit y , and flow. I w ish t o t hank Yasser Shohoud for v erifying m y appr oach t o t r ansact ion processing and sharing wit h m e his own, Richar d Grim es for r eviewing t he book, and Roger Sessions for writ ing t he For eword. Thank s also t o Johnny Blum enst ock for providing m e wit h a place t o writ e. Finally, t his book would not be possible wit hout m y
15
wife, Dana, whose const ant support and encouragem ent m ade t his book a r ealit y. Thank you, Dana.
16
Cha pt e r 1 . COM + Com pone n t Se r vice s By now, m ost developers of lar ge- scale ent erprise applicat ions ar e convinced of t he benefit s of com ponent - orient ed developm ent . They hav e discover ed t hat by br eak ing a large sy st em down int o sm aller unit s, t hey can w rit e code t hat is easier t o reuse on ot her proj ect s, easier t o dist r ibut e across m ult iple com put ers, and easier t o m aint ain. As long as t hese com ponent s adhere t o a binary st andard t hat defines how t hey com m unicat e wit h one anot her, t hey can be inv oked as needed at runt im e and discarded when t hey have finished t heir wor k. This t y pe of applicat ion is also par t icularly suit ed t o t he Web, wher e client s request ser vices of rem ot e applicat ions and t hen, once sat isfied, m ove on t o ot her t asks. For nearly a decade, t he Microsoft Com ponent Obj ect Model ( COM) has been t he st andard for com ponent s t hat r un on Windows m achines, including Windows 9x and Me client s and Windows NT and 2000 server s. The COM m odel is well docum ent ed by t he Microsoft Com ponent Obj ect Model Specificat ion. Tools such as Visual C+ + and Visual Basic m ake it easy t o cr eat e COM com ponent s, and scor es of book s, t raining classes, and art icles are available t o t each pr ogr am m ers how t o use t hem . Many feat ures of t he Windows operat ing syst em are now im plem ent ed as COM com ponent s, and m any com panies have inv est ed heav ily in COMbased sy st em s of t heir own. I n July 2000, Microsoft announced a radically new com ponent m odel as par t of it s .NET developm ent plat form , suddenly calling int o quest ion t he viabilit y of exist ing COM applicat ions. .NET com ponent s bear lit t le resem blance t o legacy COM com ponent s and ar e not backw ards com pat ible. They can be m ade t o int eroper at e wit h COM com ponent s but do not do so nat ur ally. When it com es t o t he services and t ools program m er s use t o build ent er prise- scale .NET applicat ions, how ever, one facilit y cont inues t o prov ide t he necessary runt im e infrast ruct ur e and ser vices: COM+ com ponent services. These serv ices have been available on Windows 2000 since it s release, but t hey will gain great er im port ance in t he m ont hs ahead. As it t ur ns out , t hey offer a bridge bet ween t radit ional COM and .NET applicat ions, m ak ing y our underst anding and m ast er y of t hem as im port ant now as it has ever been. I n t his chapt er, we prov ide a quick over view of t he COM+ suit e of com ponent services and t hen int roduce y ou t o t he Com ponent Serv ices Explorer, your pr im ary t ool for building and m anaging bot h COM and .NET ent erpr ise applicat ions. You will also creat e, debug, and deploy a sim ple COM+ " Hello Wor ld" applicat ion, using a t r adit ional COM com ponent and learning about COM+ applicat ion t y pes and configured com ponent s as y ou do so.
17
1 .1 COM+ Com pon e n t Ser vice s Com ponent s need runt im e services t o work. The original COM runt im e support ed com ponent s locat ed on t he sam e m achine, t y pically a desk t op PC. As t he focus of Windows developm ent shift ed from st andalone PCs t o net worked syst em s, Microsoft found it necessary t o add addit ional serv ices ( see The Evolut ion of COM+ Serv ices) . Fir st , t hey added support for dist r ibut ed applicat ions, or applicat ions whose com ponent s ar e locat ed on m or e t han one m achine ( som et im es r eferred t o as " COM on a wire" ) . Lat er , Microsoft added new services t o support ent er prise applicat ions, whose com plexit y and scale placed new dem ands on t he r esources of a syst em and required an ent irely new level of support . These t r ends were only exacer bat ed by t he m ove t o web- based applicat ions aim ed at huge num ber s of cust om er s connect ed over t he public I nt ernet . Collect ively, t he services t hat support COM and .NET com ponent based applicat ions ar e known as t he COM+ com ponent ser vices, or sim ply as COM+ .
The Evolut ion of COM + Se r vice s COM solved a num ber of pr oblem s facing early com ponent dev eloper s by prov iding a binary st andar d for com ponent s, defining a com m unicat ion int er face, and providing a way to link com ponent s dynam ically. COM freed developer s fr om hav ing t o deal w it h " plum bing" and connect ivit y issues, allowing t hem t o concent rat e on designing com ponent s. By t he m id- 1990s, however , it was clear t hat Windows dev eloper s needed addit ional ser vices t o suppor t dist ribut ed and t ransact ion- or ient ed applicat ions. Dist r ibut ed COM ( DCOM) was released in 1995, a specificat ion and ser vice used t o dist r ibut e com ponent s across differ ent m achines and inv oke t hem rem ot ely. Then, Microsoft r eleased t he Micr osoft Transact ion Serv er ( MTS) in 1998, which provided com ponent developers wit h new ser vices for t ransact ion m anagem ent , declarat ive r ole- based securit y , inst ance act ivat ion m anagem ent , com ponent deploym ent and inst allat ion, and an adm inist rat ion t ool for m anaging com ponent configurat ions. Ther e was m ore t o MTS t han j ust new ser vices. MTS represent ed a program m ing m odel in w hich t he com ponent dev eloper sim ply declared ( using t he MTS adm inist r at ive t ool) which ser vices a com ponent r equired, and left it t o MTS t o prov ide an appr opr iat e r unt im e env ir onm ent . Dev eloper s 18
could now spend even less effor t on low - level serv ice plum bing ( such as int eract ing wit h t ransact ion pr ocessing m onit ors or m anaging t he life cycle of an obj ect ) , and m ore on t he business logic t he cust om er paid for. Yet , MTS had it s lim it at ions. Forem ost was t he fact t hat MTS was built on t op of conv ent ional COM/ DCOM. The underlying operat ing sy st em and COM it self wer e unawar e t hat MTS ev en exist ed. MTS resort ed t o esot eric hacks and kludges t o provide it s serv ices, and MTS could not provide it s ser vices t o every COM obj ect ( m ult it hreaded apart m ent obj ect s were excluded) . Som e serv ices, such as obj ect pooling, were eit her not possible or unav ailable. The developm ent of a new v ersion of t he Windows NT operat ing syst em ( init ially called NT 5.0 and lat er renam ed Windows 2000) , gave Microsoft an oppor t unit y t o cor rect t he deficiencies of MTS and DCOM by fusing t hem int o a new com pr ehensiv e com ponent services suit e. Micr osoft added yet m or e services, including obj ect pooling, queued com ponent s, and event s, and m ade t he package a part of t he core Windows operat ing syst em . The new suit e of serv ices was nam ed COM+ 1.0, t he subj ect of t his book. The nex t version of COM+ , COM+ 1.5, is scheduled for r elease wit h Windows XP in Q4 2001 and is described in Appendix B. The COM+ acronym is an ov erloaded and oft en m isused t erm . Today it is used inform ally t o refer t o bot h t he lat est ver sion of t he COM com ponent specificat ion and t he com ponent services available on t he lat est ver sions of Windows. I n t his book, we use t he t er m COM+ t o refer t o t he COM+ com ponent services. When we speak of COM+ com ponent s, we refer t o COM com ponent s configured t o run under t hose services. Howev er, as y ou will see, a COM+ applicat ion m ay consist of eit her COM or .NET com ponent s ( see COM+ : The Migrat ion Pat h t o .NET) . Her e is a quick sum m ar y of t he m ost im port ant serv ices prov ided by COM+ : Adm inist r at ion Tools and serv ices t hat enable program m ers and adm inist rat ors t o configure and m anage com ponent s and com ponent - based applicat ions. The m ost im por t ant t ool is t he Microsoft Managem ent Console Com ponent Serv ices Ex plorer. COM+ also prov ides a st andard locat ion, t he COM+ Cat alog, for st oring configur at ion infor m at ion. The Com ponent Services Explorer is explained in t he following sect ion. The COM+ Cat alog is descr ibed in Chapt er 6. Just - in- Tim e Act ivat ion ( JI TA)
19
Serv ices t hat inst ant iat e com ponent s w hen t hey are called and discard t hem when t heir wor k is done. JI TA is ex plained in Chapt er 3. Obj ect pooling Serv ices t hat allow inst ances of fr equent ly used, but expensive, r esour ces, such as dat abase connect ions, t o be m aint ained in a pool for use by num er ous client s. Obj ect pooling can im prove t he per form ance and responsiveness of a dist ribut ed applicat ion dram at ically . I t is explained in Chapt er 3. Transact ions Serv ices t hat allow oper at ions carr ied out by dist r ibut ed com ponent s and resources such as dat abases t o be t reat ed as a single oper at ion. Transact ion m anagem ent is a requirem ent of m ost com m ercial syst em s. COM+ Tr ansact ion ser vices are discussed in Chapt er 4. Sy nchr onizat ion Serv ices for cont rolling concurrent access t o obj ect s. These serv ices are explained in Chapt er 5. Securit y Serv ices for aut hent icat ing client s and cont rolling access t o an applicat ion. COM+ support s r ole- based securit y , which is explained in Chapt er 7. Queued com ponent s Serv ices t hat allow com ponent s t o com m unicat e t hrough asynchronous m essaging, a feat ur e t hat m ak es possible loosely coupled applicat ions or even disconnect ed applicat ions. Queued com ponent s are discussed in Chapt er 8. Event s Serv ices t hat allow com ponent s t o infor m one anot her of significant ev ent s, such as changes in dat a or sy st em st at e. COM+ suppor t s a publish- subscr ibe m odel of event not ificat ion, which is described in Chapt er 9. To sum m arize, COM+ is about com ponent ser vices and has alm ost not hing t o do wit h t he way a com ponent is developed. The .NET fr am ework allows you t o develop binary com ponent s m or e easily t han does COM, but it cont inues t o r ely on com ponent serv ices available t hrough COM+ . The m anner in which .NET and COM com ponent s ar e configur ed t o use t hese ser vices, howev er , is not t he sam e. Cur rent ly, m ost Windows ent erpr ise developer s are dev eloping applicat ions based on t he exist ing COM st andard using Visual Basic 6 and Visual C+ + 6 wit h ATL. For t his reason, t his book uses COM ex am ples t o dem onst rat e COM+ . However, t hese sam e serv ices are available t o .NET com ponent s as well. Chapt er 10 shows you how t o use t hem . COM+ 1.0 is an int egr al part of Windows 2000 and r equir es no special inst allat ion. Som e COM+ feat ur es ar e av ailable only when
20
bot h t he client and server are running on Windows 2000 m achines, but COM+ client s can usually r un on Windows 9.x and Windows NT m achines as well.
COM + : The M igr a t ion Pa t h t o .N ET .NET is Microsoft ’s next - generat ion com ponent t echnology and applicat ion developm ent plat for m . ( For a quick overv iew of t he .NET plat for m , see Appendix C.) However , adopt ing a radically new t echnology such as .NET is never an easy endeav or for com panies and dev eloper s. Most have m ade a considerable invest m ent in an ex ist ing, oft en COM- based, code base and t he developer sk ills needed t o m aint ain it . Unless com panies hav e a com pelling reason t o m ove t o .NET or a r easonable m igr at ion pat h, t hey post pone or avoid m ak ing t he change. However, because COM and .NET com ponent s can coexist in t he sam e COM+ applicat ion, com panies can cont inue t o build COM com ponent s t oday , adding .NET ser viced com ponent s t o t heir applicat ions at a lat er t im e when t he advant ages of doing so are m ore com pelling. This is a m igrat ion st r at egy wort h y our considerat ion. When Windows XP is released in Q4 2001, it will include a new ver sion of COM+ com ponent ser vices, COM+ 1.5. This new ver sion im pr oves COM+ 1.0 usabilit y and addr esses som e of t he pit falls of using COM+ 1.0 on Windows 2000, as described in t his book. COM+ 1.5 also adds new feat ur es t o ex ist ing services and lay s t he foundat ion for int egrat ion wit h .NET web serv ices. Appendix B sum m ar izes t he fort hcom ing changes.
1 .2 Th e Com pone n t Ser vice s Ex plor e r COM+ com ponent s and applicat ions ar e m anaged t hrough t he Com ponent Services Explorer ( form erly known as t he COM+ Explorer) .The Com ponent Serv ices Explorer is a Microsoft Managem ent Console snap- in and is available on ev ery Windows 2000 m achine. To fir e up t he Com ponent Ser vices Explor er, go t o t he St art m enu and select Set t ings Cont rol Panel. When t he Cont r ol Panel window appear s, select t he Adm inist rat ive Tools direct ory and t hen select t he Com ponent Ser vices applicat ion. The first t hing you should do aft er locat ing t he Com ponent Ser vices Explorer is cr eat e a shor t cut t o it on y our desk t op. As a dev eloper , you need easy access t o t he Com ponent Services Explor er , your m ain gat eway int o COM+ ( see Figure 1- 1) . You can use t he Com ponent Services Explorer t o creat e and configur e COM+ 21
applicat ions, im port and configure COM or .NET com ponent s, expor t and deploy your applicat ions, and adm inist er y our local m achine. You can even adm inist er COM+ on ot her m achines on t he net work, provided y ou have adm inist rat ive priv ileges on t hose m achines. A COM+ applicat ion is a logical gr oup of COM+ com ponent s. Com ponent s usually share an applicat ion if t hey depend on one anot her t o accom plish t heir t ask s and when all t he com ponent s requir e t he sam e applicat ion level configur at ion, as wit h securit y or act ivat ion policy . Com ponent s in t he sam e applicat ion are oft en dev eloped by t he sam e t eam , and are m eant t o be deployed t oget her . You can see all t he COM+ applicat ions inst alled on your m achine by opening t he Com ponent Ser vices Explor er and expanding t he My Com put er Com put er s folder in t he Tr ee window: Com put ers COM+ Applicat ions. Ev ery icon in t he COM+ Applicat ions folder represent s a COM+ applicat ion. Each COM+ applicat ion cont ains COM+ com ponent s. Com ponent s m ust be explicit ly im port ed int o t he Com ponent Ser vices Explor er t o t ak e advant age of COM+ serv ices. The Com ponent Serv ices Explorer offers a hier archical approach t o m anaging COM+ serv ices and configur at ions: a com put er cont ains applicat ions, and an applicat ion cont ains com ponent s. A com ponent has int erfaces, and an int erface has m et hods. Each it em in t he hierar chy has it s ow n configurable proper t ies. Not e t hat t he hierar chy allows you t o v iew t he par am et ers of any m et hod list ed in t he hierarchy. Figu r e 1 - 1 . Th e Com p on e n t Se r vices Ex p lor e r
1 .3 H ello COM+ The best way t o becom e acquaint ed wit h t he Com ponent Services Explorer and basic COM+ t erm inology is t o do a t r ivial exam ple. This sect ion walks you t hrough t he COM+ equiv alent of t he
22
canonical " Hello World" pr ogr am . You will build a COM+ applicat ion cont aining a COM com ponent t hat displays a m essage box say ing " Hello COM+ " . When developing your " Hello COM+ " applicat ion, follow t hese st eps: 1. Cr eat e a classic COM com ponent . All COM+ com ponent s st ar t t heir life as classic COM com ponent s, developed wit h such t ools as ATL, MFC, or Visual Basic 6.0. 2. Cr eat e a new COM+ applicat ion t o host t he com ponent . 3. Add t he com ponent t o t he applicat ion. 4. Writ e a client and t est t he com ponent . The r est of t his chapt er uses t his " Hello COM+ " exam ple t o dem onst r at e v arious COM+ feat ures and capabilit ies. The exam ple is also available as part of t he sour ce files provided wit h t his book ( see t he Preface for inform at ion on how t o access t hese files) . 1 .3 .1 Bu ildin g a COM Com pon e n t We will use ATL 7.0 t o gener at e a classic COM com ponent , alt hough you can also do it in Visual Basic 6.0 wit h alm ost t he sam e ease. St ar t a new ATL pr oj ect in Visual St udio.NET and nam e it Hello. For sim plicit y, do not use At t r ibut ed proj ect ( deselect At t ribut ed in t he ATL Proj ect Wizard under Applicat ion Set t ings) . Also, do not select COM+ 1.0 support . This select ion adds a few int er faces ex plained in subsequent chapt ers t hat are not relevant t o t his ex am ple. Bring up t he Add Class dialog ATL and select t he Sim ple ATL Obj ect it em . This st ep should bring up t he ATL Sim ple Obj ect Wizar d dialog ( see Figure 1- 2) . Type t he following ent r ies, in order: 1. I n t he Short Nam e field, ent er Message . 2. I n t he CoClass field, ent er Hello . Your com plet ed dialog should look like Figure 1- 2. There is no need t o access t he Opt ions select ion in t he dialog ( j ust use t he default s) . Click OK when y ou’re done. Figu r e 1 - 2 . Use t h e ATL ob j ect w iz ar d t o gen e ra t e a sim p le COM ob j e ct
23
Right - click t he I Message int er face icon in t he Class View, and select Add and t hen Add Met hod... fr om t he pop- up cont ex t m enu. This st ep brings up t he Add Met hod Wizar d. Ent er ShowMessage as t he m et hod nam e and click OK. Aft er following t hese st eps, t he ATL Obj ect Wizard will generat e a new int erface definit ion in t he pr oj ect I DL file, and t he new m et hod wizar d will add a m et hod t o t hat int er face. Ver ify t hat t he int erface definit ion in t he I DL file looks lik e t his: [ //various IDL attributes ] interface IMessage : IDispatch { [id(1), helpstring("method ShowMessage")] HRESULT ShowMessage( ); }; Also m ake sur e t hat t he I DL file cont ains a t ype library section wit h t he CoClass definit ion: [ //you will have a different CLSID here: uuid(C530E78E-9EE4-47D3-86CC-3B4EE39CBD26), helpstring("Message Class") ] coclass Hello { [default] interface IMessage; }; Nex t , go t o t he m essage.cpp file and im plem ent t he ShowMessage( ) m et hod of t he CMessage class: STDMETHODIMP CMessage::ShowMessage( ) {
24
::MessageBox(::GetActiveWindow( ),"Hello COM+","First COM+ Application",MB_OK); return S_OK; } You can now com pile and build t he DLL. Every COM+ com ponent m ust r eside in a DLL, and t hat DLL m ust cont ain a t ype library em bedded in it as a resource. ATL will com pile and build t he DLL for you and add a reference t o t he t y pe librar y in t he proj ect r esource file, t he hello.rc file. COM+ does not requir e y ou t o regist er your com ponent , alt hough t he ATL build process w ill regist er it for you. As y ou will see lat er, COM+ m aint ains it s own com ponent s regist r at ion and configurat ion reposit ory. 1 .3 .2 Cr e a t in g a COM + Applica t ion Open t he Com ponent Ser vices Explorer and expand My Com put er COM+ Applicat ions folder . Right - click t he COM+ Applicat ions folder and select New Applicat ion from t he pop- up cont ext m enu. This st ep br ings up t he Applicat ion I nst all Wizard. Click Next on t he first wizar d screen. I n t he next wizard screen, select t he Creat e an Em pt y Applicat ion opt ion in t he next wizard screen. Now t he wizard will let you specify t he new applicat ion nam e and it s applicat ion t y pe, which can be eit her a libr ary or a server t ype ( see Figure 1- 3) . Ent er Hello COM+ for t he applicat ion nam e, and change t he applicat ion t ype from t he default Serv er applicat ion t o Library applicat ion. A library applicat ion indicat es t hat t he com ponent s in t he applicat ion will be loaded direct ly in t he process of t heir client s ( lik e a classic COM inproc serv er) . A ser ver applicat ion indicat es t hat t he com ponent s will run in t heir own pr ocess ( sim ilar t o a classic COM local ser ver) . You can alway s change t he applicat ion nam e and it s act ivat ion t ype lat er wit h t he Com ponent Ser vices Explorer . Click Nex t and Finish in t he last wizar d screen. You hav e j ust cr eat ed your fir st COM+ applicat ion. Figu re 1 - 3 . N a m in g you r n ew COM + a pp licat ion a n d con figu rin g it t o be a libr a ry or a ser ve r a pp lica t ion
25
I f you ex am ine t he Applicat ions folder now, you will see y our Hello COM+ applicat ion. Right - click it s icon and select Propert ies from t he pop- up cont ex t m enu. The applicat ion’s proper t ies page—a collect ion of t abs t hat let you configure t he applicat ion—will now appear . I n fact , every it em in t he Com ponent Ser vices Explor er ( applicat ions, com ponent s, int erfaces, m et hods, roles, and subscript ions) has a pr opert ies page accessible in t he sam e w ay ( by select ing Pr opert ies on t he it em 's cont ex t m enu or t he pr opert ies but t on on t he Com ponent Serv ices Explorer t oolbar ) . The Hello COM+ applicat ion's pr opert ies page is shown in Figur e 1- 4. The General t ab cont ains t he applicat ion nam e, which you can change her e if you'd lik e, and a descr ipt ion field. The descript ion field is a useful place t o put a few sent ences docum ent ing t he applicat ion's purpose, it s owner, et c. Each COM+ applicat ion is uniquely ident ified by a GUI D, called t he Applicat ion I D, show n at t he bot t om of t he Gener al t ab. You will alm ost nev er use t he Applicat ion I D dir ect ly, but COM+ uses it int ernally. Figu r e 1 - 4 . Th e a pp lica t ion p rope rt ie s p a ge
26
Ot her t abs on t he applicat ion propert ies page let y ou configur e t he applicat ion act iv at ion m ode, suppor t for queued com ponent s, securit y set t ings, and idle- t im e m anagem ent . Lat er chapt ers describe t hese applicat ion- lev el configurat ions in dept h. Close t he proper t ies page and ex am ine t he applicat ion’s Com ponent s folder. As you m ight expect , it is em pt y now. You will now add a new com ponent t o t his applicat ion. 1 .3 .3 Addin g a Com pone n t t o a COM + Applica t ion You can add a new com ponent t o your applicat ion ( not sur pr isingly ) by using anot her w izar d. Right - click t he Com ponent s folder , select New fr om t he pop- up cont ex t m enu, and click Com ponent . The Com ponent I nst all Wizard will now appear. Click Nex t on t he fir st scr een. On t he next scr een, select I nst all New Com ponent fr om t he t hr ee choices. The wizard will open a st andard file- open dialog box. Look for t he folder wher e you built hello.dll and select it . The wizar d will present you wit h all t he com ponent s it could find in t he specified DLL. I n t he case of hello.dll, t he wizar d shows only t he single com ponent cont ained in it ( see Figur e 1- 5) . The wizard act ually loads t he em bedded t ype library in t he DLL and look s for CoClass definit ions. You can use t he Add but t on t o specify addit ional DLLs. Not e t hat all t he com ponent s in t he select ed DLL will be added. I f you want t o add j ust a subset of t hem , you m ust add t hem all first and t hen rem ove t he ones t hat do not belong in t he applicat ion m anually. Click Next , and t hen click Finish in t he last wizard scr een. Your com ponent is now par t of t he Hello COM+ applicat ion.
Avoid using t he " I m port com ponent ( s) t hat are already r egist ered" opt ion in t he Com ponent I nst all Wizard. This opt ion has a bug and will not ret r ieve inform at ion about t he com ponent ( s) int er faces. You will not see t he com ponent ( s) int erfaces and m et hods in t he Com ponent Serv ices Explorer and will not be able t o configure t hem . Figu re 1 - 5 . Th e Com p on e n t I n st a ll W iz a rd
27
Because t y pe inform at ion is em bedded in t he DLL, COM+ knows about y our com ponent ’s int erfaces and m et hods. You can expand t he I nt er faces and Met hods folders ( under t he Hello.Message com ponent ) t o verify t hat COM+ has im por t ed t he com ponent corr ect ly. As shown in Figur e 1- 6, t he IMessage int er face and t he ShowMessage m et hod were bot h im por t ed. Figu r e 1 - 6 . Th e H ello COM + a pp lica t ion a n d it s con t a in e d com pon e n t
The I nt erfaces folder cont ains one ent ry for each int er face y our com ponent support s. The int erfaces on t he CoClass definit ion in t he t y pe libr ary det er m ine t he num ber of ent r ies. The Met hods folder
28
cont ains one it em for each m et hod in t hat int erface, again based on t he int er face definit ion in t he t y pe libr ary. 1 .3 .4 W r it in g a Te st Clie n t Client s can cr eat e t he com ponent using t he class I D CLSID_Hello ( C+ + ) or Hello ( Visual Basic 6.0) . Alt hough t he com ponent is now a COM+ com ponent and is part of a COM+ applicat ion, t he client side code is t he sam e as if t he com ponent wer e st ill a classic COM com ponent . To prove t his point ( and t est y our com ponent ) , wr it e a short C+ + client , such as t he code in Exam ple 1- 1. Ex a m p le 1 - 1 . A sim ple COM + clie n t
#import "Hello.dll" no_namespace named_guids ::CoInitialize(NULL); HRESULT hres = S_OK; IMessage* pMessage = NULL; hres = ::CoCreateInstance(CLSID_Hello,NULL,CLSCTX_ALL, IID_IMessage,(void**)&pMessage); hres = pMessage->ShowMessage( pMessage->Release( );
);
::CoUninitialize( ); When y ou r un t he client , you will see t he " Hello COM+ " m essage box ( see Figure 1- 7) . Figu r e 1 - 7 . Th e " H ello COM + " m e ssag e box from you r first COM + com pon e n t
Alt ernat ively, you can writ e t he client side in Visual Basic 6.0. Add t he com ponent t ype library Hello.TLB, t he Visual Basic pr oj ect references br owser , and wr it e: Dim obj As Hello Set obj = New Hello obj.ShowMessage set obj = Nothing Visual Basic 6.0 client s can also creat e t he obj ect using it s prog- I D. I n t hat case, t he t ype- libr ary is not r equired ( at t he expense of t y pe- safet y) :
29
Dim obj As Object Set obj = CreateObject("Hello.Message.1") obj.ShowMessage set obj = Nothing Because t he client side r em ains const ant , regar dless of t he com ponent configurat ion and applicat ion t ype, COM+ helps decouple t he client fr om t he server. This point is discussed in dept h in t he next chapt er.
1 .4 COM+ Con figu r ed Com pon e n t s COM+ allows you t o im port only in- proc ( DLL) com ponent s. You cannot im por t COM com ponent s t hat r eside in a local ser ver ( EXE) ; COM+ let s you configure t he act ivat ion t ype of your applicat ion, server , or library . I n t he case of a library , t he client sim ply loads t he original DLL int o it s pr ocess and uses t he com ponent . I f you configure t he applicat ion t o be a serv er applicat ion, COM+ prom ot es your or iginal DLL t o becom e a local server by host ing it in a surrogat e process of it s own. However, COM+ cannot m ak e a libr ar y applicat ion out of a COM local serv er. I n addit ion, m any COM+ serv ices require explicit pr ocess- lev el adm inist rat ion t hat t he local server ’s code sim ply does not cont ain. Once an in- proc com ponent is im port ed t o COM+ , it is called a configured com ponent t o em phasize t he fact t hat m uch com ponent funct ionalit y and behav ior is act ually configured and adm inist er ed out side t he com ponent . A classic COM com ponent ( be it in- pr oc or local) t hat has not been im port ed int o COM+ is called a nonconfigur ed com ponent . Configured and nonconfigur ed com ponent s can int eract freely and call each ot her’s int er faces. The configured com ponent m ust reside on a Window s 2000 m achine, but t he client of a configured com ponent can reside on any Windows- fam ily m achine, such as Windows NT, Windows Me, or Windows 9x. Configurat ion let s y ou cont rol t he way your applicat ion, com ponent , int er face, or m et hod behaves under COM+ . The COM+ dev elopm ent par adigm let s COM+ m anage as m uch of t he nonbusiness logic plum bing as possible by declar ing what serv ices y ou want t o use. Doing so let s y ou focus on t he dom ain problem you are t r ying t o solv e and add business v alue inst ead of plum bing code t o your product . Your configured com ponent ’s int er faces can be dual, dispat ch, or cust om int er faces. I f you use aut om at ion- com pliant int er faces, you do not need t o pr ovide COM+ wit h a pr oxy/ st ub DLL ( see COM I nt er face Types for m or e inform at ion) .
30
COM I nt e r fa ce Type s I n gener al, t here are t wo k inds of COM int er face t ypes: aut om at ion- com pliant int erfaces and cust om int erfaces. Cont rar y t o com m on concept ions, an aut om at ion- com pliant int er face does not have t o derive fr om I Dispat ch or have all t he par am et ers be var iant s or variant s- com pat ible t ypes ( such as a BSTR or long) . An aut om at ion- com pliant int erface m ust have one of t he following t wo direct iv es in it s definit ion: dual or oleaut om at ion. For exam ple: [ object, uuid(30548235-4EC3-4087-9956-ED26748F47E9), dual, helpstring("An example for automation compliant interface"), ] interface IMyInterface : IUnknown { HRESULT MyMethod([in]long lNumber); }; COM can m ar shal an aut om at ion- com pliant int er face by cr eat ing t he appropr iat e pr oxy and st ub aut om at ically at runt im e. However, aut om at ion- com pliant int er faces do have lim it at ions on par am et er t ypes; for exam ple, t hey cannot hav e as m et hod param et er s st ruct s wit h point er s in t hem . For ult im at e flexibilit y , y ou can use cust om int erfaces. These int er faces do not hav e dual or oleaut om at ion in t heir int er face definit ion, and it is t he dev eloper’s r esponsibilit y t o provide a prox y and a st ub DLL. However, if your design calls for cust om int erfaces, y ou should provide COM+ wit h a proxy/ st ub DLL t hat was built using t he MI DL swit ch /Oicf t o enable t ype library m arshaling. I n any case, configured com ponent s cannot use int erfaces t hat r equire cust om m ar shaling. You can develop configured com ponent s in C+ + , Visual Basic, or even C# , since one of t he cor e pr inciples of COM, language independence, is m aint ained in COM+ . You m ay be wondering by now, where does COM+ st or e t he configurat ion inform at ion for all your applicat ions and com ponent s? Unlik e classic COM, COM+ does not use t he Windows regist ry. COM+ uses a dedicat ed reposit ory called t he COM+ cat alog. No form al Micr osoft docum ent at ion of t he ex act physical locat ion of t he cat alog ex ist s, sim ply because it is not useful t o you. The only bit of configurat ion inform at ion st ill st ored in t he Windows r egist r y is t he com ponent t hreading m odel and r em aining classic COM infor m at ion ( such as InprocServer32 and prog-ID regist ry keys) .
31
1 .5 Applica t ion s, D LLs, a n d Com pon en t s COM+ applicat ions ar e logical pack aging unit s; DLLs, however, ar e physical packaging unit s. Ther e is no corr elat ion bet ween logical and physical pack aging. The only r equirem ent is t hat a configur ed com ponent m ust belong t o exact ly one COM+ applicat ion; it cannot belong t o m ore t han one, and it m ust belong t o at least one t o t ake adv ant age of COM+ com ponent serv ices. As dem onst r at ed in Figur e 1- 8, a COM+ applicat ion can host com ponent s from one or m ult iple DLLs ( Applicat ion 2 has com ponent s from t wo DLLs) . I t is also possible t hat not all t he com ponent s in a DLL ar e host ed in COM+ applicat ions ( such as com ponent E) , and one DLL can cont r ibut e com ponent s t o m ult iple COM+ applicat ions ( DLL 1 cont r ibut es com ponent s t o Applicat ion 1 and Applicat ion 2) . Figu re 1 - 8 . COM + ap plica t ion s a nd D LLs
The separ at ion of phy sical fr om logical pack aging gives you great flex ibilit y in designing your applicat ion’s layout . All t he com ponent s in t he sam e COM+ applicat ion share t he sam e applicat ion- level configurat ion set t ings, regar dless of t heir underly ing DLL packaging. However, I r ecom m end t hat y ou avoid inst alling com ponent s from t he sam e DLL int o m ore t han one applicat ion, such as com ponent s B and C in Figure 1- 8. The reason is t hat com ponent s in t he sam e applicat ion ar e assum ed t o operat e t ight ly t oget her and t r ust each ot her . On t he ot her hand, not hing is assum ed about com ponent s fr om different applicat ions. By placing com ponent s from t he sam e DLL int o m ult iple applicat ions, y ou m ay int r oduce needless securit y checks. You m ight also int r oduce cross- process m arshaling overhead, if t hose com ponent s need one anot her t o oper at e, which is probably why t hey w ere put in t he sam e DLL in t he first place. The COM+ Com ponent I nst all Wizard also does not handle com ponent s from t he sam e DLL in m ult iple applicat ions v er y well. When y ou use t he wizard t o add com ponent s from a DLL t o an applicat ion, t he wizard t ries t o add all com ponent s in t he DLL t o t he applicat ion. I f som e of t he com ponent s are already part of ot her applicat ions, t he wizar d will t reat t his sit uat ion as an er ror since it
32
will t hink y ou are t ry ing t o include a com ponent in m ore t han one applicat ion. The bot t om line is t hat you should put all com ponent s t hat cooperat e closely or perfor m relat ed funct ionalit y int o a single applicat ion. Those com ponent s can be writ t en by m ult iple dev eloper s and be cont ained in m ult iple DLLs, but t hey will ult im at ely share t he sam e applicat ion configurat ion and be deployed t oget her .
1 .6 Configu r in g COM + Applica t ion s The prim ary benefit of using COM+ is t hat you can configure a com ponent or t he applicat ion cont aining it wit hout changing any code on t he obj ect or t he client side. This advant age enables y ou t o focus your obj ect code on it s int ended pur pose, relying on t he var ious serv ices COM+ pr ov ides inst ead of hav ing t o develop t hem your self. This sect ion shows you how t o configur e som e of t he applicat ion- level opt ions for t he Hello COM+ program you creat ed. 1 .6 .1 COM + Applica t ion Type s As m ent ioned prev iously, t he applicat ion act ivat ion t ype ( a server or a library applicat ion) is a configurable applicat ion- level at t ribut e called act iv at ion. You can configure t he applicat ion’s act iv at ion t y pe in t he applicat ion’s proper t ies page, under t he Act iv at ion t ab ( see Figure 1- 9) . Figu r e 1 - 9 . Applica t ion Act iva t ion t a b
Changing t he applicat ion t y pe has significant im plicat ions for m ost COM+ serv ices. The applicat ion t ype is a design- t im e decision t hat should consider t he securit y needs of your com ponent s, t he calling pat t erns of y our client s, fault isolat ion ( a ser ver applicat ion get s it s
33
own pr ocess) , and specific COM+ serv ices r equirem ent s. Throughout t he book, a part icular serv ice configur at ion t hat is relat ed t o t he act ivat ion t ype is point ed out explicit ly . However, even wit hout k nowing m uch about COM+ , you can use t he following rule t o decide on your act iv at ion t y pe: prefer server t ype applicat ions, unless y ou absolut ely need t o run in t he client process for perfor m ance reasons. Libr ar y applicat ions have som e lim it at ions in using COM+ serv ices ( such as secur it y and queued com ponent suppor t ) , and t hey cannot be accessed from anot her m achine. 1 .6 .2 COM + Sur r oga t e Pr oce sse s I f t he or iginal COM com ponent s resided in a DLL, how does COM+ achieve different act iv at ion m odes for t he configur ed com ponent s? When t he applicat ion is configur ed as a libr ar y, t he client loads t he DLL direct ly int o it s pr ocess. When t he applicat ion is configur ed as a server applicat ion, COM+ cr eat es a surr ogat e process for it , called dllhost .exe, t hat loads t he DLL. COM+ t hen places a proxy in t he client process and a st ub in t he surr ogat e process t o connect t he client t o t he obj ect . You can hav e m ult iple inst ances of t he dllhost process running on your m achine sim ult aneously; if client s have cr eat ed obj ect s from different server applicat ions, each ser ver applicat ion get s it s own inst ance of dllhost . To verify t hese point s yourself, configure t he Hello COM+ exam ple t o r un as a serv er applicat ion. Run t he t est client again, cr eat e t he obj ect , and call t he ShowMessage( ) m et hod, but do not press t he OK but t on. The Com ponent Serv ices Explorer gives you v isual feedback when a server applicat ion is running: t he applicat ion icon and t he act ive com ponent s will be spinning. Library applicat ions will hav e no v isual feedback when t hey are r unning in a client process, even if t hat pr ocess is anot her COM+ serv er applicat ion. Expand t he COM+ Applicat ions folder and select t he St at us View on t he Com ponent Services Explorer t oolbar ( t he but t on at t he far r ight end of t he t oolbar; see Figur e 1- 10) . The Com ponent Services Explorer will display t he process I D of t he r unning server applicat ions. Record t he process I D for t he Hello COM+ applicat ion. Nex t , bring up Windows Task Manager and locat e t he pr ocess wit h a m at ching I D. I t s im age nam e w ill be dllhost .ex e. Figu re 1 - 1 0 . Ex a m in in g a ru n ning se rve r a p plicat ion
34
The first CoCreateInstance( ) request for a com ponent in a ser ver applicat ion creat es a new dllhost process, t o host com ponent s from t hat applicat ion only. Subsequent CoCreateInstance( ) calls t o obj ect s from t he sam e applicat ion creat e new obj ect s in t he exist ing dllhost inst ance. Unlike classic COM, t here is no way t o creat e each obj ect in it s ow n process. No COM+ equiv alent t o t he COM call you m ak e t o CoRegisterClassObject(...REGCLS_SINGLEUSE...) exist s. The Com ponent Serv ices Explorer also let s y ou m anage server applicat ion act iv at ion adm inist rat ively. You can shut down a running applicat ion by r ight - click ing on it s icon in t he Com ponent Services Explorer and select ing Shut down fr om t he pop- up cont ext m enu. You can shut it down even when client s ar e holding act iv e references t o obj ects in t he applicat ion. ( You shut down applicat ions t his way fr equent ly during debugging sessions.) The Com ponent Serv ices Explorer does not pr ovide a way t o shut down library applicat ions, since COM+ m ay not even m anage t heir client pr ocess. You can also select St art from t he ser ver applicat ion pop- up cont ext m enu t o launch a new dllhost pr ocess associat ed wit h t hat applicat ion. However, no obj ect s will be cr eat ed unless you use obj ect pooling, which is discussed in Chapt er 3. 1 .6 .3 I dle Tim e M a na ge m e n t Anot her dist inct ion bet ween a classic COM local ser ver and a COM+ server applicat ion is pr ocess shut down. I n classic COM, w hen t he last client has released it s last r efer ence on an obj ect in t he process, COM would shut down t hat process. COM+ provides idle t im e m anagem ent for COM+ server applicat ions. COM+ server applicat ions can be left running indefinit ely even when idle ( when t here are no ext ernal client s) , or you can have COM+ shut t hem down aft er a predet er m ined t im eout . This shut down is done for t he sake of per form ance. I m agine a sit uat ion in which a client creat es an obj ect fr om a serv er applicat ion ev ery 2 m inut es on average, uses it for 1 m inut e and 55 seconds, and t hen releases it . Under classic COM, you would pay an unnecessary per form ance penalt y
35
for creat ing and dest roy ing t he serv er process. Under COM+ , you can configur e t he ser ver applicat ion t o be left running when idle for a specific t im e. I f during t hat t im e no client r equest for creat ing a new obj ect has com e t hr ough, COM+ is allowed t o shut down t he process t o r elease it s r esources. I n t his exam ple, you would per haps configur e t he server applicat ion t o be left r unning when idle for 3 m inut es, as you would want t o com pensat e for variances in t he client calling pat t er n. I f a new call com es in wit hin t hose 3 m inut es, COM+ zer os t he idle t im e count er and st art s it up when t he applicat ion is idle again. You can configure serv er applicat ion idle t im e m anagem ent under t he Adv anced t ab on t he serv er’s proper t ies page ( see Figur e 1- 11) . Libr ary applicat ions do not hav e an idle t im e m anagem ent opt ion and will be unloaded from t heir client process once t he last obj ect has been r eleased. Figu re 1 - 1 1 . Con fig u r ing se rve r ap plicat ion id le t im e m a n a ge m e n t
1 .7 D ebu ggin g COM + Applica t ion s Debugging a COM+ applicat ion, be it a libr ary or a server applicat ion, is not m uch differ ent from debugging an in- proc COM obj ect or a local ser ver. A librar y applicat ion has t he clear adv ant age of allow ing y ou t o st ep t hrough y our code direct ly from t he t est client , since a librar y and a server applicat ion share t he sam e process. A server applicat ion alway s runs in a different process t han your t est client and, t her efor e, in a different debug
36
session ( a different inst ance of Visual St udio is at t ached t o t hat process) . When debugging t he business logic part of your applicat ion, you m ay find it useful t o debug it as a libr ary applicat ion, ev en if t he design calls for a ser ver applicat ion. When debugging a libr ary applicat ion, you m ay also need t o point Visual St udio t o t he exact locat ion of t he com ponent ’s DLLs. This st ep is requir ed so y ou can set breakpoint s in t he com ponent ’s code. When debugging a com ponent in a ser ver applicat ion, y ou can st ep int o t he com ponent ’s code fr om t he t est client side in t wo w ay s. Fir st , y ou can st ar t t he client pr oj ect in t he debugger, break at a line where y ou call a m et hod on a com ponent in t he ser ver applicat ion, and sim ply st ep int o it ( F11 in Visual C+ + or F8 in Visual Basic) . This process launches a new inst ance of t he debugger and at t aches it t o t he running dllhost cont aining your com ponent . You can t hen st ep t hrough y our com ponent ’s code. Second, you can at t ach a debugger t o a server applicat ion by configur ing it t o launch in a debugger . On t he ser ver applicat ion propert ies page, under t he Advanced t ab, t her e is t he Debugging pr opert ies group. I f you check t he Launch in debugger checkbox ( see Figure 1- 12) , when t he first request for cr eat ing an obj ect from t hat applicat ion com es in, COM+ launches t he applicat ion in a Visual C+ + debugger session. You m ay use t his opt ion oft en t o t r ack bugs in t he const ruct ors of com ponent s or bugs t hat do not happen in t he scope of a client call. COM+ is able t o at t ach t he debugger t o t he applicat ion using a com m and- line opt ion for Visual St udio. When you launch t he debugger wit h an ex ecut able filenam e as a par am et er, t he debugger st ar t s a debug session and cr eat es t he specified pr ocess ( in COM+ ’s case, alw ays dllhost ) . COM+ also specifies t he ser ver applicat ion I D as a com m and line param et er for dllhost : msdev.exe dllhost.exe /ProcessID:{CCF0F9D9-4500-41248DAF-B7CF8CBC94AC} This code inform s dllhost t hat it is now associat ed wit h t he specified server applicat ion. Figu re 1 - 1 2 . Lau n ch in g COM + ser v er a pp lica t ion in a d ebug ge r
37
1 .8 D eployin g COM + Applica t ion s Once y ou have t est ed your COM+ applicat ion and configur ed all t he COM+ serv ices t o your lik ing, you need t o inst all y our applicat ion on a cust om er / client m achine. The Com ponent Services Ex plorer can generat e a special file t hat capt ur es all your applicat ion com ponent s and set t ings. This file is a Window s I nst aller ( MSI ) file, ident ified by t he .m si file ext ension. Clicking on an MSI file launches t he Windows I nst aller and inst alls t he applicat ion wit h all it s COM+ configurat ion param et er s. There is a one- t o- one relat ionship bet ween an applicat ion and an MSI file. Thus, if y ou have m ult iple applicat ions in y our product , y ou m ust gener at e one MSI file for each applicat ion. To gener at e t he MSI file, r ight - click on your applicat ion icon in t he Com ponent Services Explorer and select Export from t he pop- up cont ext m enu. This act ion should br ing up t he Applicat ion Expor t Wizar d. Click Nex t t o go t o t he second wizar d screen, where y ou are request ed t o ent er t he nam e and locat ion for t he applicat ion expor t file t o be creat ed ( see Figur e 1- 13) . Next , you should decide how t o export t he applicat ion: as a Server applicat ion or as an Applicat ion proxy ( see Figure 1- 13) . Click Next and t hen click Finish on t he nex t Wizar d screen. Figu re 1 - 1 3 . Ex port in g a COM + a pplica t ion
38
1 .8 .1 Pr ox y COM + Applica t ion s The nam es Server applicat ion and Applicat ion pr oxy ar e confusing. A " Serv er applicat ion" export is relev ant for bot h library and server applicat ions. I t m eans t hat t he applicat ion will include in t he MSI file t he COM obj ect s t hem selves, t heir set t ings, and t heir pr ox y/ st ub DLLs ( if r equir ed) , and will inst all all on t he serv er m achine. An " Applicat ion proxy " expor t inst alls on t he client m achine only t he t y pe inform at ion in t he MSI it creat es ( as well as t he pr oxy/ st ub DLLs, if required) . The generat ed file does not hav e t o include t he com ponent s t hem selves ( unless t he t y pe infor m at ion is em bedded in t he com ponent s, in which case t he com ponent s are only used as cont ainers and ar e not r egist ered) . You can use a proxy inst allat ion when y ou want t o enable r em ot e access from a client m achine t o t he m achine where t he applicat ion act ually resides. A proxy expor t is available only for a COM+ ser ver applicat ion, not for a library applicat ion. When y ou inst all a serv er export on anot her m achine, it will inst all t he com ponent s for local act ivat ion. CoCreateInstance( ) request s cr eat e t he obj ect s locally—in t he client process, if it is a librar y applicat ion, or in a dllhost pr ocess, if it is a server applicat ion. When y ou inst all a proxy export , act ivat ion r equest s on t hat m achine will be redir ect ed t o anot her rem ot e m achine. I n a way, a proxy export inst alled on a client m achine is a t hir d kind of COM+ applicat ion. This k ind is usually called a pr oxy applicat ion. You can configure t he proxy applicat ion t o access any rem ot e m achine on t he net wor k where t he server applicat ion is inst alled, not j ust t he m achine t hat generat ed t he proxy export . You specify t he " real" applicat ion locat ion on t he pr oxy applicat ion pr opert ies page under t he Act iv at ion t ab.
39
A prox y applicat ion can ev en be inst alled on m achines running Windows NT or Windows 9x wit h DCOM, pr ovided t hose m achines hav e Windows I nst aller inst alled on t hem . Because t he Windows I nst aller cannot use t he COM+ cat alog t o st ore t he proxy applicat ion inform at ion on a non- Windows 2000 m achine, it will use t he regist ry and will st or e only t he subset of infor m at ion requir ed for DCOM t here. Window s I nst aller is not com m only found on nonWindows 2000 m achines. To m ake sur e client s on t hose m achines are able t o access your COM+ applicat ions, you should incor por at e t he Windows I nst aller inst allat ion in y our product inst allat ion. The Windows I nst aller inst allat ion file is called inst m si.exe and is available as part of t he Developers Plat for m SDK. A prox y applicat ion cannot export anot her MSI file. I n fact , all t he applicat ion- com ponent , int erface, and m et hod- level set t ings on a proxy applicat ion ar e disabled, ex cept t he Rem ot e server nam e under t he Act ivat ion t ab. The Rem ot e server nam e edit box is disabled in library and serv er applicat ions. 1 .8 .2 I n st a llin g a n d Un in st a llin g a n Ex por t e d Applica t ion The m ost com m on way t o inst all an MSI file on anot her m achine is sim ply t o click on it , which w ill launch t he Windows I nst aller . The applicat ion files ( DLLs and pr oxy/ st ubs) will be placed in a default locat ion: \Program Files\COMPlus Applications\{} I f you wish t o have t he applicat ion inst alled in a differ ent locat ion, you m ust use t he Com ponent Services Explorer Applicat ion I nst all Wizar d. Bring up t he wizard and select I nst all pr e- built applicat ion( s) . Browse t o where t he MSI file is st ored, and select it . The wizard will let you choose whet her y ou want t o use t he default locat ion for inst allat ion or specify a differ ent one. I f you want t o aut om at e uninst alling COM+ applicat ions, y ou can use a com m and line inst ruct ion t o inv oke t he Windows I nst aller t o uninst all a COM+ applicat ion: msiexec -x .msi You can also use t he Windows Cont r ol Panel’s Add/ Rem ove Program s applet t o add or r em ove COM+ applicat ions.
1 .9 Su m m a r y I n t his chapt er, you creat ed a t r ivial exam ple COM com ponent and im plem ent ed it in a DLL. You used it as an in- pr oc serv er or as a local serv er and even cont rolled it s life cycle and idle t im e m anagem ent by configur ing t he com ponent ( act ually it s cont aining applicat ion) different ly. All t his was achieved wit hout changing a 40
single line of code on t he obj ect or t he client side. This achievem ent reflect s t he power of COM+ : it enables you t o focus on y our product and dom ain pr oblem s at hand, while declar at iv ely t aking advant age of available services. The r est of t his book discusses t hese serv ices t horoughly, including t heir int er act ions and pit falls, and provides t ips and t ricks for how t o apply t hem product iv ely .
41
Cha pt e r 2 . COM + Cont e x t COM+ provides services t o com ponent s by int ercept ing t he calls t he client m akes t o com ponent int er faces. The idea of prov iding serv ices t hrough an int ercept ion m echanism is not a COM+ innovat ion. As y ou will see, classic COM also pr ov ides com ponent serv ices v ia int ercept ion. What is new is t he lengt h t o which COM+ t akes t he idea. This chapt er st ar t s by describing t he way classic COM uses m ar shaling t o pr ov ide it s services and t o encapsulat e t he runt im e requirem ent s of it s obj ect s. Nex t , t he chapt er int roduces you t o t he COM+ cont ext —t he inner m ost ex ecut ion scope of an obj ect . COM+ call int er cept ion occurs at cont ext boundar ies. Generally , you need not be concer ned wit h cont ext s at all. They ar e t r ansparent t o y ou, whet her y ou develop a client or a com ponent . However, t he COM+ cont ext is a good m odel for explaining t he w ay COM+ serv ices are im plem ent ed. This book clearly out lines t he few cases when you should int er act w it h t he cont ex t s direct ly. I nt er act ion w it h t he cont ex t s occur s m ost ly when dealing wit h COM+ inst ance m anagem ent and t ransact ions, but also when dealing wit h som e securit y issues.
2 .1 En ca psu la t ion via M a r sha lin g in COM One of t he core principles of classic COM is locat ion t ranspar ency . Locat ion t ranspar ency allows t he client code t o be independent of t he act ual obj ect 's locat ion. Not hing in t he client 's code pert ains t o wher e t he obj ect execut es, alt hough t he client can insist on a specific locat ion as well. A client CoCr eat es it s obj ect s and COM inst ant iat es t hem in t he client 's process, in anot her pr ocess on t he client 's m achine, or on anot her m achine alt oget her. COM decides wher e t he obj ect s will execut e based on a few Regist r y values. Those values ar e m aint ained out side t he obj ect code. A change in t hose values can cause t he sam e obj ect t o be act ivat ed in a differ ent locat ion. The sam e client code handles all cases of obj ect locat ion. You can say t hat COM com plet ely encapsulat es t he obj ect locat ion. A key idea in obj ect - orient ed and com ponent - or ient ed program m ing is encapsulat ion, or inform at ion hiding. Encapsulat ion prom ot es t he design of m ore m aint ainable and ext ensible syst em s. By ignoring t he obj ect locat ion, t he client code is decoupled furt her fr om t he obj ect . The client code does not need t o be m odified if t he obj ect locat ion changes. COM encapsulat es t he obj ect locat ion by int roducing a pr oxy and st ub bet ween t he obj ect and it s client . The client t hen int er act s wit h t he obj ect direct ly or t hr ough a proxy , and COM m arshals t he call from t he client t o t he obj ect 's t rue locat ion, if
42
it needs t o ( all t hree cases ar e shown in Figure 2- 1) . The im port ant obser vat ion here is t hat t he client code is not r equired t o m ak e assum pt ions about t he locat ion of it s called obj ect s or t o m ake explicit calls acr oss processes ( using nam ed pipes, for inst ance) or across m achines ( using socket s) . Figu re 2 - 1 . Cla ssic COM com p le t e ly en cap su lat e s t h e obj e ct loca t ion from t h e clie n t b y in t r odu cing a pr ox y/ st u b b et w e e n t h em
To pr ov ide locat ion t ranspar ency, COM pr ox ies are poly m orphic wit h t he obj ect ; t hey support ex act ly t he sam e set of int erfaces as t he real obj ect , so t he client cannot t ell t he differ ence bet ween t he proxy and t he r eal obj ect . Anot her t im e when classic COM encapsulat es an obj ect pr opert y using m arshaling is in it s handling of t he obj ect ’s sy nchronizat ion needs. The obj ect ’s developer declar es in t he Regist r y what t hr eading m odel t he obj ect uses. I f an incom pat ibilit y exist s bet ween t he creat ing client - t hr eading m odel and t he obj ect ’s t hr eading m odel, COM put s a pr oxy and st ub bet ween t hem and m ar shals calls from t he client t hr ead t o t he obj ect t hread. Since m any t hreads can exist in a giv en pr ocess, COM div ides a pr ocess int o apart m ent s, and any call crossing an apar t m ent boundary is m ar shaled ( see Figur e 2- 2) . Again, t he proxy and st ub com plet ely encapsulat e t he obj ect ’s execut ion t hread. The sam e client code can handle calling m et hods on obj ect s on t he sam e t hread ( in t he sam e apart m ent ) , on a different t hr ead ( in a differ ent apart m ent ) in t he sam e process, or on anot her t hread in a different pr ocess. The proxy and st ub ar e r esponsible for perform ing a t hr ead cont ex t swit ch when m arshaling t he call from t he client t hr ead t o t he obj ect t hr ead. Because t he obj ect needs t o appear t o t he client as t hough it is execut ing on t he sam e t hr ead as t he client , t he pr oxy and st ub will also handle t he r equired synchronizat ion; t he pr oxy has t o block t he client t hr ead and wait for t he st ub t o r et urn fr om t he call on t he obj ect t hread. COM concur rency m anagem ent m akes it possible for
43
t he client t o ignor e t he exact synchronizat ion requir em ent of t he obj ect . A dedicat ed sy nchronizat ion pr ot ocol, such as post ing m essages bet ween t he client and t he obj ect , or signaling and wait ing on event s or nam ed event s is not necessar y. Because not hing in t he client ’s code consider s t he obj ect ’s t hr eading need, when t he obj ect ’s t hreading m odel changes ( when a new version of t he obj ect wit h a new t hreading m odel is deployed) , t he client code rem ains unchanged. Figu r e 2 - 2 . Cla ssic COM e n ca psula t es t h e ob j e ct ex e cu t ion t h re a d b y in se rt in g a p rox y an d a st u b be t w ee n t h e clie n t a n d t h e ob j ect
The t wo exam ples hav e a few t hings in com m on. The pr oxy int er cept s calls fr om t he client t o t he obj ect , m aking sure t he obj ect get s t he runt im e envir onm ent it requir es t o operat e proper ly . The proxy and st ub m arshal away incom pat ibilit ies bet ween t he client and t he obj ect , and t hey perfor m pr e- and post - call pr ocessing, such as t hread cont ex t swit ching, cross- process com m unicat ion, block ing t he calling t hr ead, and signaling int ernal event s. I n bot h exam ples, t he obj ect declar es it s requir em ent s in t he Regist r y, rat her t han prov iding specific code for im plem ent ing t hem . While classic COM pr ov ides only a few serv ices by int er cept ing t he client ’s calls, y ou can see t he pot ent ial for im plem ent ing addit ional serv ices t hrough t his m echanism . I deally, you could declar e which serv ices y our com ponent requires and t hen use syst em com ponent serv ices inst ead of im plem ent ing t hem y our self. This is wher e COM+ com es in.
2 .2 En ca psu la t ion via I n t er cept ion in COM+ COM+ provides it s com ponent services via int er cept ion. You can configure y our com ponent t o t ake adv ant age of services, and COM+
44
put s a proxy and st ub bet ween t he com ponent and it s client , if t he client and t he com ponent inst ance are incom pat ible wit h any one of t he serv ices. I t also put s a pr oxy and stub bet ween t hem if a serv ice requires int er cept ion, r egardless of t he way t he client and t he obj ect ar e configur ed. The ex act obj ect configur at ion is com plet ely encapsulat ed by t he proxy and st ub and t he call int er cept ion. Not hing in t he client code couples it t o t he obj ect configurat ion. This developm ent is a m aj or st ep t owar d ult im at e encapsulat ion, in w hich t he com ponent cont ains alm ost not hing but business logic and in which t he way it uses com ponent services such as t r ansact ions, secur it y, event s, and act iv at ion is hidden fr om t he client . Sim ilarly, t he com ponent does not car e about it s client configurat ion, as t he t wo do not need t o int er act wit h each ot her about t he w ay t hey use t he serv ices. Because an obj ect can hav e t he sam e t hreading m odel as it s cr eat ing client w hile differing in ot her serv ice configur at ion, apart m ent s can no longer be t he innerm ost ex ecut ion scope of an obj ect . I nst ead, COM+ subdivides apart m ent s, so each obj ect can be placed in a correct r unt im e env ironm ent appr opr iat e t o it s needs and int ercept all calls t o t he obj ect . The subdivision of an apar t m ent int o unit s of obj ect s t hat share t he sam e configurat ion is called a cont ext . Each apar t m ent has one or m or e cont ex t s, and a given cont ext belongs t o exact ly one apart m ent . A cont ex t can host m ult iple obj ect s, and each obj ect belongs t o exact ly one cont ext . Figure 2- 3 shows an ex am ple of how pr ocesses and apar tm ent s can be br oken down int o cont ext s under COM+ . Figu re 2 - 3 . COM + su bd ivid es ap ar t m e n t s int o con t ex t s
Because a COM+ obj ect m ust belong t o exact ly one cont ex t , every apart m ent has at least one cont ex t and pot ent ially m any m ore. Ther e is no lim it at ion t o t he num ber of cont ext s an apar t m ent can host . All calls in and out of a cont ext m ust be m arshaled v ia a pr oxy and st ub so t hat COM+ can int ercept t he calls and pr ov ide configured serv ices. This idea is sim ilar t o t he classic COM requir em ent t hat all cross- apart m ent calls be m ar shaled so t hat 45
COM can enforce t hr eading m odel configurat ions. Obj ect s in t he sam e cont ext can have direct point ers t o one anot her, because t hey are configured t o use t he sam e set of services in a way t hat allows sam e- cont ext act iv at ion, and hence, direct access. Mediat ing bet ween obj ect s in t he sam e cont ext is not necessar y. 2 .2 .1 Ligh t w e igh t Pr ox ie s When COM+ m arshals a call bet w een t wo cont ext s in t he sam e apart m ent , it does not need t o per form a t hread cont ext sw it ch. However, COM+ st ill put s a proxy and st ub in place t o int er cept t he call fr om t he client t o t he obj ect and perform a service context swit ch. This sw it ch ensures t hat t he obj ect get s t he runt im e env ironm ent it r equires. COM+ uses a new k ind of proxy for t his m ar shaling: a lightw eight proxy. I t is called a light weight proxy because no expensiv e t hread cont ext swit ch is needed t o m ar shal calls fr om t he client t o t he obj ect . The perfor m ance hit for a serv ice cont ext swit ch is a fr act ion of t hat incur r ed when perform ing a t hr ead cont ex t swit ch. A ser vice cont ext swit ch can som et im es be as light weight as sim ply check ing t he value of a flag, but usually it inv olv es som e pre- and post - call pr ocessing t o m ar shal away differ ences in t he r unt im e env ironm ent bet w een t he client and t he obj ect . The light weight pr oxies ar e not t he st andar d proxies used for crossapart m ent / process/ m achine calls. St andard prox ies are eit her cr eat ed using t he MI DL com piler or pr ovided by t he st andar d t ype librar y m arshaler . For a service swit ch, COM+ generat es t he light weight pr ox ies on t he fly , at r unt im e, based on t he ex act obj ect configurat ion. A light weight pr oxy, like any ot her prox y, present s t he client w it h t he ex act sam e set of int erfaces as t hose found on t he act ual obj ect . COM+ provides t he light weight proxy wit h t he right int erface signat ures based on t he t ype library em bedded in t he com ponent ’s DLL. An exam ple for a light weight proxy is a proxy t hat pr ov ides calls sy nchr onizat ion t o t he obj ect . I f t he obj ect is configured t o r equire sy nchr onizat ion ( t o prevent access by m ult iple concur rent t hreads) , but it s client does not require sy nchronizat ion, COM+ put s a light weight sy nchronizat ion pr oxy bet ween t he t wo. Anot her exam ple is secur it y. I f t he obj ect is configured t o require an access check before accessing it , ver ify ing t hat t he caller was gr ant ed access t o t he obj ect , but it s client does not care about securit y, t here will be a light weight securit y pr oxy in bet ween. This proxy m ak es sure t hat only aut hor ized callers are allowed access t o t he obj ect I f t he obj ect is in a different cont ext from t hat of it s caller because of incom pat ibilit y in j ust one com ponent ser vice ( or if a ser vice always m andat es a separ at e cont ext ) , t here will be j ust one
46
light weight pr oxy bet ween t he caller and t he obj ect . Therefor e, what should COM+ do if t he client and t he obj ect differ in m ore t han one serv ice? The exact w ay t he light weight pr oxies m echanism is im plem ent ed is not docum ent ed or widely k now n. However, in t his case, COM+ probably does not generat e j ust one light weight proxy t o do m ult iple serv ice swit ches, but r at her put s in place as m any light weight pr ox ies as needed, one for every serv ice swit ch. For exam ple, consider an obj ect t hat im plem ent s t he int erface IMyInterface and is configur ed t o use t wo COM+ serv ices: Service A and Ser vice B. I f t he client does not use Ser vice A and Service B, COM+ put s t wo light weight pr oxies in place, as shown in Figur e 2- 4. The light weight pr oxy t o Serv ice A only knows how t o do a Serv ice A swit ch, and t he light weight proxy t o Service B only knows how t o do a Service B swit ch. Bot h serv ices support t he IMyInterface int er face, and would delegat e t he client call from t he first proxy t o t he second, t o t he obj ect , and t hen back again. The net result is t hat when t he client calls int o t he cont ext wher e t he obj ect resides, t he obj ect get s t he correct r unt im e env ironm ent it r equires t o operat e. I f t he client and t he obj ect bot h use Service C, no light weight pr oxy t o Ser vice C is required. ( St ubs have been rem oved from Figure 2- 4 for clar it y.) Figu r e 2 - 4 . Ligh t w e igh t p rox ie s pe r for m se r vice sw it ch e s
2 .2 .2 Assign in g Obj e ct s t o Con t e x t s When a client calls CoCreateInstance( ) ( New or CreateObject( ), in Visual Basic) , asking for a new inst ance of a configured com ponent ( an obj ect ) , COM+ fir st const r uct s t he obj ect and t hen decides which cont ext t o place t he obj ect in. I n COM+ t erm inology, COM+ decides in which cont ex t t o act ivat e t he obj ect . COM+ bases it s decision on t wo fact or s: t he com ponent ’s configurat ion and t he configurat ion of it s creat ing client . Obv iously, it would be best if t he obj ect could shar e a cont ext wit h t he client . Doing so would oblit erat e t he need for COM+ t o m ar shal calls from t he client t o t he obj ect , and t hus avoid having t o pay even t he slight per for m ance penalt y of light weight pr oxies. COM+ exam ines t he new ly cr eat ed obj ect ’s configur at ion in t he COM+ cat alog and com par es it wit h t he configurat ion ( or rat her, t he 47
cont ext at t ribut es) of t he cr eat ing client . I f t he client ’s cont ex t can provide t he obj ect w it h a sufficient runt im e envir onm ent for it s configurat ion, COM+ places t he obj ect in t he client ’s cont ex t . I f, on t he ot her hand, t he client ’s cont ext cannot pr ov ide t he obj ect wit h it s requir ed r unt im e env ironm ent , COM+ creat es a new cont ext , places t he obj ect in it , and put s light weight pr oxies bet ween t he t wo cont ext s. Not e t hat COM+ does not t r y t o find out if anot her appr opr iat e cont ext for t he obj ect in t hat apar t m ent alr eady exist s. The algorit hm is sim ple—t he obj ect eit her shar es it s cr eat or's cont ext or get s a new cont ext . Obv iously, t he pr econdit ion for sam e- cont ext act ivat ion is having a com pat ible t hr eading m odel bet ween t he client and t he obj ect . Ot her wise, t he obj ect is placed in a different apar t m ent , and hence, a differ ent cont ex t by definit ion, since a cont ext belongs t o exact ly one apart m ent . Classic COM com ponent s ( nonconfigur ed com ponent s) do not r ely on COM+ ser vices to operat e and do not requir e light weight proxies t o m ediat e bet ween t heir client runt im e environm ent and t heir own. I f a nonconfigured com ponent can shar e t he sam e apart m ent as it s cr eat ing client ( com pat ible t hreading m odel) , it will also share it s cont ext , and t he client will get a direct point er t o it , inst ead of a proxy. However , if t he nonconfigured obj ect r equires a different apart m ent , it is placed in a suit able apar t m ent , in what is k now n as t he default cont ex t . Each apart m ent has one default cont ext used for host ing nonconfigured com ponent s. The default cont ex t is defined m ost ly for COM+ int er nal consist ency ( every obj ect m ust hav e a cont ext ) , and no light weight proxies are used w hen obj ect s in ot her cont ex t s ( in t he sam e apart m ent ) access it . You can sum up t he COM+ algorit hm for allocat ing obj ect s t o cont ext s wit h t his r ule: a configured com ponent is usually placed in it s own cont ex t , and a nonconfigured com ponent shares it s cr eat or 's cont ext .
2 .3 Th e Con t ex t Obj e ct COM+ represent s each cont ext by an obj ect called t he cont ex t obj ect . Every cont ext has exact ly one cont ex t obj ect . Obj ect s can obt ain a point er t o t heir cont ext obj ect by calling CoGetObjectContext( ) ( see Figure 2- 5) . All obj ect s in t he sam e cont ext get t he sam e cont ext obj ect . CoGetObjectContext( ) is defined as: Figu re 2 - 5 . By callin g CoGe t Ob j e ct Con t e x t ( ) , ob j e ct s can g et a poin t er t o t h e ir con t ex t ’s con t ex t ob j ect
48
HRESULT CoGetObjectContext(REFIID riid, void** ppInterface); The cont ext obj ect suppor t s a few int er faces, so t he fir st param et er of CoGetObjectContext( ) is alway s an I I D t hat specifies which int er face t o ret r ieve. Two of t he cont ext obj ect ’s int erfaces, IObjectContext and IObjectContextActivity, are legacy int er faces from MTS and are provided prim arily for backward com pat ibilit y wit h MTS com ponent s r unning under COM+ . The ot her t wo int er faces, IContextState and IObjectContextInfo, are specific t o COM+ . Throughout t his book, all chapt ers use t hese t wo int er faces, rat her t han t he legacy MTS int erfaces.
Pr ogr a m m ing in t he COM + Envir onm e n t To m ake program m at ic calls in C+ + against COM+ - specific int er faces, such as IObjectContextInfo, you need t o inst all t he lat est Plat form SDK and include t he header file com svcs.h ( from t he SDK include dir ect ory , not t he Visual St udio 6.0 include dir ect or y) or im port t he DLL com svcs.dll fr om y our sy st em direct ory and provide t he following im port dir ect ives: #import "COMSVCS.DLL" raw_interfaces_only,raw_native_types, no_namespace,named_guids, no_auto_exclude Visual Basic 6.0 developers should im por t t he COM+ Serv ices Type Library t o access COM+ ser vices program m at ically . The IContextState int er face cont r ols obj ect deact iv at ion ( discussed in Chapt er 3) and t ransact ion vot ing ( discussed in Chapt er 4) by m anipulat ing st at e bit s in t he cont ex t obj ect . IObjectContextInfo gains access t o various aspect s of t he cur rent t r ansact ion, ret r iev es t he cur rent act ivit y I D ( discussed in Chapt er 5) , and r et rieves t he curr ent cont ext I D. The IObjectContextInfo int er face is defined as: interface IObjectContextInfo : IUnknown { BOOL IsInTransaction( ); HRESULT GetTransaction([out]IUnknown** ppTransaction); HRESULT GetTransactionId([out]GUID* pTransactionId);
49
HRESULT GetActivityId([out]GUID* pActivityId); HRESULT GetContextId([out]GUID* pContextId); }; Every COM+ cont ext has a unique I D ( a GUI D) associat ed wit h it . Ret r ieving t he curr ent cont ext I D is som et im es useful for t racing and debugging pur poses. Ex am ple 2- 1 shows how t o t r ace t he current cont ex t I D by calling CoGetObjectContext( ), r equest ing t he IObjectContextInfo int er face, and t hen calling t he IObjectContextInfo::GetContextId( ) m et hod. Ex a m p le 2 - 1 . Re t r ie vin g t h e cu rr en t con t ex t I D w it h I Obj e ct Con t ex t I n fo::Get Con t e x t I d( )
HRESULT hres = S_OK; IObjectContextInfo* pObjectContextInfo = NULL; GUID guidContextID = GUID_NULL; hres =::CoGetObjectContext(IID_IObjectContextInfo,(void**)&pOb jectContextInfo); ASSERT(pObjectContextInfo != NULL);//not a configured component? hres = pObjectContextInfo->GetContextId(&guidContextID); pObjectContextInfo->Release( ); USES_CONVERSION; WCHAR pwsGUID[150]; ::StringFromGUID2(guidContextID,pwsGUID,150); TRACE("The object is in context with ID %s",W2A(pwsGUID));
Not e t hat only COM+ - configured com ponent s should call CoGetObjectContext( ). When a nonconfigur ed com ponent calls CoGetObjectContext( ), t he call w ill fail wit h t he r et urn value of E_NOINTERFACE, and t he r et urned int erface point er will be set t o NULL. The asser t ion check in Exam ple 2- 1 t est s for t hat condit ion. One m ore point r egarding t he cont ext obj ect : t he cont ex t obj ect and it s int erfaces are privat e t o t he specific cont ext t hey r epr esent and should not be shared wit h or passed t o obj ect s in ot her cont ext s. Doing so m ay int r oduce har d- t o- det ect bugs and
50
nondet erm inist ic behavior of obj ect deact ivat ion and dest ruct ion, and it m ay affect t ransact ion sem ant ics and accur acy.
2 .4 Th e Ca ll Obj e ct I n addit ion t o providing a cont ex t obj ect t o represent t he cont ex t of an obj ect , COM+ cr eat es a t r ansient obj ect called t he call obj ect each t im e t hat obj ect is called. The t ransient call obj ect represent s t he curr ent call in pr ogr ess. Obj ect s can access t heir call obj ect by calling CoGetCallContext( ) ( see Figure 2- 6) . The CoGetCallContext( ) signat ure is defined as: HRESULT CoGetCallContext(REFIID riid, void** ppInterface); The call obj ect only ex ist s as long as a call from a client is in progress, and it is dest r oyed by COM+ aft er t he called m et hod ret ur ns. You should not cache a point er t o t he call obj ect as a m em ber v ar iable of y our obj ect because t hat point er will be invalid once t he m et hod t hat sav ed it r et urns. Furt herm ore, if y our obj ect is doing work in t he background—t hat is, no m et hod call fr om t he client is cur rent ly in progress—it will not hav e access t o a call obj ect . I f you t ry t o access a call obj ect while a call is not in progress, CoGetCallContext( ) will fail and r et ur n t he er ror code RPC_E_CALL_COMPLETE. You can, however, st ill access t he cont ex t obj ect , which exist s as long as t he cont ext ex ist s, and whose point er can be cached by t he obj ect s associat ed wit h it . The call obj ect exposes t wo int erfaces used t o obt ain inform at ion about t he call securit y set t ings. These int er faces, discussed in Chapt er 7, are ISecurityCallContext and IServerSecurity. Figu re 2 - 6 . W h e n a m e t h od ca ll is in pr og re ss, a COM + ob j e ct h a s a ccess t o t h e ca ll ob j ect
51
2 .5 Cr oss- Con t ex t M a n ua l Ma r sha lin g Cr oss- cont ext call int er cept ion via m ar shaling is how COM+ provides it s com ponent ser vices t o y our obj ect . A client in a differ ent cont ex t cannot access y our obj ect direct ly , even if it has a dir ect r aw point er t o it . I nt er cept ing t he call and perform ing t he right service swit ches r equires a pr oxy and a st ub in bet w een. Ot herw ise, t he obj ect execut es in t he client cont ext , possibly in an ill- suit ed runt im e env ir onm ent . I f t he client get s t he point er t o y our obj ect in one of t he following way s: • • •
CoCr eat ing t he obj ect Querying an obj ect t he client already has for addit ional int er faces Receiv ing t he point er as a m et hod par am et er on a COM int er face
Then COM+ will, under t he hood, put int er cept or s ( pr oxy s and st ubs) in place, t o m ak e sur e all calls int o t he obj ect ar e m ar shaled. I f t he client does any t hing else t o obt ain t he int erface point er, such as ret r ieve it from a global variable or a st at ic m em ber v ar iable shared am ong all client s, you hav e t o m ar shal t he point er m anually your self. Dealing w it h pooled obj ect s is anot her sit uat ion r equiring m anual m arshaling, as you will see in t he nex t chapt er . Classic COM requires t hat all cross- apart m ent calls be m arshaled, even when t he call is in t he sam e pr ocess, t o ensure t hr eading m odel com pat ibilit y. The classic COM m echanism s for m anually m ar shaling int erface point ers acr oss apart m ent boundaries hav e been m ade cont ex t- aware. They are what you should use t o m ar shal int erface point ers m anually across cont ext boundar ies w it h COM+ . Generally , t hese m echanism s rely on t he CoMarshalInterface( ) and CoUnmarshalInterface( ) funct ions. When y ou need t o m anually m arshal an int erface point er fr om Cont ex t A t o Cont ex t B, you would serialize t he int erface point er int o a st r eam in Cont ext A using CoMarshalInterface( ), and get it out of t he st r eam using CoUnmarshalInterface( ) in Cont ext B. This sequence would m anually set up pr ox ies in Cont ext B for accessing t he obj ect . You can also use t he CoMarshalInterThreadInterfaceInStream( ) and CoGetInterfaceAndReleaseStream( ) helper m et hods t o aut om at e som e of t he st eps requir ed when using j ust CoMarshalInterface( ) and CoUnmarshalInterface( ). 2 .5 .1 Th e Globa l I n t e r f a ce Ta ble
52
The preferr ed way t o m anually m arshal int erface point ers bet ween cont ext s is by using t he global int erface t able ( GI T) . Ev er y pr ocess has one globally accessible t able used for m anually m ar shaling int er face point er s. Globally accessible m eans accessible fr om every cont ext and ev ery apart m ent in t he pr ocess. An int er face point er is checked int o t he GI T in one cont ex t . Then y ou get back an ident ifying cook ie ( a num ber ) , which is cont ext - neut ral and can be passed fr eely bet ween client s across cont ext boundar ies, placed in global v ar iable or class m em bers, et c. Any client , at any cont ext in t he process, can access t he GI T and use t he cook ie t o get a proper ly m arshaled int er face point er for it s cont ext . The GI T is only useful in cross- cont ext m arshaling in t he sam e process and has no role in cr oss- process m arshaling. The GI T saves y ou t he agony of progr am m ing direct ly against CoMarshalInterface( ) or it s helper funct ions, and m ore im port ant ly , it overcom es a ser ious lim it at ion of t he CoMarshalInterface( ) funct ion. Using CoMarshalInterface( ), you can unm arshal an int erface point er j ust once for every CoMarshalInterface( ) call. Using t he GI T, you can check an int er face point er int o t he GI T once and check out int er face point er s m ult iple t im es. The GI T suppor t s t he IGlobalInterfaceTable int er face, w hich is defined as: interface IGlobalInterfaceTable : IUnknown { HRESULT RegisterInterfaceInGlobal([in]IUnknown *pUnk, [in]REFIID riid, [out]DWORD *pdwCookie); HRESULT RevokeInterfaceFromGlobal([in]DWORD dwCookie); HRESULT GetInterfaceFromGlobal([in]DWORD dwCookie, [in]REFIID riid,\ [out]void** ppInterface); } You can creat e t he GI T wit h t he class I D of CLSID_StdGlobalInterfaceTable. RegisterInterfaceInGlobal( ) is used t o check an int er face point er int o t he GI T from w it hin one cont ext and t o get back t he ident ifying cook ie. GetInterfaceFromGlobal( ) is used t o get a proper ly m arshaled int er face point er at any ot her cont ex t using t he cookie. RevokeInterfaceFromGlobal( ) is used t o rem ov e t he int er face point er from t he GI T. Exam ple 2- 2 shows how t o use t he IGlobalInterfaceTable int er face t o m anually m ar shal a point er of t y pe IMyInterface from Cont ext A t o Cont ex t B, or any ot her cont ext in t he process, using t he GI T and a global v ariable. Ex a m p le 2 - 2 . M a n u ally m ar sh a lin g a poin t er u sin g t h e GI T
53
//In context A: HRESULT hres = S_OK; extern DWORD dwCookie;//A global variable accessible in any context IMyInterface* pMyInterface = NULL; /* Some code to initialize pMyInterface, by creating an object that supports it*/ //Now, you want to make this object accessible from other contexts. dwCookie = 0; //Create the GIT IGlobalInterfaceTable* pGlobalInterfaceTable = NULL; hres = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable,NULL, CLSCTX_INPROC_SERVER,IID_IGlobalInterfaceTable, (void**)&pGlobalInterfaceTable); //Register the interface in the GIT hres = pGlobalInterfaceTable >RegisterInterfaceInGlobal(pMyInterface, IID_IMyInterface, &dwCookie); pGlobalInterfaceTable->Release( );//Don’t need the GIT ///////////////////////////////////////////////////////// //////////////////////// //In context B: IMyInterface* pMyInterface = NULL; IGlobalInterfaceTable* pGlobalInterfaceTable = NULL; hres = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable,NULL, CLSCTX_INPROC_SERVER,IID_IGlobalInterfaceTable, (void**)&pGlobalInterfaceTable); //Get the interface from the GIT hres = pGlobalInterfaceTable>GetInterfaceFromGlobal(dwCookie, IID_IGlobalInterfaceTable, (void**)&pMyInterface);
54
pGlobalInterfaceTable->Release(
);
/* code that uses pMyInterface */ pMyInterface->Release(
);
///////////////////////////////////////////////////////// ///////////////////////// //Don’t forget to revoke from the GIT when you are done or before shutting down IGlobalInterfaceTable* pGlobalInterfaceTable = NULL; //You can use a cached pointer to the GIT or re-create it: hres = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable,NULL, CLSCTX_INPROC_SERVER,IID_IGlobalInterfaceTable, (void**)&pGlobalInterfaceTable); hres = pGlobalInterfaceTable>RevokeInterfaceFromGlobal(dwCookie); pGlobalInterfaceTable->Release( ); The GI T increm ent s t he refer ence count of t he int erface point er when it is r egist ered. As a result , t he client t hat regist er ed t he int er face point er can act ually let go of it s own copy of t he int erface point er, and t he obj ect would not be dest r oyed. When you rev ok e t he obj ect from t he GI T, t he GI T releases it s copy . When t he process shut s down gr acefully , if you forget t o r evoke your int er faces, t he GI T revokes all t he obj ect s it st ill has, allowing t hem t o be released. The GI T will AddRef( ) an int erface point er t hat is ret ur ned from a call t o GetInterfaceFromGlobal( ). A client should call a m at ching Release( ) for every GetInterfaceFromGlobal( ) called. Any client in t he process can rev ok e a r egist ered int er face point er . However, I r ecom m end as a convent ion t hat t he client w ho regist ered t he obj ect should be t he one r ev oking it . 2 .5 .2 Th e GI T W r a ppe r Cla ss Using t he raw global int er face t able has a few dr awbacks. The result ing code is som ewhat cum bersom e and t he IGlobalInterfaceTable m et hod nam es ar e t oo long. I n addit ion, t he m et hods are not t y pe safe because t hey require you t o cast t o and fr om a void* point er . Previously , I saw a need for wr it ing a sim ple C+ + wrapper class t hat com pensat es for t he r aw usage drawback s. The wr apper class provides bet t er m et hod nam es and 55
t y pe safet y , and because t he class I D for t he GI T is st andard, it s const ruct or cr eat es t he global int erface t able and it s dest ruct or releases it . The wr apper class is called CGlobalInterfaceTable and is defined as: template class CGlobalInterfaceTable { public: CGlobalInterfaceTable( ); ~CGlobalInterfaceTable( ); HRESULT Register(Itf* pInterface,DWORD *pdwCookie); HRESULT Revoke(DWORD dwCookie); HRESULT GetInterface(DWORD dwCookie,Itf** ppInterface); protected: IGlobalInterfaceTable* m_pGlobalInterfaceTable; private://prevent misuse CGlobalInterfaceTable(const CGlobalInterfaceTable&); void operator =(const CGlobalInterfaceTable&); }; By defining t he GI T helper m acr o: #define GIT(Itf) CGlobalInterfaceTable You get aut om at ic t ype safet y because t he com piler enfor ces t he m at ch bet ween t he int erface I D and t he int erface point er used. Using t he wrapper class is t r ivial. Here is t he code required t o ret rieve an int er face point er fr om t he t able, for ex am ple: IMyInterface* pMyInterface = NULL; GIT(IMyInterface) git; git.GetInterface(dwCookie,&pMyInterface); Com pare t his code t o Ex am ple 2- 2. Using t he wrapper class r esult s in concise, elegant , and t y pe- safe code. The GI T wrapper class is included as par t of t he source code available wit h t his book.
2 .6 Su m m a r y This chapt er int roduced t he COM+ cont ext concept : a m echanism for prov iding com ponent services. By int ercept ing client calls and per form ing addit ional processing, COM+ can ensur e t hat t he obj ect has j ust t he runt im e env ir onm ent it requir es. As st at ed at t he beginning of t his chapt er, you usually do not need t o int er act w it h COM cont ext or be aware t hat t hey exist . But underst anding t his abst r act concept helps dem yst ify t he way COM+ serv ices oper at e. Cont ex t and call int er cept ion is an ext ensible m echanism . As t im e goes by , new services can be added t his way
56
wit hout affect ing exist ing applicat ions. When a client creat es inst ances of y our old com ponent in t he new environm ent , COM+ silent ly does it s cont ex t com pat ibilit y in t he backgr ound, and your exist ing com ponent never k nows t hat new serv ices ar e available.
57
Cha pt e r 3 . COM + I nst a nce M a na ge m e nt A few years ago, t he dom inant program m ing m odel and design pat t ern was t he client / ser ver m odel. COM and DCOM were predom inant com ponent t echnologies, and all was well. Then cam e t he I nt er net revolut ion. Alm ost overnight , a new par adigm em erged—t he m ult it ier archit ect ur e . Scalabilit y is perhaps t he single m ost im port ant dr iv ing force behind t he m ov e fr om classic t wo- t ier client / server t o m ult it ier applicat ions. Today , being able t o handle a very large num ber of client s is necessar y for surv iv al. The classic t wo- t ier m odel sim ply does not scale well from a few dozen client s t o t ens of t housands of client s ham m er ing on your syst em at peak load. The t w o- t ier m odel of dedicat ing one server obj ect per client quickly causes crit ical resour ces t o dwindle under such loads. Allocat ing r esour ces such as a dat abase connect ion, a sy st em handle, or a wor ker t hread t o each client is unr ealist ic. The m iddle t ier was int roduced precisely because you could no longer m ap client obj ect s dir ect ly t o your dat a processing obj ect s. The m iddle t ier allows pooling of resources, such as dat abase connect ions, har dware obj ect s, or com m unicat ion por t s. The m iddle t ier also allows you t o act ivat e your obj ect s j ust when t hey are required and release t hem as soon as possible. COM+ provides you wit h t wo elegant and user- fr iendly inst ance m anagem ent serv ices t hat y ou can use t o build scalabilit y int o your sy st em design from day one: obj ect pooling and Just - in- Tim e Act iv at ion ( JI TA) . This chapt er first defines t he pr oblem s you face when designing a m odern dist r ibut ed syst em ; it t hen ex plains COM+ st rat egies for m anaging obj ect s t hat com pose it .
3 .1 Clien t Type s A dist r ibut ed syst em , by it s v ery nat ure, im plies t hat it s client s ar e not on t he sam e m achine as t he obj ect s pr oviding t he services. I n every dist ribut ed syst em , t here are t ypically t wo kinds of client s: rich client s and I nt er net client s. The r ich client t y pically shar es t he sam e local area net work , called t he I nt r anet . ( A r ich client can also be called an int r anet client .) I n m ost cases, no firewalls bet ween t he rich client and t he applicat ion exist , so t he rich client can inv ok e binary calls on com ponent s in t he applicat ion. The I nt er net client connect s t o your applicat ion t y pically by using a web browser , but m ore of t he ot her opt ions, such as hand- held PDAs and cellular phones, are possible as well. The I nt er net client is locat ed out side of your local ar ea net work and can r eside anywhere on t he I nt ernet . I n
58
m ost cases, a fir ewall exist s bet ween t he I nt ernet client and your applicat ion. Most applicat ions have a m ixt ure of rich and I nt ernet client s. Som e sy st em s had only rich client s unt il t hey were opened t o t he I nt er net . Ot her sy st em s wer e designed pr im arily for t he I nt ernet , but had t o support rich client s—per haps for applicat ion m anagem ent , back- office oper at ions, or ot her specific needs. I n any case, when you design an applicat ion, y ou should plan t o suppor t bot h kinds of client s. The t wo k inds differ not only in t he w ay t hey connect t o your applicat ion, but also in t heir pat t ern of int er act ion wit h it . Your design should be able t o scale up t o bot h k inds of client s and com pensat e for t heir differences. COM+ inst ance m anagem ent serv ices were developed to answer precisely t hat challenge. 3 .1 .1 Sca lin g Up w it h Rich Clie n t s A r ich client 's int eract ion wit h t he ser ver obj ect s of a dist ribut ed applicat ion resem bles t hat of t he classic client / serv er applicat ion. The client connect s t o t he serv er m achine using a net work prot ocol such as TCP/ I P. Because t he I nt ranet is considered a secure env ironm ent , it usually cont ains no fir ewalls and t he client can connect direct ly t o your ser ver obj ect s in t he m iddle t ier using DCOM ( see Figure 3- 1) . The calling pat t er n t o your applicat ion is as follows: creat e an obj ect , use it , and event ually r elease it . The r ich client usually pr esent s t o t he user a r ich user int er face. The word " rich" in t his cont ext m eans t hat t he user int erface cont ains and execut es binary code, processing inform at ion and r endering it t o t he user. The user int er face is t y pically built wit h t ools such as Visual Basic or Visual C+ + wit h MFC. The new .NET Fr am ewor k provides a new libr ary of Windows Form s classes for building r ich client s. ( See Chapt er 10) . Even if t he user accesses your applicat ion w it h a web browser, t hat br owser m ay cont ain binary Act iv eX cont rols. I nt ranet client s use rich user int erfaces because t hey m ust usually pr ovide a rich user experience. This ex per ience support s m or e pr ivileges and feat ur es for em ployees t han are available t o cust om er s connect ing t o t he sam e sy st em via an I nt ernet browser. Figu r e 3 - 1 . Rich clie nt conn e ct ing t o a m u lt it ie r syst e m
59
Consider y our bank , for ex am ple. Most banks t oday pr ovide easy access over t he I nt er net for t heir cust om ers, allowing sim ple operat ions such as viewing account balances and t r ansferr ing funds bet ween account s. Howev er , only bank t eller s can per form operat ions such as opening or closing account s and select ing var ious saving and inv est m ent plans. The nex t t im e you are in y our bank , peek ov er t he t eller’s scr een. The t eller probably uses a rich client user int erface t hat does not look lik e t he one you use w hen you log on t o t he I nt ernet banking applicat ion offered by t he bank. I n a t ypical syst em , t here ar e significant ly fewer r ich client s t han I nt er net client s ( as t her e ar e fewer bank t ellers t han bank cust om er s) . The over head of creat ing a few server- side obj ect s, allocat ing resources, and doing t he cleanup for each client is not a scalabilit y lim it at ion. What really im pedes scalabilit y is t he pot ent ial t hat r ich client applicat ions have for holding ont o obj ect s for long per iods of t im e, while act ually using t he obj ect in only a fract ion of t hat t im e. I t is possible t hat w hen an I nt ranet applicat ion is st ar t ed, it inst ant iat es all t he obj ect s it needs and releases t hem only at shut down, in an at t em pt t o achiev e bet t er per form ance and responsiv eness t o t he user. I f y our design calls for allocat ing an obj ect for each client , you will t ie up cr ucial lim it ed r esources for long per iods and event ually run out of r esour ces. 3 .1 .2 Sca lin g Up w it h I nt e r ne t Clie n t s When users access y our sy st em over t he I nt er net , t hey act ually use a web brow ser t o connect t o an I nt ernet web ser ver ( such as t he Microsoft I nt ernet I nfor m at ion Ser ver , I I S) . The browser gener at es a ser vice request as part of t he HTTP st ream . The web server cr eat es obj ect s requir ed for handling t he client r equest , and when it finishes processing t he request , it r eleases t he obj ect s ( see Figur e 3- 2) . I t is im por t ant t o em phasize t hat t he I nt ernet client connect ion is st at eless; no obj ect references ar e m aint ained out side t he scope of individual request s. The client is usually a t hin user int er face, anot her nam e for an int er face t hat consist s of sim ple HTML rendered by a w eb br owser. The browser’s m ain j ob is t o send t he user’s r equest s t o t he server and display t he web ser ver ’s
60
reply. Alt hough som e script s sent by t he web serv er , such as Dynam ic HTML ( DHTML) , m ay execut e on t he client side, such client - side logic is used prim arily t o form at t he inform at ion on t he user’s scr een and has not hing t o do wit h serv er - side obj ect s. Figu re 3 - 2 . I n t er n e t clien t con n e ct in g t o a m u lt it ie r syst e m
Depending on how widely your sy st em is used, y ou could have a huge num ber of client s ask ing for ser vice at any given m om ent . The lengt h of t im e t he w eb ser ver holds t he obj ect s for an indiv idual client r equest is usually not a scalabilit y lim it at ion. However, because t her e are so m any I nt ernet client s, scalabilit y is lim it ed by t he overhead for each client request : creat ing obj ect s, init ializing t hem , allocat ing expensive r esources such as dat abase connect ions, set t ing up pr ox ies, doing cr oss- m achine or pr ocess calls, and doing cleanup. This problem is t he opposit e of t he scalabilit y pr oblem for rich client s. Syst em s t hat use an ineffect ive appr oach of allocat ing obj ect s per client request sim ply cannot handle a lar ge num ber of client s. At periods of peak dem and, t he serv ice appears t o be unav ailable or has irrit at ingly slow response t im e.
3 .2 I n st a n ce Ma na gem e n t a nd Sca lin g Being sm art about t he way you allocat e y our obj ect s t o client s is t he key t o scalabilit y in a m odern dist ribut ed syst em . Sim ple algorit hm s can be used t o gover n when and how expensive obj ect s t hat have access t o scarce resources will act ually ser vice a client request . I n dist r ibut ed- syst em s t er m inology, t hese algorit hm s and heur ist ics ar e called inst ance m anagem ent . COM+ r efer s t o inst ance m anagem ent as act ivat ion. COM+ provides ev er y configured com ponent w it h access t o readym ade inst ance m anagem ent serv ices. Ev er y COM+ com ponent has on it s proper t ies page an Act ivat ion t ab t hat let s you cont rol t he way obj ect s are creat ed and accessed ( see Figure 3- 3) . You can use COM+ ’s t w o inst ance m anagem ent services, obj ect pooling and JI TA, indiv idually, or com bine t hem in a v ery powerful way . Neit her t echnique is a COM+ innovat ion. What is new about COM+ is t he ease wit h which y ou can t ake advant age of t he ser vice. That ease
61
allows you t o focus your developm ent effor t s on t he dom ain problem at hand, not on t he wr it ing of inst ance m anagem ent plum bing. Figu re 3 - 3 . Th e COM + com pon e n t ’s Act ivat ion t a b
3 .3 Obj e ct Poolin g The idea behind obj ect pooling is j ust as t he nam e im plies: COM+ can m aint ain a pool of obj ect s t hat are alr eady creat ed and ready t o serve client s. The pool is creat ed per obj ect t ype; differ ent obj ect s t y pes have separ at e pools. You can configur e each com ponent t ype pool by set t ing t he pool param et er s on t he com ponent ’s proper t ies Act iv at ion t ab ( as shown in Figure 3- 3) . Wit h obj ect pooling, for each obj ect in t he pool, y ou pay t he cost of cr eat ing t he obj ect only once and reuse it wit h m any client s. The sam e obj ect inst ance is recy cled repeat edly for as long as t he cont aining applicat ion runs. The obj ect ’s const ruct or and dest ruct or are each called only once. Obj ect pooling is an inst ance m anagem ent t echnique designed t o deal wit h t he int eract ion pat t er n of I nt ernet client s—num er ous client s creat ing obj ect s for every request , not holding r eferences on t he obj ect s, but releasing t heir obj ect references as soon as t he request processing is done. Obj ect pooling is useful w hen inst ant iat ing t he obj ect is cost ly or when you need t o pool access t o scant r esources. Obj ect pooling is m ost appr opr iat e when t he obj ect init ializat ion is generic enough t o not requir e client - specific
62
par am et ers. When using obj ect pooling, you should always st riv e t o per form in t he obj ect ’s const ruct or as m uch as possible of t he t im econsum ing wor k t hat is t he sam e for all client s, such as acquir ing connect ions ( OLEDB, ADO, ODBC) , running init ializat ion script s, init ializing ext ernal devices, creat ing file handles, and fet ching init ializat ion dat a from files or acr oss a net work . Avoid using obj ect pooling if const ruct ing a new obj ect is not a t im e- consum ing operat ion because t he use of a pool r equir es a fix ed over head for pool m anagem ent ev er y t im e t he client cr eat es or releases an obj ect . Any COM+ applicat ion, whet her a serv er or a library applicat ion, can host obj ect pools. I n t he case of a serv er applicat ion, t he scope of t he pool is t he m achine. I f you inst all pr oxies t o t hat applicat ion on ot her m achines, t he scope of t he pool can be t he local net work. I n cont r ast , if t he applicat ion is a libr ar y applicat ion, t hen a pool of obj ect s is creat ed for each client process t hat loads t he libr ar y applicat ion. As a result , t wo client s in differ ent processes will end up using t wo dist inct pools. I f you w ould lik e t o have j ust one pool of obj ect s, configure your applicat ion t o be a server applicat ion. 3 .3 .1 Poole d Obj e ct Lif e Cycle When a client issues a r equest t o creat e a com ponent inst ance and t hat com ponent is configured t o use obj ect pooling, inst ead of cr eat ing t he obj ect , COM+ first checks t o see if an available obj ect is in t he pool. I f an obj ect is available, COM+ ret ur ns t hat obj ect t o client . I f t here is no av ailable obj ect in t he pool and t he pool has not yet reached it s m ax im um configured size, COM+ creat es a new obj ect and hands it back t o t he cr eat ing client . I n any case, once a client get s a reference t o t he obj ect , COM+ st ay s out of t he way . I n every respect except one, t he client ’s int eract ion wit h t he obj ect is t he sam e as if it wer e a nonpooled obj ect . The ex cept ion occur s when t he client calls t he final r elease on t he obj ect ( when t he reference count goes down t o zero) . I nst ead of releasing t he obj ect , COM+ ret ur ns it t o t he pool. Figure 3- 4 describes t his life cycle graphically in a UML act ivit y diagram . [ 1] [ 1]
I f you are not fam iliar wit h UML activ it ies diagram s, read UML Dist illed by Fow ler and Scott ( Addison Wesley, 1997) . Chapt er 9 in t hat book cont ains a det ailed explanat ion and an ex am ple.
Figu r e 3 - 4 . A poole d obj e ct life cycle
63
I f t he client chooses t o hold ont o t he pooled obj ect for a long t im e, it is allowed t o do so. Obj ect pooling is designed t o m inim ize t he cost of cr eat ing an obj ect , not t he cost of using it . 3 .3 .2 Con figu r ing Pool Pa r a m e t e r s To use obj ect pooling for a giv en com ponent , you should fir st enable it by select ing t he " Enable obj ect pooling" checkbox on com ponent ’s Act ivat ion t ab. The check box allows y ou t o enable or disable obj ect pooling. The t wo ot her param et er s let you cont rol t he pool size and t he obj ect cr eat ion t im eout . The m inim um pool size det er m ines how m any obj ect s COM+ should keep in t he pool, ev en when no client s w ant an obj ect . When an applicat ion t hat is configured t o cont ain pools of obj ect s is fir st launched, COM+ cr eat es a num ber of obj ect s for each pool equal t o t he specified m inim um pool size for t he applicat ion. I f t he m inim um pool size is zero, COM+ doesn’t cr eat e any obj ect s unt il t he first client r equest com es in. Minim um pool size is used t o m it igat e sudden spikes in dem and by hav ing a cache of ready - t o- use, init ialized obj ect s. The m inim um pool size m ust be less t han t he m ax im um pool size, and t he Com ponent Ser vices Explor er enforces t his condit ion. The m axim um pool size configurat ion is used t o cont rol t he t ot al num ber of obj ect s t hat can be creat ed, not j ust how m any obj ect s t he pool can cont ain. For exam ple, suppose you configure t he pool t o have a m inim um size of zer o and a m axim um of four. When t he first creat ion r equest com es in, COM+ sim ply cr eat es an obj ect and hands it over t o t he client . I f a second request com es in and t he first obj ect is st ill t ied up by t he first client , COM+ creat es a new
64
obj ect and hands it over t o t he second client . The sam e is t rue for t he t hird and fourt h client s. However , when a fift h request com es along, four obj ect s ar e already creat ed and t he pool has r eached it s m ax im um pot ent ial size, ev en t hough it is em pt y. Once y ou r each t hat lim it and all obj ect s ar e in use, furt her client s request s for obj ect s ar e blocked unt il an obj ect is r et ur ned t o t he pool. At t hat t im e, COM+ hands it over t o t he wait ing client . I f, on t he ot her hand, t he client wait ed for t he dur at ion specified in t he t im eout field, t he client is unblocked and CoCreateInstance( ) ret urns t he err or code CO_E_ACTIVATIONFAILED_TIMEOUT ( not E_TIMEOUT, as docum ent ed in t he COM+ sect ion of t he MSDN) . COM+ m aint ains a queue for each pool of wait ing client s t o handle t he sit uat ion in which m ore t han one client is block ed while wait ing for an obj ect t o becom e av ailable. COM+ ser vices t he client s in t he queue on a fir st com e, fir st - serv ed basis as obj ect s are ret ur ned t o t he pool. A cr eat ion t im eout of zer o causes all client calls t o fail, r egardless of t he st at e of t he pool and av ailabilit y of obj ect s. I f t he pool cont ains m or e obj ect s t han t he configured m inim um size, COM+ per iodically cleans t he pool and dest roy s t he surplus obj ect s. Ther e is no docum ent at ion of when or how COM+ decides t o do t he cleanup. Deciding on t he m inim um and m axim um pool size configur at ion depends largely on t he nat ure of your applicat ion and t he w ork per form ed by your obj ect s. For exam ple, t he pool size can be affect ed by: • • • •
Expect ed syst em load highs and lows Per form ance profiling done on y our product t o opt im ize t he usage of resources Various param et ers capt ured dur ing inst allat ion, such as user preferences and m em ory size The num ber of licenses y our cust om er has paid for ; you can set t he pool size t o t hat num ber and have an easy- t o- m anage licensing m echanism
I n gener al, when configur ing y our pool size, t ry t o balance available resources. You usually need t o t r ade m em ory used t o m aint ain a pool of a cer t ain size and t he pool m anagem ent over head in exchange for fast er client access and use of obj ect s. 3 .3 .3 Poole d Obj e ct D e sign Re qu ir e m e n t s When y ou want t o pool inst ances of y our com ponent , you m ust adhere t o cert ain requir em ent s and const r aint s. COM+ im plem ent s obj ect pooling by aggr egat ing your obj ect in a COM+ supplied wrapper . The aggr egat ing wr apper’s im plem ent at ion of AddRef( ) and Release( ) m anage t he r efer ence count and ret ur n t he obj ect
65
t o t he pool when t he client has released it s r efer ence. Your com ponent m ust t herefore support aggregat ion t o be able t o use obj ect pooling. When you im port a COM com ponent int o a COM+ applicat ion, COM+ verifies t hat your com ponent suppor t s aggr egat ion. I f it does not , COM+ disables obj ect pooling in t he Com ponent Services Explorer. I f y ou im plem ent y our obj ect using ATL, m ake sur e y our code does not cont ain t he ATL m acro DECLARE_NOT_AGGREGATABLE( ), as t his m acro pr event s your obj ect fr om being aggregat ed. By default , t he Visual C+ + 6.0 ATL Wizard inser t s t his m acro int o your com ponent ’s header file when generat ing MTS com ponent s. You m ust rem ove t his m acr o t o enable obj ect pooling ( it is safe t o do so—t here are no side effect s in COM+ ) . Anot her design point t o pay at t ent ion t o is y our pooled obj ect 's t hr eading m odel. A pooled obj ect should have no t hread affinit y of any sort —it should m ak e no assum pt ion about t he ident it y of t he t hr ead it execut es on, or use t hr ead local st orage, because t he execut ion t hr ead can be different each t im e t he obj ect is pulled fr om t he pool t o serv e a client . The pooled obj ect t her efore cannot use t he single- t hreaded apart m ent m odel ( STA) because STA obj ect s always r equire execut ion on t he sam e t hread. When you im port a com ponent t o a COM+ applicat ion, if t he com ponent 's t hr eading m odel is m ark ed as apar t m ent ( STA) , COM+ disables obj ect pooling for t hat com ponent . A pooled obj ect can only use t he fr ee m ult it hr eaded apar t m ent m odel ( MTA) , t he bot h m odel, or t he neut ral t hreaded apart m ent m odel ( NTA, covered in Chapt er 5) . I f per form ance is im port ant t o you, y ou m ay want t o base your pooled com ponent 's t hreading m odel on y our client s' t hr eading m odel. I f your client s are predom inant ly STA- based, m ark your com ponent as Both so t hat it can be loaded dir ect ly in t he client 's STA. I f your client s are predom inant ly MTA based, m ar k your com ponent as eit her Free or Both ( t he Both m odel also allows direct use by STA client s) . I f your client s are of no part icular apar t m ent designat ion, m ar k your com ponent as Neutral. For m ost pract ical pur poses, t he neut ral- t hr eading m odel should be t he m ost flex ible and per form ance- orient ed m odel. Table 3- 1 sum m arizes t hese decisions. 7DEOH3RROHGREMHFWWKUHDGLQJPRGHO &OLHQWVWKUHDGLQJPRGHO
5HFRPPHQGHGSRROHGREMHFWWKUHDGLQJPRGHO
No part icular m odel
NTA
STA
Bot h
MTA
Bot h/ MTA
Bot h
Bot h
NTA
NTA
Deciding not t o use STA has t wo im por t ant consequences:
66
• •
Pooled obj ect s cannot display a user int erface because all user int er faces require t he STA m essage loop. You cannot develop pooled obj ect s using Visual Basic 6.0 because all COM com ponent s developed in Version 6 are STA based and use t hread local st or age. The next version of Visual Basic, called Visual Basic.NET, allows y ou t o develop m ult it hr eaded com ponent s.
3 .3 .4 Obj e ct Pooling a n d Con t e x t When a pooled obj ect is placed in t he pool, it does not have any cont ext . I t is in st asis—fr ozen and wait ing for t he next client act ivat ion r equest . When it is br ought out of t he pool, COM+ uses it s usual cont ext act ivat ion logic t o decide in which cont ex t t o place t he obj ect —in it s cr eat or 's cont ext ( if t he t wo ar e com pat ible) or in it s own new cont ext . Fr om t he obj ect 's perspect ive, it is alway s placed in a new cont ext ; different fr om t he one it had t he last t im e it w as act ivat ed. Obj ect s oft en r equir e cont ex t - specific init ializat ion, such as ret r iev ing int er face point er s or fine- t uning securit y. Obj ect pooling only sav es you t he cost of reconst r uct ing a new obj ect and init ializing it t o gener ic st at e. Each t im e an obj ect is act ivat ed, you m ust st ill do a cont ext - specific init ializat ion, and y ou benefit fr om using obj ect pooling only if t he cont ext - specific init ializat ion t im e is short com pared t o t hat of t he obj ect 's const ruct or. But when cont ext - specific init ializat ion is used, how does t he obj ect know it has been placed in a new cont ex t ? How does it obj ect know when it has been ret urned t o t he pool? I t knows by im plem ent ing t he IObjectControl int erface, defined as: interface IObjectControl : IUnknown { HRESULT Activate( ); void Deactivate( ); BOOL CanBePooled( ); }; COM+ aut om at ically calls t he IObjectControl m et hods at t he appr opr iat e t im es. Client s of your obj ect don't ev er need t o call t hese m et hods. COM+ calls t he Activate( ) m et hod each t im e t he obj ect is pulled fr om t he pool t o serv e a client —j ust aft er it is placed in t he execut ion cont ext , but before t he act ual call from t he client . You should put cont ex t - specific init ializat ion in t he Activate( ) m et hod. Activate( ) is your pooled obj ect 's wakeup call—it t ells it when it is about t o st art ser ving a new client . When using Activate( ), you should ensur e t hat you hav e no left ov ers in your obj ect st at e ( dat a m em bers) fr om prev ious calls, or from a st at e t hat was m odified fr om int eract ion wit h pr ev ious client s. Your obj ect
67
should be indist inguishable from a newly creat ed obj ect . The st at e should appear as if t he obj ect ’s const ruct or was j ust called. COM+ calls Deactivate( ) aft er t he client releases t he obj ect , but befor e leav ing t he cont ex t . You should put any cont ex t - specific cleanup code in Deactivate( ). When obj ect pooling is enabled, aft er calling t he Deactivate( ) m et hod, COM+ invokes t he CanBePooled( ) m et hod t o let your obj ect decide whet her it want s t o be recycled. This is your obj ect ’s opport unit y t o override t he configured obj ect pooling set t ing at runt im e. I f your obj ect ret ur ns FALSE from CanBePooled( ), t he obj ect is released and not r et urned t o t he pool. Usually , y ou can ret ur n FALSE when you cannot init ialize t he obj ect ’s st at e t o t hat of a brand- new obj ect , because of an inconsist ency or er ror , or if y ou want t o hav e runt im e fine t uning of t he pool size and t he num ber of obj ect s in it . I n t he m ost cases, your im plem ent at ion of CanBePooled( ) should be one line: return TRUE;, and y ou should use t he Com ponent Serv ices Explorer t o adm inist er t he pool. I m plem ent ing IObjectControl is not r equired for a pooled obj ect . I f you choose not t o im plem ent it and y ou enable obj ect pooling, your obj ect is always ret urned t o t he pool aft er t he client calls Release( ) on it . Figure 3- 5 em phasizes t he calling sequence on a pooled obj ect t hat suppor t s IObjectControl. I t shows when COM+ calls t he m et hods of IObjectControl and when t he obj ect is part of a COM+ cont ext . Figu r e 3 - 5 . Th e life cycle of a p ooled ob j ect u sin g I Ob j e ct Con t r ol
68
Finally, IObjectControl has t w o abnorm alit ies wor t h m ent ioning: first , t he int er face cont ains t wo m et hods t hat do not r et urn HRESULT, t he r equired ret urned value according t o t he COM st andar d of any COM int er face. IObjectControl’s second abnorm alit y is t hat only COM+ can invok e it s m et hods. The int er face is not accessible t o t he obj ect ’s client s or t o t he obj ect it self. I f a client queries for t he IObjectControl int erface, QueryInterface( ) r et urns E_NOINTERFACE.
3 .4 Ju st - in - Tim e Act iva t ion Obj ect pooling is a gr eat inst ance m anagem ent service, but what should you do when you deal wit h r ich client s w ho can hold ont o obj ect references for long periods of t im e? I t is one t hing if t he rich client s m ake int ensive use of t he obj ect , but as y ou saw ear lier, t hey act ually m aint ain t he r eference on t he obj ect t o im prov e per form ance on t heir side, and m ay act ually call m et hods on t he obj ect for only a fract ion of t hat t im e. From t he obj ect ’s per spect ive, it m ust st ill hold ont o it s r esour ces because a call m ay
69
com e t hr ough at any m om ent . Obj ect pooling is of lit t le benefit , since it sav es you t he cost of creat ing t he obj ect , not t he cost of m aint aining it w hile t ied up wit h a client . Clear ly, anot her t act ic is requir ed t o handle gr eedy I nt r anet client s. COM+ provides anot her inst ance m anagem ent t echnique called Just - in- Tim e Act ivat ion ( JI TA) t hat allows y ou t o dedicat e an obj ect per client only while a call is in progress. JI TA is m ost useful when inst ant iat ing t he obj ect is not a cost ly oper at ion com pared wit h t he expensive or scarce resources t he obj ect holds ont o. I t is especially useful if t he obj ect holds ont o t hem for long periods. 3 .4 .1 H ow JI TA W or k s JI TA int ercept s t he call fr om t he client t o t he obj ect , act ivat es t he obj ect j ust when t he client issues a m et hod call, and t hen dest roys t he obj ect as soon as t he m et hod r et urns. As a r esult , t he client m ust never have a dir ect reference t o t he obj ect . As ex plained in Chapt er 2, if t he client is in a differ ent cont ex t t han t he obj ect , t he client act ually holds a point er t o a proxy and t he proxy int eract s wit h a st ub. The COM+ proxy and st ub perfor m t he JI TA int er cept ion, and t oget her t hey const it ut e a single logical ent it y. Let ’s call t his ent it y t he int ercept or . To guarant ee t hat t her e is always an int er cept or bet ween t he client and t he obj ect , com ponent inst ances configur ed t o use JI TA are always placed in t heir own cont ext , regardless of pot ent ial com pat ibilit y w it h t heir creat or. Figure 3- 6 shows how t his int ercept ion works: 1. The int ercept or calls t he obj ect ’s m et hod on behalf of t he client . 2. When t he m et hod call r et urns, if t he obj ect indicat es t hat it can be deact iv at ed, t he int ercept or r eleases t he obj ect and not es t o it self t hat it no longer has t he obj ect . Meanwhile, t he client cont inues t o hold a reference t o a pr oxy and does not know it s obj ect was released. 3. When t he client m akes anot her call, t he int er cept or not es t hat it is not connect ed t o an obj ect . 4. The int ercept or creat es a new obj ect . 5. The int ercept or delegat es t he call t o t he new obj ect . When t he client r eleases t he obj ect , only t he int ercept or needs t o be dest r oyed because t he obj ect was already released. Figu r e 3 - 6 . Th e in t e rce pt or h a ndles t h e m et h od ca lls in JI TA by cre a t in g t h e obj e ct a s it is n e e de d an d d isp osing of it be t w e en ca lls
70
3 .4 .2 Be n e fit s of Usin g JI TA JI TA is beneficial because y ou can now release t he expensiv e resources t he obj ect occupies long before t he client releases t he obj ect . By t hat sam e t ok en, acquisit ion of t he r esources is post poned unt il a client act ually needs t hem . Rem em ber t hat act ivat ing and dest roying t he obj ect repeat edly on t he obj ect side, wit hout t ear ing down t he connect ion t o t he client ( wit h it s client side prox y) is m uch cheaper t han norm ally creat ing and releasing t he obj ect . Anot her side effect of JI TA is t hat it im pr oves overall reliabilit y. I m agine t he case of a client t hat cr ashed or sim ply forgot t o r elease an obj ect . When using JI TA, t he obj ect and t he resour ces it holds are released independent ly of unreliable or undisciplined client s. 3 .4 .3 Usin g JI TA You can configure any COM+ com ponent t o use JI TA. On t he Act iv at ion t ab of t he com ponent ’s proper t ies page ( see Figure 3- 3) , you can check t he " Enable Just I n Tim e Act ivat ion" checkbox t o enable JI TA for your com ponent . I n fact , w hen y ou use t he Com ponent I nst allat ion Wizard t o add a new com ponent , it enables JI TA for t he new com ponent by default . However, COM+ cannot arbit rar ily kill y our obj ect j ust because t he m et hod has ret urned. What if an obj ect is not ready t o be deact iv at ed? What if it needs t o per form addit ional act ivit ies t o bring it self t o a consist ent st at e, and can only t hen be dest royed? An obj ect t hat want s t o get t he m ost out of JI TA is r equired t o do t wo t hings: fir st , it should be st at e- aware. Second, it should t ell COM+ when t he obj ect can be deact ivated. Mind y ou, a JI TA obj ect does not need t o be st at eless. I n fact , if it w ere t r uly st at eless, t here would be no need for JI TA in t he fir st place. The obj ect has t o proact ively m anage it s st at e, m uch like a t r ansact ional obj ect , as discussed in t he next chapt er . I deally, a JI TA obj ect should be act ivat ed at t he beginning of every m et hod call and deact ivat ed aft er t he call. I f y ou int end t o signal t o COM+ t o deact ivat e your obj ect only aft er a part icular m et hod r et urns or when a special
71
event has occur red, t he client m ay hold ont o t he obj ect bet ween t he calls for long periods of t im e and significant ly ham per scalabilit y. So, if a JI TA obj ect is t o be act ivat ed j ust befor e every m et hod call and deact iv at ed im m ediat ely aft er each call, t hen it needs t o do t wo t hings: at t he beginning of each call, t he obj ect should init ialize it s st at e fr om v alues saved in durable st orage. At t he end of t he call, it should ret ur n it s st at e t o t he st or age. Com m only used dur able st or age opt ions include dat abases and t he Windows filesyst em . Alt hough a JI TA obj ect can st ore it s st at e in nondurable st or age, nam ely in- m em or y, I r ecom m end not doing so for t wo reasons. First , if t he JI TA obj ect par t icipat es in t ransact ions ( discussed in t he next chapt er ) , t he st orage has t o be dur able. Second, m em ory st or age t ies t he obj ect t o a par t icular m achine and precludes m ult im achine load balancing. Not all of t he st at e of an obj ect can be sav ed by value. For ex am ple, if t he st at e includes int erface point ers t o ot her obj ect s, t he obj ect should release t hose obj ect s and r e- creat e t hem on t he next act ivat ion. A dat abase connect ion from a connect ions pool is anot her ex am ple of a st at e t hat cannot be st or ed. An obj ect should ret ur n t he connect ion t o t he pool before r et urning fr om a m et hod call and gr ab a new connect ion from t he pool upon act ivat ion. Using JI TA has one im port ant im plicat ion for int erface design—ev ery m et hod call m ust include a param et er t o ident ify t he obj ect of w hich t he m et hod is a m em ber . The obj ect uses t hat param et er t o ret rieve it s st at e from t he durable st orage, and not t he st at e of anot her inst ance of t he sam e t ype. Exam ples for such param et er s include t he account num ber for bank account obj ect s and t he order num ber for obj ect s represent ing st ore orders. Exam ple 3- 1 shows a m et hod on a JI TA obj ect t hat accept s a par am et er of t ype PARAM ( a pseudot ype invent ed for t his ex am ple) used t o ident ify t he obj ect : STDMETHODIMP CMyClass::MyMethod(PARAM objectIdentifier) The obj ect t hen uses t he ident ifier t o ret r iev e it s st at e and save t he st at e back at t he end of t he m et hod call. JI TA clear ly offer s you a t r adeoff bet ween perfor m ance ( t he overhead of r econstr uct ing t he obj ect st at e on each m et hod call) and scalabilit y ( holding ont o t he st at e and it s resources) . No definit ive rules descr ibe when and t o what ext ent y ou should t rade per form ance for scalabilit y. You m ay need t o profile your syst em , and ult im at ely r edesign som e obj ect s t o use JI TA and som e not t o use JI TA. Never t heless, JI TA is a powerful inst ance m anagem ent t echnique av ailable wit h one click of y our m ouse. JI TA also let s COM+ k now when it is allowed t o deact iv at e t he obj ect . You have already seen t hat each JI TA obj ect m ust reside in a cont ex t separ at e from t hat of it s caller. Each cont ext has a
72
cont ext obj ect associat ed wit h it , as ex plained in t he prev ious chapt er . Each cont ext obj ect has a value in it called t he done bit , which, as t he nam e im plies, is a one- bit Boolean flag. Whenever a cont ext is init ialized, and an obj ect is placed in it , t he done bit is set t o zero ( FALSE) . A JI TA obj ect let s COM+ know t hat it is ready t o be dest royed by set t ing t he done bit on t he cont ex t obj ect t o TRUE. The obj ect int er cept or checks t he done bit every t im e a m et hod r et urns cont r ol t o it . I f t he done bit is set t o TRUE, t he int ercept or releases t he JI TA obj ect . Because each COM+ cont ext m aps t o a single cont ext obj ect , a JI TA obj ect always resides in it s own pr ivat e, dedicat ed cont ext . I f m ore t han one obj ect wer e in t he cont ex t , any one could set t he done bit t o TRUE, and t he int ercept or m ight deact ivat e t he wrong obj ect . You can set t he value of t he done bit eit her pr ogram m at ically or adm inist rat ively. You can set t he done bit program m at ically in t wo ways, and bot h r equire accessing an int erface exposed by t he cont ext obj ect . The recom m ended way t o set t he done bit for a JI TA obj ect is t o use t he int er face IContextState, an int erface t hat Microsoft fine t uned t o suppor t JI TA obj ect s. I t s definit ion is as follows: enum tagTransactionVote { TxCommit= 0, TxAbort = TxCommit + 1 }TransactionVote; interface IContextState : IUnknown { HRESULT SetDeactivateOnReturn([in] BOOL bDeactivate); HRESULT GetDeactivateOnReturn([out]BOOL* pbDeactivate); HRESULT SetMyTransactionVote ([in]TransactionVote txVote); HRESULT GetMyTransactionVote ([out]TransactionVote* ptxVote); } IContextState defines m et hods for set t ing t he done bit and ret riev ing it s cur rent v alue. IContextState is also used in t r ansact ion vot ing, discussed in t he next chapt er. You can obt ain IContextState by using t he call CoGetObjectContext( ); you can call t he IContextState m et hod SetDeactivateOnReturn( ) t o set t he done bit , as shown in Ex am ple 3- 1. Ex a m p le 3 - 1 . Usin g I Con t ex t St a t e t o t ell COM + t o d ea ct iva t e t h e obj e ct
STDMETHODIMP CMyClass::MyMethod(PARAM objectIdentifier) {
73
GetState(objectIdentifier); DoWork( ); SaveState(objectIdentifier); //Let COM+ deactivate the object once the method returns HRESULT hres = S_OK; IContextState* pContextState = NULL; hres = ::CoGetObjectContext(IID_IContextState,(void**)&pContextS tate); ASSERT(pContextState != NULL)//Will be NULL if not imported to the COM+ Explorer hres = pContextState->SetDeactivateOnReturn(TRUE); ASSERT(hres != CONTEXT_E_NOJIT)//will return CONTEXT_E_NOJIT if JITA was not //enabled for this object pContextState->Release( ); } Anot her way of set t ing t he done bit uses t he IObjectContext int er face. You can obt ain t his int erface by using CoGetObjectContext( ) and calling it s SetComplete( ) m et hod. However, IObjectContext is a legacy int er face from MTS, and using it t o deact ivat e t he obj ect can hav e t ransact ion v ot ing side effect s, discussed in t he nex t chapt er . A COM+ JI TA obj ect should use IContextState.
D e a ct iva t ing a JI TA Obj e ct D e v e lope d in VB 6 .0 I f you use Visual Basic 6.0 t o develop your JI TA obj ect , y ou m ust access IObjectContext fir st , and t hen query it for IContextState t o flag t he obj ect for deact iv at ion: Dim objectContext As ObjectContext Dim contextState As IContextState Set objectContext = GetObjectContext ’QueryInterface for IContextState: Set contextState = objectContext contextState.SetDeactivateOnReturn (True) Program m at ic cont r ol over when COM+ should deact ivat e your obj ect gives you ult im at e cont r ol over when deact ivat ion occur s. When using JI TA, however, you ar e m or e likely t o want t o deact iv at e y our obj ect each t im e a m et hod r et ur ns. COM+ prov ides you wit h an adm inist r at iv e way t o inst ruct it t o always deact ivat e t he obj ect upon m et hod ret urn. When JI TA is enabled for a com ponent , you can search in t he Com ponent Ser vices Explor er for
74
t he m et hod lev el, display t he m et hod propert ies page, select t he General t ab, and check " Aut om at ically deact ivat e t his obj ect when t his m et hod r et ur ns" ( see Figure 3- 7) . Figu r e 3 - 7 . Th e m e t h od’s Gen e ra l t a b
The " Aut om at ically deact iv at e" set t ing is done at t he m et hod lev el, not t he int erface level. This set t ing pot ent ially leaves t he obj ect wit h som e m et hods t hat do not deact iv at e t he obj ect on r et urn ( especially if t hey do not acquire expensive r esources) and som e t hat do. Howev er , for consist ency’s sake, y ou should set all int erface m et hods and all com ponent int erfaces in a unifor m fashion. Even when you adm inist rat ively configur e a m et hod t o deact ivat e t he obj ect when it r et ur ns, you can st ill override t his configurat ion program m at ically at runt im e by calling IContextState::SetDeactivateOnReturn(FALSE). COM+ only uses t he adm inist r at ive set t ing when you do not m ake a program m at ic call yourself t o set t he cont ex t obj ect ’s done bit . 3 .4 .4 JI TA a n d I Obj e ct Cont r ol Your JI TA obj ect can choose t o im plem ent t he IObjectControl int er face. COM+ queries for t he int er face and calls IObjectControl::Activate( ) each t im e a new inst ance of y our com ponent is creat ed and placed in t he COM+ cont ext aft er t he obj ect const ruct or is called, but before t he act ual m et hod is called. By let t ing t he obj ect know when it ent er s a cont ex t , COM+ allows t he obj ect t o perform cont ex t - specific init ializat ion in Activate( ), such as passing a r efer ence t o t he obj ect t o anot her obj ect , caching int er face point er s t o ot her COM+ obj ect s ( such as IContextState) , or perform ing pr ogram m at ic securit y checks ( see Chapt er 7) .
75
I f you set t he done bit t o TRUE, aft er t he m et hod has r et urned ( but befor e t he obj ect dest ruct or is called) COM+ calls IObjectControl::Deactivate( ). You should put your cont ext specific cleanup code, such as releasing cached int erface point er s, in Deactivate( ). Aft er calling Deactivate( ), COM+ dest roys ( releases) t he obj ect . I f t he JI TA obj ect is not configured t o use obj ect pooling, t hen COM+ never calls IObjectControl::CanBePooled( ). However, you st ill m ust im plem ent all t he m et hods of a COM int er face y our obj ect support . Just ret ur n TRUE fr om CanBePooled( ), which m ak es your obj ect suppor t pooling ( you m ay st ill want t o configur e it t o suppor t pooling in t he fut ure) . Figure 3- 8 shows t he life cy cle of a JI TA obj ect t hat im plem ent s IObjectControl. A JI TA obj ect t hat suppor t s IObjectControl is not ified by COM+ when t he obj ect is placed in a new cont ex t and also j ust before t he obj ect leav es t he cont ext and is dest royed. Figu re 3 - 8 . Life cycle of a JI TA obj e ct t h a t im plem e n t s I Ob j ect Con t r ol
Her e are a few m ore im por t ant point s about JI TA obj ect s and I Obj ect Cont r ol: •
I f you ret ur n anyt hing except S_OK from IObjectControl::Activate( ), perhaps out of failur e t o init ialize a cont ext - specific st at e, t he client get s t he HRESULT of CO_E_INITIALIZATIONFAILED as a r et urn v alue from t he m et hod it want ed t o call.
76
•
•
Merely enabling JI TA and im plem ent ing IObjectControl will not get your obj ect deact iv at ed aft er every m et hod call—y ou m ust eit her configure t he m et hod adm inist rat ively or set t he done bit progr am m at ically. I f y ou do not want t o use JI TA, but wish t o know when you ent er a cont ex t in or der t o do cont ext - specific init ializat ion, you can enable JI TA support and im plem ent IObjectControl. Even t hough im plem ent ing IObjectControl is opt ional, I st rongly recom m end t hat you im plem ent it when y ou use JI TA because it m ak es m anaging your obj ect 's life cycle m uch easier.
3 .5 Com bin in g JI TA w it h Obj ect Poolin g The t wo inst ance m anagem ent t echniques provided by COM+ ar e not m ut ually exclusive. JI TA and obj ect pooling can be com bined in a very powerful way. Using bot h obj ect pooling and JI TA on t he sam e com ponent is useful in sit uat ions when obj ect init ializat ion is bot h gener ic ( not client specific) and expensive. Thus, using j ust JI TA would not m ak e sense; when you hav e no cont r ol over t he lengt h of t im e, t he obj ect 's client keeps it s reference t o t he obj ect , so you would realize m ar ginal gain fr om obj ect pooling. When y ou configure y our obj ect t o use bot h, inst ead of creat ing and releasing t he obj ect on each m et hod call, COM+ gr abs an obj ect from t he pool and ret ur ns t he obj ect t o t he pool aft er t he m et hod com plet es it s execut ion. The JI TA aspect s ar e st ill m aint ained because t he obj ect inst ance will be t or n away from it s client . The pool will also be used on every m et hod call, not j ust on CoCreate and Release calls fr om t he client . I m plem ent ing IObjectControl is opt ional, but I st r ongly r ecom m end it . As alway s, a call t o IObjectControl::Activate( ) m ark s ent ry t o a cont ext , and a call t o IObjectControl::Deactivate( ) m ar ks an exit . COM+ calls IObjectControl::CanBePooled( ) aft er every Deactivate( ), let t ing t he obj ect decide whet her it want s t o be recycled or dest royed. This life cycle is shown in Figur e 3- 9. When y ou configure y our com ponent t o support bot h JI TA and obj ect pooling, COM+ deact iv at es t he obj ect ev ery t im e t he done bit is set and ret ur ns it t o t he pool inst ead of releasing it . New m et hod calls ar e served by recycled obj ect s from t he pool, not w it h new inst ances. Figu re 3 - 9 . Th e life cycle of a com pon en t u sin g JI TA a nd ob j e ct pooling
77
Obj ect s now can m aint ain st at e bet ween calls because t hey are not dest royed, but r at her ret ur ned t o t he pool. The t r ut h is, when y ou use JI TA and obj ect pooling t oget her, your obj ect st ill cannot m aint ain a client - specific st at e bet w een inv ocat ions; Once t he obj ect is back in t he pool, it could v ery well be ret r iev ed t o serve a differ ent client t han t he pr ev ious one. A JI TA obj ect can m aint ain j ust t he generic part of t he st at e and benefit from going t hrough t hat init ializat ion only once. When a pooled obj ect is configur ed t o use JI TA, t he sem ant ics of t he m ax im um pool size v alue act ually set s t he t ot al num ber of obj ect s t hat COM+ is forced t o creat e t o serve act iv e client calls, not t he t ot al num ber of connect ions t o client s. The num ber of connect ions ( t he num ber of client s holding r eferences t o proxies) m ay be a m uch lar ger num ber because m any client s m ay not be engaged in calling a m et hod on obj ect s. Configuring a COM+ com ponent t o be a singlet on is an int erest ing exam ple of what y ou can do when com bining JI TA wit h obj ect pooling. A singlet on is a com ponent wit h only one inst ance. All client s share t he sam e singlet on—t he client s ar e oft en not even awar e t hat t her e is j ust one inst ance of t he class. [ 2] For exam ple, suppose you hav e a configur ed com ponent used t o cont r ol a single resource, such as a har dware device or a com m unicat ion port . To m ak e sure t hat all client s get t he sam e obj ect , y ou can configure
78
your com ponent t o use JI TA and obj ect pooling, wit h m inim um and m ax im um pool sizes set t o one. Having a pool size of exact ly one ensur es t hat at any giv en m om ent , ex act ly one obj ect ( a singlet on) is associat ed wit h a r esource. Using JI TA ensures t hat once t he obj ect has finished serv icing one client , it can serv e anot her , even if t he curr ent client has not released it s r efer ence t o it . The singlet on is also t he only case of a JI TA obj ect t hat can m aint ain full st at e bet ween m et hod calls, since you can be cer t ain t hat t he sam e obj ect is called t o serv e all client s. However, before y ou st ar t using a singlet on, m ake sure t hat it s disadvant ages ( a single point of failure, a per form ance hot spot , a bot t leneck, and an inabilit y t o scale t o large num ber or client s) are not r elevant in y our design and t hat it is a valid m odeling of an ent it y in y our applicat ion dom ain. [ 2] See Design Pat ter ns—Elem ents of Reusable Obj ect- Orient ed Softwar e, by Gam m a, et al. ( Addison Wesley, 1995) , p. 127.
3 .6 Obj e ct Con st r u ct or St r in g COM+ allows you t o pass a const r uct ion param et er t o new inst ances of your com ponent . This inst ance act ivat ion service has not hing t o do wit h applicat ion scalabilit y , JI TA, or obj ect pooling, and is not hing m or e t han a neat ser vice. On your com ponent ’s Pr opert ies page, t here is a propert ies gr oup nam ed " Obj ect const r uct ion" on t he Act ivat ion t ab. Once you enable t his serv ice ( by check ing t he " Enable obj ect const r uct ion" checkbox) , you can specify a st r ing in free for m . Every inst ance of your com ponent has access t o t his one st ring ( y ou cannot specify a st ring per inst ance) . Because calls t o CoCreateInstance( ) or CreateObject( ) do not accept init ializat ion param et er s, y ou have t o w ork t o gain access t o t he const ruct or st r ing. The first t hing you need t o do ( besides enable t he service) is have your com ponent im plem ent an int er face called IObjectConstruct, defined as: interface IObjectConstruct : IUnknown { HRESULT Construct([in]IDispatch* pConstructionObj); }; I f you enable obj ect const ruct ion but do not im plem ent t he int er face, all client at t em pt s t o cr eat e a new inst ance of y our com ponent will fail, showing t he er ror code E_NOINTERFACE. They will fail because COM+ will refuse t o hand over t o t he client an obj ect t hat could not be init ialized proper ly . IObjectConstruct has only one m et hod, Construct( ), which COM+ uses t o pass in a
79
point er t o anot her int er face called IObjectConstructString, defined as: interface IObjectConstructString : IDispatch { [id(1),propget] HRESULT ConstructString([out, retval] BSTR* pVal); }; COM+ calls y our obj ect ’s im plem ent at ion of IObjectConstruct::Construct( ) t o deliver t he st ring only once, im m ediat ely aft er calling t he obj ect const r uct or. Not e t hat COM+ passes t he const r uct ion st ring t o your obj ect befor e t he call t o IObjectControl::Activate( ), since t he init ializat ion par am et er should provide gener ic, rat her t han cont ext - specific, inform at ion. Exam ple 3- 2 shows how t o use t he const r uct or st ring obj ect passed int o IObjectConstruct::Construct( ) t o access your com ponent ’s configured const ruct or st ring. Ex a m p le 3 - 2 . I m plem e n t in g I Ob j e ct Con st r u ct ::Con st r u ct ( ) a n d a cce ssin g you r com p on e n t ’s con figu re d con st r u ct or st r in g
// IObjectConstruct::Construct( ) STDMETHODIMP CMyComponent::Construct(IDispatch * pConstructionObj) { HRESULT hres = S_OK; BSTR bstrConstruct; IObjectConstructString* pString = NULL; hres = pConstructionObj>QueryInterface(IID_IObjectConstructString, (void**)&pString); hres = pString->get_ConstructString(&bstrConstruct); pString->Release( ); //Use bstrConstruct return S_OK; } Not e t hat em pt y st r ings m ay be valid par am et ers and t hat y our obj ect should be writ t en t o handle an em pt y st ring. However, why go t hrough a som ewhat odd m echanism of r et r ieving t he st r ing fr om a dedicat ed int erface, rat her t han passing IObjectConstruct::Construct( ) a BSTR direct ly? The answer is t hat in t he fut ure, COM+ m ay allow you t o pass ot her kinds of par am et ers for const r uct ion, such as num bers, dat a st ruct ures, or m ay be even int er face point er s. The COM+ designers want ed t o put
80
in place a generic m echanism t hat could ext end t o handling m or e t han j ust st rings. You can use a const r uct ion st ring t o specify par am et ers com m on t o all com ponent s, but whose v alue is deploym ent specific, such as: • • •
Log filenam e and locat ion. The COM+ logbook, present ed in Appendix A, uses t he const r uct or st ring t o do j ust t hat . Applicat ion or com ponent configurat ion filenam e and locat ion. I f your com ponent holds a generic ODBC connect ion, y ou can specify a DSN file nam e—referencing a file cont aining infor m at ion about t he dat abase t o be used by t his com ponent —inst ead of eit her passing it in as a m et hod par am et er or har dcoding it .
3 .7 COM+ I n st a n ce M a n a ge m e n t Pit fa lls COM+ inst ance m anagem ent and obj ect act ivat ion have a few m inor pit falls and lim it at ions y ou should be aware of t o m ake t he best use of what COM+ has t o offer . This sect ion also discusses a feat ur e of t he Com ponent Serv ices Explorer t hat will help you profile your applicat ion and keep t rack of y our obj ect inst ances. 3 .7 .1 I dle Tim e M a na ge m e n t Under classic COM, a pr ocess host ing COM obj ect s would be left running as long as client s w it h act ive r eferences t o obj ect s ar e in t hat process. Once t he last client releases it s reference on t he last obj ect in t hat pr ocess, COM would shut down t he host ing process. This policy clearly conflict s wit h COM+ obj ect pooling—t he idea is t o keep obj ect s aliv e, even if t hey do not serve any client s. COM+ allows you t o configure your server applicat ion's idle t im e m anagem ent on t he Adv anced t ab of t he applicat ion's pr opert ies page ( see Figure 3- 10) . The Advanced t ab has a pr opert ies gr oup called Ser ver Pr ocess Shut down. I f your applicat ion cont ains pools of obj ect s, you can leave t he host ing pr ocess running when t he applicat ion is idle—t hat is, when t he applicat ion is not serv icing client s and all obj ect s are in t he pool. Howev er , y our obj ect s cont inue t o occupy r esources as long as t he pr ocess is running, and if t he client act ivat ion r equest s ar e few and far bet ween, t his m ay not be a good t r adeoff. Alt ernat ively, you can specify how long y ou want t o k eep t he applicat ion idle by providing any num ber bet ween 0 and 999 m inut es. You should decide on t he exact value based on your client s' calling pat t ern and opt im ize t he overall act iv at ion overhead and r esource consum pt ion. For exam ple, if you expect t he int erval 81
bet ween client s’ act iv at ions of pooled obj ect s t o be 10 m inut es, you should configure t he applicat ion t o be left idle at least t hat long, plus a cert ain safet y fact or ( 20 percent for ex am ple) . I n t his case, you would set t he idle t im eout t o 12 m inut es. I f you set t he t im eout t o 0, y ou will get t he classic COM behav ior . Set t ing t he t im eout t o 0 is especially useful during debugging because as long as t he applicat ion is running, you cannot rebuild t he com ponent DLL; you cannot r ebuild it because t he applicat ion pr ocess has t hat DLL loaded and locked. Usually , when y ou discover a defect dur ing a debug session, y ou should fix it , rebuild t he com ponent , and ret est . By set t ing t he t im eout t o 0, you can rebuild im m ediat ely. By default , aft er creat ing a new COM+ applicat ion, t he applicat ion is configured t o shut down aft er 3 m inut es of idle t im e. Figu re 3 - 1 0 . Con side r lea vin g a n applica t ion h ost in g obj e ct p ools r u n n in g e ve n w h e n idle
3 .7 .2 Too La r ge a M in im u m Pool Size I f you set a com ponent t o have an obj ect pool wit h a m inim um size great er t han zer o, t hen when t he applicat ion cont aining t he com ponent is launched, COM+ cr eat es t he m inim um size num ber of obj ect s and put s t hem in t he pool. The fir st act ivat ion request for any obj ect ( pooled or not ) from t hat applicat ion m ay t ake a long t im e t o com plet e if y ou have a t oo large a m inim um pool size. Obj ect s from y our applicat ion m ay end up paying t he pool init ializat ion pr ice, r esult ing in slow response t im e t o t heir client s. To m it igat e t his pr oblem , consider st art ing y our applicat ion
82
explicit ly, eit her m anually fr om t he Com ponent Serv ices Explorer or program m at ically by program m ing w it h t he COM+ cat alog, as explained in Chapt er 6. 3 .7 .3 Re qu e st in g a JI TA Obj e ct in t h e Ca lle r ’s Con t e x t The Com ponent Serv ices Explorer let s you r equire t hat a com ponent always be act ivat ed in it s creat or ’s cont ex t by check ing t he " Must be act ivat ed in caller’s[ 3] cont ext " checkbox on t he Act iv at ion t ab in t he com ponent propert ies page ( see Figure 3- 3) . I f t he cr eat ing client were in anot her cont ex t , t he act ivat ion call would fail w it h t he er ror code CO_E_ATTEMPT_TO_CREATE_OUTSIDE_CLIENT_CONTEXT. [ 3]
The nam e is inaccurat e—it should be " Must be act ivat ed in cr eat or 's contex t ."
You can use t his set t ing only when y ou are sur e t hat t he cr eat ing client w ill not be in anot her pr ocess and will hav e configur at ion set t ings close enough t o allow t he com ponent inst ance t o shar e it s cont ext . This set t ing is av ailable as an adv anced opt im izat ion serv ice for cases when t he calling client m ak es shor t , frequent calls t o t he com ponent and t he ov erhead of cross- cont ext m arshaling get s in your way. As y ou saw before, a JI TA inst ance m ust hav e it s own cont ext so it can have it s own done bit t o set and an in int ercept or bet w een it and t he client . The t w o set t ings, " Enable Just I n Tim e Act iv at ion" and " Must be act ivat ed in t he caller’s cont ext ," are m ut ually exclusive, yet t he Com ponent Serv ices Ex plorer gladly let s you set a com ponent t o use bot h set t ings. Bewar e of configur ing a JI TA obj ect t o always be act ivat ed in it s caller ’s cont ex t because t his configurat ion causes all act ivat ion request s t o fail. 3 .7 .4 Fa ilin g t o Re le a se Poole d Obj e ct D a t a M e m be r s When r et rieved from t he pool, a pooled obj ect should be placed in a differ ent cont ex t on each act ivat ion. As explained in t he prev ious chapt er , obj ect references under COM+ are cont ex t - relat iv e and m ust be m ar shaled bet ween cont ex t s. Your design of t he pooled obj ect m ay have it include int er face point er s t o ot her obj ect s as dat a m em ber s. Those references ar e required for t he pooled obj ect t o funct ion pr operly. I n fact , such references m ay be t he very reason why you m ade it a pooled obj ect , if creat ing t he cont ained obj ect s t akes a long t im e t o com plet e. Clear ly , y our obj ect cannot cr eat e t he cont ained obj ect s and save t hem as dat a m em bers because t he dat a m em bers would be invalidat ed on t he nex t deact iv at ion. You can get ar ound t his problem in t wo ways. First , y ou can cr eat e t he cont ained obj ect s, r egist er t hem in t he Global I nt erface Table ( GI T) ( covered in t he prev ious chapt er ) , and save t he ident ifying
83
GI T cook ies as dat a m em ber s, r at her t han r aw int erface point ers. The pooled obj ect should im plem ent IObjectControl, and on every call t o IObjectControl::Activate( ), it should get t he obj ect s out of t he GI T and hav e a cur rent - cont ex t safe copy of t he dat a m em bers. When COM+ calls IObjectControl::Deactivate( ), t he obj ect should release it s local copy. When t he pooled obj ect is finally r eleased, it should revoke t he int erface point er s fr om t he GI T. The second solut ion w ould use pooled obj ect s as dat a m em ber s. On every call t o IObjectControl::Activate( ), t he pooled obj ect should creat e ( ret r ieve fr om t heir pools) all t he helper obj ect s it needs, and on calls t o IObjectControl::Deactivate( ), it should release it s local copies. Because t he helper obj ect s ar e pooled obj ect s t hem selves, t here should not be m uch of a penalt y for cr eat ing t hem . The only t hing you should rem em ber is t o configure t he various pools t o hav e enough obj ect s in t hem . You can, of course, m ix t he t wo solut ions ( hav e som e obj ect s pooled and use t he GI T on t he r est ) . As always, you, as t he applicat ion designer, are responsible for finding t he right solut ion for y our design and addr essing t he part icular const r aint s of t he dom ain pr oblem at hand. 3 .7 .5 Poole d Obj e ct s a nd Aggr e ga t ion COM+ im plem ent s obj ect pooling by aggr egat ing your obj ect and int er cept ing t he act ivat ion calls from t he client . By doing so, COM+ keeps t r ack of your pooled obj ect and m anages it s life cy cle ( ret ur ns it t o t he pool inst ead of r eleasing it and calls IObjectControl m et hods at appropriat e t im es) . As a r esult , your pooled obj ect is discouraged fr om aggr egat ing ot her COM/ COM+ obj ect s. I m agine, for exam ple, t hat Obj ect A is a pooled obj ect , and it aggr egat es anot her pooled Obj ect B. COM+ aggregat es Obj ect A and m anages it s act ivat ion r ecycling, but who would m anage Obj ect B’s recy cling? Because t here is no way for t he client t o t ell t hat a giv en obj ect is pooled, it is bet t er t o be safe t han sorry in t his case. Even if you ar e cer t ain t hat Obj ect B is not a pooled obj ect , t here is no guarant ee t hat it will not be configured t o be a pooled obj ect in t he fut ure. Av oid aggr egat ion wit hin a pooled obj ect . 3 .7 .6 Tr a ck ing I n st a n ce Act iv it y When developing a configured com ponent t hat t ak es adv ant age of COM+ inst ance m anagem ent serv ices, it is som et im es hard t o k eep t r ack of exact ly what is going on wit h inst ances of your com ponent : how m any are in t he pool, how m any are act ually servicing client s, et c. Tr ying t o gauge t he var ious par am et ers, such as pool size and act ivat ion t im eout , m ay r equir e a lot of profiling of t he average call
84
t im e and t he client ’s calling pat t erns. The Com ponent Services Explorer prov ides you wit h crucial infor m at ion t o help y ou develop and fine t une y our applicat ion. I f y ou expand t he com ponent folder and select t he St at us View from t he t oolbar , COM+ displays v ar ious st at ist ics on inst ances of each com ponent in your applicat ion ( see Figure 3- 11) . The st at us view colum ns descr ipt ion is in Table 3- 2. You will find t he st at us view helpful in alm ost all phases of dev elopm ent and deploym ent . Figu re 3 - 1 1 . Se le ct t h e com p on en t folde r St at u s V ie w t o d isplay va riou s st at ist ics on in st a n ces of you r com p on en t s
7DEOH7KH&RPSRQHQWIROGHUVWDWXVYLHZFROXPQVIURPOHIWWRULJKW &ROXPQ
'HVFULSWLRQ
Prog I D
The com ponent ident ify ing pr ogram m at ic I D.
Obj ect s
The t ot al num ber of out st anding r eferences t o obj ect s of t his t ype. I f you use JI TA, t his num ber is t he num ber of client s t hat st ill hold a reference t o an inst ance. The num ber m ay be m uch larger t han all t he ot her num bers.
The num ber of curr ent ly act ivat ed obj ect s—obj ect s t hat are in a cont ext t ied up w it h a client . I f t he obj ect uses JI TA and set s t he done bit t o Act ivat ed TRUE aft er ev er y call, t hen t he num ber in t he Act ivat ed colum n w ill be t he sam e as t he num ber in t he I n Call colum n. Pooled
The t ot al num ber of pooled obj ect s creat ed. This num ber includes bot h t he obj ect s in t he pool and pooled obj ect s out side t he pool t hat serv ices client s.
I n Call
The num ber of obj ect s cur r ent ly execut ing m et hod calls on behalf of client s. This num ber is always less t han or equal t o t he Act ivat ed colum n because t he obj ect s can use JI TA and deact ivat e t hem selves bet ween calls.
The av erage call t im e in m illiseconds of all t he calls, on all t he m et hods, across all inst ances in t he last 20 seconds. A call t im e is defined as t he Call Tim e t im e it t ook t he obj ect t o execut e t he call, and does not include obj ect act ivat ion, t he t im e spent m arshaling t he call across cont ext , process, or m achine boundary.
Collect ing t he st at ist ics causes a sm all perform ance hit . COM+ only present s t he st at us inform at ion on obj ect s t hat ar e configur ed t o provide it . Configure your com ponent t o support st at ist ics on t he com ponent Act ivat ion t ab by check ing " Com ponent support s event s
85
and st at ist ics" ( see Figur e 3- 3) . By default , COM+ enables t his suppor t w hen y ou inst all a new configur ed com ponent .
86
Cha pt e r 4 . COM + Tr a nsa ct ions Consider t he everyday oper at ion of wit hdr awing cash fr om an aut om at ed t eller m achine ( ATM) , an operat ion y ou perfor m fr equent ly . You access your account , specify t he am ount t o wit hdr aw, and t hen receive cash fr om t he m achine. Yet even an operat ion t his m undane inv olv es m ult iple m achines ( t he ATM, t he bank m ainfram e, and probably a few ot her m achines) and m ult iple dat abases ( an account s dat abase, a m oney t ransfer dat abase, an audit dat abase, and so on) , each of which m ay also r eside on a m achine of it s own. At t he ATM it self, t he wit hdrawal involves bot h a soft war e user int er face and m echanical devices such as t he car d reader , keypad, bill deliv ery m echanism , and receipt pr int er. The difficult y in developing an ATM applicat ion lies in t he fact t hat all of t hese st eps can succeed or fail independent ly of t he ot hers. For ex am ple, suppose t he ATM can’t connect t o t he m ainfr am e at t he bank or for som e reason cannot ex ecut e y our request . Or, suppose t here is a secur it y pr oblem ( t he wrong PI N code was ent er ed) or t he hardware fails ( t he ATM r uns out of bills) . I n addit ion, m ult iple users m ay access t he bank ’s syst em sim ult aneously. Their access and t he changes t hey m ake t o t he sy st em m ust be isolat ed from one anot her. For ex am ple, while y ou are wit hdr awing m oney at t he ATM, y our spouse could be accessing t he account online and a t eller could be doing a balance check for a loan approv al. Nev er t heless, bot h you and t he bank expect eit her all t he operat ions inv olv ed in accom plishing t he r equest t o succeed, or all t he operat ions t o fail. Par t ial success or par t ial failure of a banking t r ansact ion is sim ply not accept able; you don’t want t he bank t o deduct t he m oney from t he cust om er ’s account but not dispense t he bills, or t o dispense t he bills but not deduct m oney from t he account . The expect at ion for an all- or- not hing ser ies of oper at ions charact erizes m any business scenar ios. Ent erpr ise- level serv ices such as funds m anagem ent , invent ory m anagem ent , r eservat ion sy st em s, and r et ail syst em s require an all- or- not hing series of operat ions. A logical oper at ion ( such as cash wit hdrawal) t hat com plies wit h t his r equirem ent is called a t ransact ion. The fundam ent al problem in im plem ent ing a t ransact ional syst em is t hat execut ing all t he oper at ions necessar y t o com plet e t he t r ansact ion requir es t ransit ioning bet ween int er m ediat e inconsist ent sy st em st at es—st at es t hat cannot t hem selves be t olerat ed as valid out com es of t he t r ansact ion. For ex am ple, an inconsist ent st at e would result if you were t o deduct m oney from one account but not cr edit it t o anot her in a sim ple t r ansfer of funds bet ween t he t wo account s. I n essence, an inconsist ent st at e is any sy st em - st at e t hat
87
is t he result of par t ial success or failur e of t he elem ent s of one logical oper at ion. One appr oach t o addr essing t he com plex failure scenar ios of a t r ansact ion is t o add err or- handling code t o t he business logic of your applicat ion. However , such an approach is im pract ical. A t r ansact ion can fail in num erous ways. I n fact , t he num ber of failure per m ut at ions is exponent ially pr oport ional t o t he num ber of obj ect s and r esources part icipat ing in t he t ransact ion. You ar e alm ost cert ain t o m iss som e of t he rare and hard- t o- pr oduce failure sit uat ions. Ev en if y ou m anage t o cov er t hem all, what will you do when t he sy st em evolv es—when t he behav ior of ex ist ing com ponent s changes and m or e com ponent s and resour ces ar e added, t hereby m ult iplying t he num ber of er rors you have t o deal wit h? The r esult ing code will be a fr agile solut ion. I nst ead of adding business value t o t he com ponent s, y ou will spend m ost of y our t im e writ ing er ror- handling code, perfor m ing t est ing and debugging, and t r ying t o reproduce bizar re failure condit ions. Addit ionally, t he t ons of err or - handling code will int r oduce a ser ious perform ance penalt y . The proper solut ion is not t o hav e t he t ransact ion err or- handling logic in y our code. Suppose t he t ransact ion could be abst ract ed enough t hat your com ponent s could focus on ex ecut ing t heir business logic and let som e ot her part y m onit or t he t ransact ion success or failur e. That t hird part y would also ensure t hat t he sy st em be k ept in a consist ent st at e and t hat t he changes m ade t o t he syst em ( in t he case of a failed t ransact ion) would be rolled back. That solut ion is exact ly t he idea behind t he COM+ t ransact ion m anagem ent serv ice. COM+ sim plifies t he use of t r ansact ions in t he ent er prise envir onm ent . COM+ provides adm inist r at ive configurat ion of t ransact ional support for your com ponent s. COM+ enables aut o- enlist m ent of resources part icipat ing in t he t r ansact ion and suppor t s m anaging and ex ecut ing t he t r ansact ion acr oss m achine boundaries. The COM+ t ransact ion m anagem ent service is based on t he MTS t r ansact ions m anagem ent m odel, wit h a few im pr ovem ent s and innovat ions.
4 .1 Tr a n sa ct ion Ba sics Befor e we discuss COM+ t r ansact ion support , y ou need t o underst and t he basics of t ransact ion processing, t he fundam ent al proper t ies t hat ev ery t r ansact ion m ust hav e, and som e com m on t r ansact ion scenarios. I f you are alr eady fam iliar w it h t he basic t r ansact ion concept s, feel free t o skip dir ect ly t o Sect ion 4.4 lat er in t his chapt er . For m ally , a t r ansact ion is a set of pot ent ialit y com plex operat ions t hat w ill all succeed or fail as one at om ic oper at ion. Transact ions
88
are t he foundat ion of elect r onic infor m at ion processing, suppor t ing alm ost every aspect of m odern life. Transact ions were first int roduced in t he early 1960s by dat abase vendor s. Today, ot her resource product s, such as m essaging sy st em s, suppor t t ransact ions as well. Tradit ionally , t he applicat ion dev eloper progr am m ed against a com plex Tr ansact ion Pr ocessing Monit or ( TPM) —a t hir d part y t hat coor dinat ed t he execut ion of t r ansact ions across m ult iple dat abases and applicat ions. The idea behind a TPM is sim ple: because any obj ect part icipat ing in a t r ansact ion can fail and because t he t ransact ion cannot pr oceed wit hout having all of t hem succeed, each obj ect should be able t o help det er m ine success or failure of t he ent ir e t ransact ion. This is called vot ing on t he t r ansact ion's out com e. While a t r ansact ion is in progress, t he sy st em can be in an inconsist ent st at e. When t he t r ansact ion com plet es, however, it m ust leave t he syst em in a consist ent st at e—eit her t he st at e it was in before t he t r ansact ion execut ed or a new one. Transact ions are so crucial t o t he consist ency of an inform at ion sy st em t hat , in gener al, whenev er you updat e a per sist ent st orage ( usually a dat abase) , y ou need t o do it under t he prot ect ion of a t r ansact ion. Anot her im port ant t ransact ion qualit y is it s dur at ion. Well- designed t ransact ions are of short dur at ion because t he speed wit h which y our applicat ion can process t ransact ions has a m aj or im pact on it s scalabilit y and t hr oughput . For exam ple, im agine an online r et ail st ore. The st ore applicat ion should pr ocess cust om er orders as quickly as possible and m anage every client 's order in a separ at e t ransact ion. The fast er t he t ransact ion execut es, t he m ore cust om er s per second t he applicat ion can ser vice ( t hroughput ) and t he m or e prepared t he applicat ion is t o scale up t o a higher num ber of cust om er s.
4 .2 Tr a n sa ct ion Pr oper t ie s Moder n st andards call for a t ransact ion t o be at om ic, consist ent , isolat ed, and durable. I n t ransact ion processing t er m inology, t hese proper t ies ar e referred t o as t he ACI D propert ies. When y ou design t r ansact ional com ponent s, you m ust adhere t o t he ACI D requir em ent s; t hey are not opt ional. As you will see, COM+ enfor ces t hem r igorously. Once you underst and t he ACI D requir em ent s and follow sim ple design guidelines, developing t r ansact ional com ponent s in COM+ becom es st r aight forward. 4 .2 .1 Th e At om ic Pr ope r t y When a t r ansact ion com plet es, all t he changes it m ade t o t he sy st em 's st at e m ust be m ade as if t hey were all one at om ic
89
operat ion. The word at om com es fr om t he Greek word at om os, m eaning indivisible. The changes m ade t o t he sy st em ar e m ade as if ev ery t hing else in t he universe st ops, t he changes are m ade, and t hen ev eryt hing resum es. I t is not possible t o observe t he syst em wit h only som e of t he changes. A t ransact ion is allowed t o change t he syst em st at e only if all t he par t icipat ing obj ect s and resour ces execut e t heir part successfully . Changing t he sy st em st at e by m aking t he changes is called com m it t ing t he t r ansact ion. I f any obj ect encount ers an error execut ing it s part , t he t r ansact ion abort s and none of t he changes is com m it t ed. This process is called abor t ing t he t r ansact ion. Com m it t ing or abort ing a t ransact ion m ust be done as an at om ic operat ion. A t ransact ion should not leave t hings t o do in t he backgr ound once it is done, since t hose operat ions violat e at om icit y . Every oper at ion result ing fr om t he t r ansact ion m ust be included in t he t ransact ion it self. Because t r ansact ions are at om ic, a client applicat ion becom es a lot easier t o develop. The client does not have t o m anage part ial failure of it s r equest or have com plex r ecovery logic. The client knows t hat t he t r ansact ion eit her succeeded or failed as a whole. I n case of failure, t he client can choose t o issue a new request ( st ar t a new t r ansact ion) or do som et hing else, such as alert t he user . The im port ant t hing is t hat t he client does not have t o r ecov er t he sy st em . 4 .2 .2 Th e Consist e n t Pr ope r t y A t ransact ion m ust leave t he sy st em in a consist ent st at e. Not e t hat consist ency is different from at om icit y. Even if all changes are com m it t ed as one at om ic operat ion, t he t ransact ion is requir ed t o guar ant ee t hat all t hose changes ar e consist ent —t hat t hey m ake sense. The com ponent developer is responsible for m ak ing sure t he sem ant ics of t he operat ions ar e consist ent . A t r ansact ion is r equired t o t r ansfer t he syst em from one consist ent st at e t o anot her. Once a t r ansact ion com m it s, t he syst em is in a new consist ent st at e. I n case of error, t he t ransact ion should abort and r oll back t he syst em fr om t he current inconsist ent and int er m ediat e st at e t o t he init ial consist ent st at e. Consist ency cont ribut es t o sim ple client - side code as well. I n case of failure, t he client k nows t hat t he syst em is in a consist ent st at e and can use it s higher - level logic t o decide t he nex t st ep ( or m aybe none at all, since t he syst em is in a consist ent st at e) . 4 .2 .3 Th e I sola t e d Pr ope r t y While a t ransact ion is in pr ogr ess, it m akes changes t o t he syst em st at e. I solat ion m eans no ot her ent it y ( t ransact ional or not ) is able 90
t o see t he int erm ediat e st at e of t he syst em . The int erm ediat e st at e shouldn’t be seen out side of t he t r ansact ion because it m ay be inconsist ent . Ev en if it w ere consist ent , t he t ransact ion could st ill abor t and t he changes could be rolled back. I solat ion is crucial t o overall syst em consist ency . Suppose Transact ion A allows Transact ion B access t o it s int erm ediat e st at e. Transact ion A abort s, and Transact ion B decides t o com m it . The problem is t hat Transact ion B based it s execut ion on a syst em st at e t hat was rolled back, and t her efor e Tr ansact ion B is left unknowingly inconsist ent . Managing isolat ion is not t r ivial. The resour ces par t icipat ing in a t r ansact ion m ust lock t he dat a accessed by t he t r ansact ion from all ot her s and m ust synchronize access t o t hat dat a w hen t he t r ansact ion com m it s or abort s. The t ransact ion m onit oring par t y should det ect and resolve deadlocks bet ween t ransact ions using t im eout s or queues. A deadlock occur s w hen t wo t ransact ions cont end for resour ces t he ot her one holds. COM+ r esolves deadlock s bet ween t ransact ions by abort ing t he deadlocked t r ansact ions. Theoret ically, various degrees of t ransact ion isolat ion ar e possible. I n gener al, t he m ore isolat ed t he t r ansact ions, t he m or e consist ent t heir result s are, but t he lower t he ov erall applicat ion t hroughput — t he applicat ion's abilit y t o pr ocess t r ansact ions as fast as it can. COM+ 1.0 t r ansact ions use t he highest degree of isolat ion, called serializat ion . This t erm m eans t hat t he r esult s obt ained from a set of concur rent t ransact ions are ident ical t o t he r esult s obt ained by running each t ransact ion serially. To achiev e ser ializat ion, all t he resources a t r ansact ion in process t ouches ar e locked fr om ot her t r ansact ions. I f ot her t r ansact ions t r y t o access t hose resources, t hey are blocked and cannot cont inue execut ing unt il t he or iginal t r ansact ion com m it s or abort s. The next version of COM+ ( see Appendix B) allows configuring t he isolat ion level of y our t r ansact ions and t r ades consist ency for t hroughput . 4 .2 .4 Th e D u r a ble Pr ope r t y I f a t ransact ion succeeds and com m it s, t he changes it m akes t o t he sy st em st at e should per sist in a durable st or age, such as a filesy st em , m agnet ic t apes, or opt ical st orage. Tr ansact ions require com m it m ent of t heir changes t o a dur able st orage because at any m om ent t he m achine host ing t he applicat ion could crash and it s m em ory could be erased. I f t he changes t o t he syst em 's st at e w ere in- m em or y changes, t hey would be lost and t he sy st em would be in an inconsist ent st at e. The changes a t r ansact ion m akes t o t he sy st em st at e m ust persist even if t he m achine cr ashes im m ediat ely aft er t he decision t o com m it t he changes is m ade. The com ponent 's dev eloper is required t o st or e t he new syst em st at e only in dur able resources. The dur able r esour ce m ust be r obust enough t o
91
wit hst and a cr ash w hile t ry ing t o com m it t he changes. One way t o achieve such robust ness would be t o m anage log files t o r ecover fr om t he cr ash and com plet e t he changes. However, how resilient t o cat ast r ophic failure t he resource r eally should be is an open quest ion t hat depends on t he nat ure and sensit iv it y of t he dat a, your budget , available t im e, and av ailable sy st em adm inist r at ion st aff. A durable syst em can be anyt hing fr om a har d disk t o a RAI D disk sy st em t hat has m ult iple m irr or sit es in places wit h no ear t hquakes.
4 .3 Tr a n sa ct ion Scen a r ios Applicat ions differ great ly in t heir com plexit y and need for COM+ t r ansact ions support . To underst and t he COM+ t ransact ions archit ect ure and t he needs it addresses, you should first exam ine a few gener ic t r ansact ion cases. 4 .3 .1 Sin gle Obj e ct / Sin gle Re sou r ce Tr a n sa ct ion Consider an applicat ion t hat com pr ises j ust one com ponent inst ance, an obj ect t hat pr ocesses a client ’s r equest and accesses a single resour ce ( such as a dat abase) t hat t ak es par t in a t r ansact ion. This sit uat ion is depict ed in Figur e 4- 1. The applicat ion ( in t his case, t he obj ect ) has t o infor m t he resour ce when a t r ansact ion is st art ed. This act is called enlist ing t he resource in t he t r ansact ion. The obj ect st art s m ak ing calls on t he resource int er faces, m ak ing changes t o it s st at e. Howev er , at t his point t he resource should only record ( log) t he changes and not act ually per form t hem . I f t he obj ect encount ers no err ors when execut ing a client ’s request , t hen on com plet ion it inform s t he r esource t hat it should t ry t o com m it t he changes. I f t he obj ect encount er s err ors, it should inst ruct t he resource t o abort and r oll back t he changes. Even if t he obj ect want s t o com m it t he t r ansact ion, any exist ing er rors on t he resource side m ight cause t he t r ansact ion t o abort . Figu r e 4 - 1 . M an a gin g a t r an sa ct ion in a sin gle obj e ct / sin g le re sou r ce scen ar io
Not e t hat only t he applicat ion can r equest t o com m it t he t r ansact ion, but eit her t he applicat ion or t he r esource can abort it . 92
You can easily deal wit h a single obj ect / single resource scenar io on your own wit hout relying on COM+ t r ansact ions by m aking explicit program m at ic calls t o enlist a resour ce in a t r ansact ion and inst ruct ing it t o com m it or r oll back at t he end of t he t r ansact ion. Most r esources support t his sor t of int eract ion out - of- t he- box and expose sim ple funct ions, such as BeginTransaction( ) and EndTransaction(commit/abort). 4 .3 .2 M ult iple Ob j e ct s/ Sin gle Re sou r ce Tr a n sa ct ion Suppose you have m ult iple obj ect s in y our applicat ion, each of which r equires access t o t he sam e resource t o service a part icular client r equest . Suppose your design calls for cont aining all t he changes t he obj ect s m ake t o t he r esource in t he sam e t r ansact ion, t o ensur e consist ency of t hese m ult iple changes ( see Figure 4- 2) . Figu r e 4 - 2 . M u lt ip le com pon e n t s w it h a sin gle r e sou rce t r an sact ion
Unfort unat ely, t hings get m uch m ore com plicat ed t han in t he previous scenario. The m ain problem is coordinat ion. Since t he resource should be enlist ed in t he t ransact ion j ust once, who should be responsible for enlist ing it ? Should it be t he first obj ect t hat accesses it ? Or m ay be it should be t he first obj ect t hat is creat ed? How would t he obj ect s know and coordinat e t his inform at ion? I n addit ion, since t he obj ect s can all be on differ ent m achines, how would you propagat e t he t ransact ion fr om one m achine t o t he nex t ? How would t he obj ect s know what t ransact ion t hey ar e in? What should you do if one m achine crashes while t he ot her m achines cont inue t o execut e t he client request ? Each of t he obj ect s can encount er er rors and abor t t he t ransact ion, and t hey ask t he resource t o com m it t he changes only if t hey all succeed. The pr oblem here is deciding which obj ect is responsible for collect ing t he vot es. How would an obj ect k now t hat a t r ansact ion is ov er ? Who is responsible for not ifying t he resour ce of t he vot ing result —t hat is, inst ruct ing t he r esource t o t ry t o com m it or r oll back t he changes? What should t he obj ect s do wit h t heir own st at e ( t heir dat a m em bers) ? I f t he r esource is unable t o com m it t he changes, t he t r ansact ion m ust abort ; in t hat case, t he obj ect s' st at e 93
reflect s inconsist ent sy st em st at e. Who will inform t he obj ect s t o purge t heir inconsist ent st at e? How would t he obj ect s know what par t of t heir st at e const it ut es syst em inconsist ency? For t unat ely, COM+ t ransact ions support m akes t his scenario as easy t o deal wit h as t he prev ious one. COM+ t ak es car e of enlist ing t he resource, propagat ing t he t ransact ion across m achine boundar ies, collect ing t he com ponent s’ v ot es, and m aint aining overall r esour ce and obj ect st at e consist ency. 4 .3 .3 M ult iple Ob j e ct s/ M u lt iple Re sou r ce s Tr a nsa ct ion An ent er prise applicat ion oft en consist s of m ult iple obj ect s accessing m ult iple r esour ces wit hin t he sam e t r ansact ion ( see Figure 4- 3) . Figu r e 4 - 3 . An e n t e rpr ise ap plica t ion com prising m ult iple com pon e n t s a n d r e sou rce s
I n addit ion t o all t he coordinat ion challenges posed by t he previous scenar io, y ou now hav e t o enlist all t he resources j ust once in t he t r ansact ion. Who k eeps t rack of what resources are used? You definit ely don’t w ant t o hav e t hat knowledge in your code because it could change. Who is r esponsible for infor m ing t he resources about t he t r ansact ion out com e ( t he com ponent s’ vot es) and asking t hem t o t r y t o com m it or abort ? Since any one of t he r esources can refuse t o com m it t he changes, how do y ou know about it and how would you inst ruct t he ot her resources to roll back t heir changes? Your com ponent s and resources m ay all be on differ ent m achines, result ing in m ult iple point s of failur e. Transact ion processing m onit ors ( TPMs) hav e evolved t o answer t hese challenges, but t hey requir e ex plicit calls fr om t he applicat ion, which result s in a cum ber som e program m ing m odel. Yet again, COM+ t ransact ions suppor t m akes t his sit uat ion as easy as t he fir st one. Even in a dist ribut ed envir onm ent wit h m ult iple resources, your program m ing m odel is elegant and sim ple. I t allows you t o focus on your business logic while relying on COM+ t o m anage t he t r ansact ion for y ou.
94
4 .4 COM+ Tr a nsa ct ion s Ar ch it e ct u r e COM+ is an advanced TPM t hat provides your com ponent s w it h easy- t o- use adm inist rat ive configurat ion for y our t r ansact ional needs. COM+ encapsulat es t he under ly ing t ransact ion m onit oring and coordinat ion required t o m anage a t r ansact ion. The COM+ t r ansact ions ar chit ect ure defines a few basic concept s y ou need t o underst and t o t ak e advant age of COM+ t r ansact ions support : resource m anager s, t he t r ansact ion root , t he t wo- phase com m it prot ocol, and t he Dist ribut ed Transact ion Coordinat or ( DTC) . 4 .4 .1 Re sou r ce M a n a ge r s A r esource ( such as a dat abase m anagem ent syst em ) t hat can par t icipat e in a COM+ t ransact ion is called a r esource m anager . A resource m anager knows how t o conduct it self proper ly in t he scope of a COM+ t ransact ion—it r ecor ds t he changes done by your applicat ion's obj ect s and will only com m it t he changes when t old t o do so. A resource m anager knows how t o discard t he changes and rev er t t o it s pr evious st at e if it is t old t o r oll back . A r esource m anager can aut o- enlist in a t r ansact ion—t he resour ce m anager can det ect it is being accessed by a t ransact ion and enlist it self in it . Every COM+ t ransact ion has a unique t r ansact ion I D ( a GUI D) , cr eat ed by COM+ at t he beginning of t he t ransact ion. The r esour ce m anager keeps t rack of t he t ransact ion I D and will not enlist t wice. Aut o- enlist ing m eans t hat y our com ponent s ar e not required t o explicit ly enlist t he resour ces needed for a t ransact ion; t her efor e, t hey do not have t o deal wit h t he problem of m ult iple obj ect s accessing t he sam e r esource, not k nowing whet her or not it is alr eady enlist ed in t he t ransact ion. A r esource m anager m ust st ore it s dat a in a dur able st orage t o m aint ain t he t ransact ion durabilit y. To m aint ain t he t ransact ion's isolat ion, a r esource m anager m ust lock all dat a ( such as r ows, t ables, and queues) t ouched by t he t ransact ion, and allow only obj ect s t hat t ake part in t hat t ransact ion t o access t hat dat a. Not e t hat all t he har d work requir ed t o m anage a r esource m anager is hidden from your com ponent s. The burden is on t he resource m anager 's shoulders, not yours. A r esource m anager m ust v ot e on t he t r ansact ion's r esult . Once t he t r ansact ion is ov er , COM+ asks each par t icipat ing resour ce m anager , " I f you wer e ask ed t o com m it t he changes, could you?" . A resource m anager is represent ed by a sy st em serv ice t hat m anages t he resource, and your obj ect s access t he r esour ce m anager v ia a proxy. Quit e a few resour ces t oday com ply wit h t hese requir em ent s: first and forem ost is Microsoft SQL Ser ver ( Ver sions 6.5 and above) , but
95
ot her non- Microsoft dat abases, such as Oracle 8i and I BM DB2, ar e COM+ resource m anager s as well. A r esource m anager does not hav e t o be a dat abase; for ex am ple, Micr osoft Message Queue ( MSMQ) is a r esource m anager . 4 .4 .2 Tr a n sa ct ion Root When m ult iple obj ect s t ak e part in a t r ansact ion, one of t hem has t o be t he first t o ask t hat a t ransact ion be creat ed t o cont ain t he operat ion ( usually a client ’s r equest ) . That first obj ect is called t he t r ansact ion root . A given t r ansact ion has exact ly one root ( see Figure 4- 4) . Figu r e 4 - 4 . A t ra n sact ion ’s root ob j ect
Designat ing an obj ect as a t ransact ion’s root , or as an int ernal obj ect , is done adm inist r at ively . The com ponent ’s developer configures it t o eit her not t ake part in t ransact ions; t o requir e a t r ansact ion, ( t o j oin an ex ist ing t r ansact ion if one ex ist s) ; or t o st ar t a new t r ansact ion if none ex ist s. I f t he com ponent st art s a new t r ansact ion, t hen it becom es t he r oot of t hat t ransact ion. The dev eloper can also configur e t he com ponent t o always st art a new t r ansact ion—t o alw ays be t he root of a new t r ansact ion. Once a t r ansact ion is creat ed, when Obj ect A in Transact ion T1 cr eat es anot her obj ect , Obj ect B, according t o B's configur at ion, it will: • •
•
Be par t of Transact ion T1. Not be par t of T1 or any ot her t ransact ion. This m ay com pr om ise isolat ion and consist ency because B can per form operat ions t hat will per sist even if T1 abor t s. Also, B has no way of deciding whet her T1 should abor t in case B has an err or . St art a new Transact ion T2. I n t hat case, Obj ect B becom es t he root of t he new t ransact ion. This opt ion m ay also com pr om ise isolat ion and consist ency, as one t ransact ion could com m it and t he ot her one could abor t independent ly of t he ot her .
Neit her A nor B needs t o act iv ely do anyt hing t o decide on t he t r ansact ion. COM+ checks t he obj ect 's configur at ion and places it in t he corr ect t r ansact ion aut om at ically. 96
4 .4 .3 Th e Tw o- Ph a se Com m it Pr ot ocol COM+ uses a t ransact ion m anagem ent prot ocol called t he t wophase com m it t o decide on a t ransact ion r esult , com m it changes t o t he syst em st at e, and enfor ce at om icit y and consist ency . The t wophase com m it pr ot ocol enables COM+ t o suppor t t ransact ions t hat inv olv e m ult iple resources. Aft er t he t ransact ion’s r oot st art s a new t r ansact ion, COM+ st ays out of t he way . New obj ect s m ay j oin t he t r ansact ion, and every resource m anager accessed aut om at ically enlist s it self wit h t hat t r ansact ion. The obj ect s ex ecut e business logic and t he resour ce m anager s record t he changes m ade under t he scope of t he t r ansact ion. You alr eady saw t hat all t he applicat ion’s obj ect s in a t r ansact ion m ust vot e during t he t ransact ion for whet her t he t r ansact ion should abort ( if t he obj ect s had an er ror) or be allowed t o com m it ( if t he obj ect s hav e done t heir work successfully ) . Again, abst aining fr om v ot ing on t he t r ansact ion’s out com e is not an opt ion for any obj ect in t he t ransact ion. A t ransact ion ends when t he root obj ect is released ( or deact iv at ed, when y ou’re using JI TA) . At t hat point , COM+ st eps back int o t he pict ure and checks t he com bined vot e of t he par t icipat ing obj ect s. I f any obj ect vot ed t o abort , t he t r ansact ion is t erm inat ed. All part icipat ing r esource m anager s are inst ruct ed t o r oll back t he changes m ade dur ing t he t r ansact ion. I f all t he obj ect s in t he t r ansact ion vot e t o com m it , t he t wo- phase com m it pr ot ocol st ar t s. I n t he fir st phase, COM+ asks all t he resource m anager s t hat t ook part in t he t ransact ion if t hey have any r eservat ions in com m it t ing t he changes recorded during t he t r ansact ion. Not e t hat COM+ is not inst r uct ing t he resource m anager s t o com m it t he changes. COM+ m erely ask s for t heir v ot e on t he m at t er . At t he end of t he fir st phase, COM+ has t he com bined v ot e of t he r esource m anagers. The second phase of t he prot ocol act s upon t hat com bined v ot e. I f all r esour ce m anagers vot ed t o com m it t he t ransact ion in t he fir st phase, t hen COM+ would inst ruct all of t hem t o com m it t he changes. I f ev en one of t he resource m anager s said in phase one t hat it could not com m it t he changes, t hen in phase t wo, COM+ would inst r uct all t he resource m anager s t o r oll back t he changes m ade, t hus abort ing t he t r ansact ion. I t is im port ant t o em phasize t hat a resource m anager ’s vot e t hat has no r eser vat ions about com m it t ing is special: it is an unbr eak able pr om ise. I f a r esource m anager v ot es t o com m it a t r ansact ion, it m eans t hat it cannot fail if, in t he second phase, COM+ inst ruct s it t o com m it . The r esource m anager should ver ify befor e v ot ing t o com m it t hat all t he changes are consist ent and legit im at e. A r esour ce m anager never goes back on it s vot e. This is t he basis for enabling t ransact ions. The various resour ce m anager
97
vendor s have gone t o great lengt hs t o im plem ent t his behavior exact ly. 4 .4 .4 Th e D ist r ibu t e d Tr a n sa ct ion Coor dina t or As dem onst r at ed in t he t r ansact ion scenarios descr ibed previously, t here is a clear need t o coordinat e a t ransact ion in a dist r ibut ed env ironm ent , t o m onit or t he obj ect s and resources in t he t r ansact ion, and t o m anage t he t wo- phase com m it . Managing t he int er act ion bet ween t he com ponent s ( by collect ing t heir v ot es) is done by COM+ ; m anaging t he t wo- phase com m it prot ocol is done by t he Dist ribut ed Tr ansact ion Coor dinat or ( DTC) . The DTC is a sy st em service t ight ly int egr at ed wit h COM+ . The DTC cr eat es new t r ansact ions, propagat es t r ansact ions acr oss m achines, collect s resource m anager s’ vot es, and inst r uct s r esour ce m anagers t o roll back or com m it . Every m achine running COM+ has a DTC syst em service. When an obj ect t hat is par t of a t ransact ion on Machine A t r ies t o access anot her obj ect or a r esource on Machine B, it act ually has a proxy t o t he rem ot e obj ect or resour ce. That pr oxy pr opagat es t he t r ansact ion I D t o t he obj ect / r esource st ub on Machine B. The st ub cont act s t he local DTC on Machine B, passing it t he t r ansact ion I D and infor m ing it t o st ar t m anaging t hat t ransact ion on Machine B. Because t he t r ansact ion I D get s propagat ed t o Machine B, r esour ce m anager s on Machine B can now aut o- enlist w it h it . When t he t r ansact ion is done, COM+ exam ines t he com bined t r ansact ion vot e of all par t icipat ing obj ect s. I f t he com bined vot e decides t o abort t he t ransact ion, COM+ inst r uct s all t he par t icipat ing resource m anagers on all part icipat ing m achines t o roll back t heir changes. I f t he com bined obj ect s’ v ot e was t o t ry t o com m it t he t ransact ion, t hen it is t im e t o st art t he t wo- phase com m it pr ot ocol. The DTC on t he r oot m achine collect s t he resour ce m anager s’ vot es on t he r oot m achine and cont act s t he DTC on every m achine t hat t ook par t in t he t ransact ion, inst ruct ing t hem t o conduct t he fir st phase on t heir m achines ( see Figure 4- 5) . The DTCs on t he r em ot e m achines collect t he resour ce m anagers’ v ot es on t heir m achines and for ward t he result s back t o t he DTC on t he root m achine. Aft er t he DTC on t he root m achine r eceives t he r esult s from all t he rem ot e DTCs, it has t he com bined r esour ce m anagers’ v ot e. I f all of t hem vot ed t o com m it , t hen t he DTC on t he root m achine again cont act s all t he DTCs on t he r em ot e m achines, inst ruct ing t hem t o conduct phase t wo on t heir r espect ive m achines and t o com m it t he t r ansact ion. I f, how ever, ev en one resour ce m anager v ot ed t o abort t he t r ansact ion, t hen t he DTC on t he r oot m achine infor m s all t he DTCs on t he r em ot e m achines t o conduct phase t wo on t heir respect ive m achines and abort t he t ransact ion. Not e t hat only t he
98
DTC on t he root m achine has t he com bined vot e of phase one, and only it can inst r uct t he final abor t or com m it . Figu r e 4 - 5 . COM + a n d t h e D TC m an ag e a dist r ib u t ed t ra n sact ion
4 .4 .5 Tr a n sa ct ions a n d Con t e x t A given t ransact ion can cont ain obj ect s from m ult iple cont ext s, apart m ent s, processes, and m achines ( see Figur e 4- 6) . Figu r e 4 - 6 . A t ra n sa ct ion ( w h ose scop e is in dica t e d by t h e da sh e d lin e) is u n re la t ed t o m ach in e, pr oce ss, a p ar t m e n t , an d con t e x t
Each COM+ cont ext belongs t o no m ore t han one t ransact ion, and m ay be none at all. COM+ dedicat es a single bit in t he cont ex t obj ect ( discussed in Chapt er 2) for t r ansact ion vot ing. An obj ect vot es on a t ransact ion’s out com e ( whet her t o proceed t o phase one of t he t wo- phase com m it prot ocol or t o abort ) by set t ing t he value of t hat bit . As a r esult , a t ransact ional obj ect m ust hav e it s own 99
privat e cont ext . Two t ransact ional obj ect s cannot share a cont ex t because t hey only hav e one bit t o vot e wit h. I f t wo obj ect s share a cont ext and one of t hem want s t o abort and t he ot her want s t o com m it , t hen y ou w ould have a problem . Therefore, each COM+ obj ect belongs t o at m ost one t ransact ion ( because it belongs t o exact ly one cont ex t ) and an obj ect can only vot e on t he out com e of it s own t r ansact ion. Collect ing t he obj ect ’s v ot e is done by t he cont ext ’s int ercept or when t he obj ect is released or deact ivat ed. The cont ext obj ect has m or e t o do wit h t he t r ansact ion t han j ust holding t he obj ect ’s v ot e bit . I nt ernally , each cont ext obj ect st ores references t o t he t ransact ion it belongs t o, if any exist . The cont ex t obj ect st ores t he t r ansact ion’s I D and a point er t o t he t r ansact ion obj ect it self. Ev ery t ransact ion is r epr esent ed by an int erface called ITransaction, and t he cont ex t obj ect st ores an ITransaction* point er t o t he cur rent t ransact ion it belongs t o. You can gain access t o t hat inform at ion by accessing t he cont ext obj ect and obt aining t he IObjectContextInfo ( fir st pr esent ed in Chapt er 2) , defined as: interface IObjectContextInfo : IUnknown { BOOL IsInTransaction( ); HRESULT GetTransaction(IUnknown** ppTransaction); HRESULT GetTransactionId([out] GUID* pTransactionID); HRESULT GetActivityId([out] GUID* pActivityID); HRESULT GetContextId([out] GUID* pContextId); }; The GetTransactionId( ) m et hod ret ur ns t he t ransact ion I D ( a GUI D) . The IsInTransaction( ) m et hod ret urns TRUE if t he cont ext is included in a t r ansact ion. The GetTransaction( ) m et hod r et ur ns a point er t o t he current t r ansact ion t his cont ex t is par t of, in t he form of a ITransaction* int erface point er. A full discussion of t he ITransaction int erface is bey ond t he scope of t his chapt er . I t is used by resour ce m anagers t o aut o- enlist in a t r ansact ion and t o v ot e during t he t wo- phase com m it prot ocol. Br iefly, when t he obj ect accesses a r esour ce m anager, it does so via a proxy . The r esource m anager’s proxy ret rieves t he t ransact ion I D and t he ITransaction* point er from t he cont ext obj ect and forwards t hem t o t he resource m anager for aut o- enlist m ent . The resource m anger look s at t he t r ansact ion I D. I f it is alr eady enlist ed in t hat t r ansact ion, t hen it does not hing. Howev er, if t his is t he first t im e t he r esour ce m anager is accessed by t hat t r ansact ion, it uses t he ITransaction* point er t o enlist . 4 .4 .6 COM + Tr a n sa ct ions Ar ch it e ct ur e Be n e f it s The benefit s of COM+ t r ansact ions archit ect ure were im plied in t he previous discussion of t he archit ect ur e’s elem ent s. Now t hat you
100
hav e t he com prehensive pict ure, y ou can see t hat t he m ain benefit s are as follows: •
•
• •
Aut o- enlist m ent of r esour ce m anagers sav es you t he t rouble of m ak ing sur e t hat resour ces are enlist ed exact ly once. Ot herw ise, com ponent s would be coupled t o one anot her by hav ing t o coordinat e who enlist s what r esource and when. An obj ect and it s client do not ever need t o k now what t he ot her obj ect s ar e doing, whet her t hey require t ransact ions, or what anot her obj ect ’s vot e is. COM+ places obj ect s in t r ansact ions aut om at ically, accor ding t o t heir configurat ion. COM+ collect s t he obj ect s’ vot es and rollback changes. All an obj ect has t o do is vot e. The program m ing m odel is sim plified, r obust , easier, and fast er t o im plem ent . The COM+ t ransact ions archit ect ure decouples t he com ponent s from specific TPM calls. Ther e is not hing in t he com ponent s’ code t hat relat es t o t he DTC or t o t ransact ion m anagem ent .
4 .5 Configu r in g Tr a n sa ct ion s Now t hat y ou underst and what t ransact ions are and what t hey ar e good for and have rev iewed t he COM+ t ransact ion archit ect ure, it is t im e t o put t hat knowledge int o pract ice t o build and configure t r ansact ional com ponent s in COM+ . You can use t he Com ponent Serv ices Explorer t o configure t r ansact ion support for y our com ponent s. Every com ponent has a Transact ions t ab on it s proper t ies page. The t ab offers you five opt ions for t ransact ion suppor t ( see Figur e 4- 7) : Disabled, Not Suppor t ed, Support ed, Requir ed, and Requir es New. The set t ings let you cont r ol whet her inst ances of your com ponent t ak e part in a t r ansact ion and if so, whet her and when t hey should be t he root of t hat t ransact ion. Figu re 4 - 7 . Con figu re t ra n sact ion su pport for a com pon e n t on t h e com p on en t ’s Tra n sact ion s t a b
101
COM+ det er m ines which t ransact ion t o place t he obj ect in when it cr eat es t he obj ect . COM+ bases it s decision on t wo fact ors: t he t r ansact ion of t he obj ect ’s creat or and t he configur ed t ransact ion suppor t of t he obj ect ( act ually, for t he com ponent t hat t he obj ect is an inst ance of) . A COM+ obj ect can belong t o it s cr eat or’s t ransact ion, be a r oot of a new t ransact ion, or not t ak e par t in a t r ansact ion. I f t he obj ect is configured wit h t ransact ion support Disabled or Not Support ed, it will nev er be part of a t ransact ion, r egardless of whet her it s creat or has a t ransact ion or not . I f t he obj ect is configur ed wit h Suppor t ed and it s creat or has a t ransact ion, t hen COM+ places t he obj ect in it s cr eat or’s t ransact ion. I f t he creat ing obj ect does not have a t r ansact ion, t hen t he newly cr eat ed obj ect will not have a t r ansact ion. I f t he obj ect is configured wit h t r ansact ion support set t o Required, t hen COM+ put s it in it s cr eat or’s t ransact ion if t he cr eat ing obj ect has a t ransact ion. I f t he cr eat ing obj ect does not hav e a t ransact ion and t he obj ect is configured t o require a t r ansact ion, COM+ cr eat es a new t r ansact ion for t he obj ect , m ak ing it t he root of t hat new t r ansact ion. I f t he obj ect is configured wit h t r ansact ion support set t o Requires New, t hen COM+ cr eat es a new t r ansact ion for it , m aking it t he root of t hat new t ransact ion, regardless whet her it s cr eat or has a t ransact ion or not . The COM+ t r ansact ion allocat ion decision m at rix is sum m arized in Table 4- 1. 7DEOH&20WUDQVDFWLRQDOORFDWLRQGHFLVLRQPDWUL[ 2EMHFWWUDQVDFWLRQDOVXSSRUW &UHDWRULVLQWUDQVDFWLRQ
7KHREMHFWZLOOWDNHSDUWLQ
Disabled/ Not Support ed
No
No Transact ion
Support ed
No
No Transact ion
Required
No
New Transact ion ( will be t he r oot )
Required New
No
New Transact ion ( will be t he r oot )
Disabled/ Not Support ed
Yes
No Transact ion
Support ed
Yes
Creat or ’s Tr ansact ion
102
Required
Yes
Creat or ’s Tr ansact ion
Required New
Yes
New Transact ion ( will be t he r oot )
Once COM+ det erm ines what t r ansact ion t o place t he obj ect in, t hat placem ent is fixed for t he life of t he obj ect , unt il t he obj ect is released by t he client . I f t he obj ect is not part of a t ransact ion, it will nev er be part of one. I f t he obj ect is part of a t r ansact ion, it w ill always be par t of t hat t ransact ion. Figure 4- 8 shows an ex am ple of how obj ect s are allocat ed t o t r ansact ions. A client t hat does not hav e a t ransact ion creat es an obj ect configured t o require a t r ansact ion. COM+ creat es a new t r ansact ion for t hat obj ect ( Transact ion 1) , m ak ing it t he root of t he t r ansact ion. The obj ect t hen cr eat es fiv e m ore obj ect s, each wit h a differ ent t ransact ion configur at ion. The obj ect s configured as Disabled and Not Support ed are placed out side Tr ansact ion 1. The obj ect s m ar ket Suppor t ed and Required are placed in Transact ion 1. However, t he obj ect configured as Requires New cannot shar e it s cr eat or’s t ransact ion, so COM+ cr eat es a new t ransact ion ( Transact ion 2) for t hat obj ect . Figu r e 4 - 8 . Alloca t ing ob j e ct s t o t ra n sact ion s ba se d on t h eir con figu ra t ion a nd t h e t r an sact ion re qu ir em e n t s of t h e cr ea t ing obj e ct
4 .5 .1 Tr a n sa ct ion D isa ble d When y ou configur e a com ponent wit h t ransact ion suppor t set t o Disabled, t he com ponent never t akes par t in any t r ansact ion. COM+ also does not consider t ransact ional configur at ion w hen deciding on act ivat ion cont ext for t his com ponent or ot her com ponent s it cr eat es. As a r esult , t he obj ect m ay or m ay not share it s cr eat or ’s cont ext , depending on t he configur at ion of ot her serv ices. You should be careful when m ixing t ransact ional obj ect s wit h nont ransact ional obj ect s, as it can j eopar dize isolat ion and consist ency . The nont ransact ional obj ect s m ay have error s, but because t hey are not par t of t he t r ansact ion, t hey cannot affect
103
t r ansact ion out com e ( t hreat ens consist ency) . I n addit ion, t he nont ransact ional obj ect s can act based on infor m at ion not yet com m it t ed ( t hr eat ens isolat ion) . The Disabled t r ansact ion support set t ing is useful in t wo sit uat ions. The first sit uat ion is w hen y ou have no need for t r ansact ions. The second is when y ou want t o prov ide cust om behavior and you need t o perform y our own pr ogr am m at ic t ransact ion support or enlist resources m anually . Not e t hat y ou ar e not allowed t o v ot e on t he out com e of any COM+ t r ansact ion; y ou have t o m anage your t r ansact ion yourself. 4 .5 .2 Tr a n sa ct ion N ot Su ppor t e d When y ou configur e a com ponent wit h t ransact ion suppor t set t o Not Support ed, even t hough it never t ak es part in any t ransact ion, COM+ t akes int o account t ransact ional configur at ion when deciding on t he act iv at ion cont ex t for t his com ponent or ot her com ponent s it cr eat es. As a r esult , t he obj ect shares it s cr eat or ’s cont ex t only if t he creat ing obj ect is also configured wit h Not Suppor t ed. Not Support ed is t he default value when im port ing a classic COM com ponent t o COM+ . Transact ion suppor t set t o Not Support ed is useful w hen t he oper at ions per form ed by t he com ponent are nice t o hav e, but should not abort t he t ransact ion t hat creat ed t hem if t he operat ions fail. For exam ple, in t he ATM use case, pr int ing t he receipt is not a cr it ical oper at ion. The w it hdrawal t r ansact ion should com m it and t he cust om er should get t he bills even if t he ATM was unable t o pr int a r eceipt . I n all ot her cir cum st ances, t r ansact ions configured as Not Suppor t ed can j eopardize isolat ion and consist ency when m ixed wit h t ransact ional com ponent s, for t he sam e r easons discussed when t ransact ion support is set t o Disabled. 4 .5 .3 Tr a n sa ct ion Su ppor t e d When y ou configur e a com ponent wit h t ransact ion suppor t set t o Suppor t ed, t he obj ect j oins t hat t ransact ion if t he obj ect ’s creat ing client has a t ransact ion. I f t he cr eat ing obj ect does not have a t r ansact ion, t he obj ect does not t ake part in any t ransact ion. Surpr isingly , t his awkward set t ing can be useful. I m agine a sit uat ion when y ou want t o propagat e a t r ansact ion from t he cr eat ing client of your obj ect t o downst ream obj ect s your obj ect cr eat es, but y our obj ect has no use for t r ansact ions it self. I f t he downst ream obj ect s requir e t ransact ion suppor t and you configure your obj ect t o not r equire a t ransact ion, t hen t he downst ream obj ect s will be placed in separ at e t ransact ions. Set t ing t he t r ansact ion support t o Support ed allows you t o propagat e t he t r ansact ion downst r eam . I n all ot her cases, you should av oid t his set t ing; it can j eopardize consist ency and isolat ion w hen t he 104
cr eat ing client does not hav e a t ransact ion, but t he downst ream obj ect s you creat e st ill r equire t r ansact ion support and are placed in t r ansact ions separat e fr om y our client . Even t hough t he com ponent m ay not have a direct need for t r ansact ion support , it st ill has t o abide by t ransact ional com ponent design guidelines ( discussed lat er in t his chapt er) , which m ay be a liabilit y if it does not require a t r ansact ion. Use t his set t ing j udiciously . 4 .5 .4 Tr a n sa ct ion Re qu ir e d When y ou configur e a com ponent wit h t ransact ion support set t o Required, y ou st at e t o COM+ t hat your com ponent requir es a t r ansact ion t o operat ion pr operly and t hat y ou have no obj ect ion t o sharing y our cr eat or’s t ransact ion. I f t he creat ing client has a t r ansact ion, t he obj ect j oins it . I f t he client does not have one, COM+ m ust cr eat e a new t ransact ion for t he obj ect , m aking it t he root of t he new t ransact ion. Not e t hat y our com ponent ’s code should operat e ident ically w hen it is t he root and when it j ust t akes part in a t r ansact ion. There is no way y our obj ect can t ell t he differ ence anyway. Set t ing t ransact ion support t o Required is by far t he m ost com m only used t ransact ion suppor t set t ing for t ransact ional com ponent s. Of cour se, t he com ponent m ust adher e t o t he design requir em ent s of a t r ansact ional com ponent . 4 .5 .5 Tr a n sa ct ion Re qu ir e s N e w When y ou configur e a com ponent wit h t ransact ion suppor t set t o Requires New, an inst ance of y our com ponent is always t he root of a new t r ansact ion, r egardless of whet her it s cr eat ing client has a t r ansact ion or not . This set t ing is useful when y ou want t o perfor m t r ansact ional work out side t he scope of t he creat ing t ransact ion. Exam ples would be when y ou want t o perfor m logging or audit operat ion or when you want t o publish event s t o subscribers, regardless of whet her your creat ing t r ansact ion com m it s or abort s. You should be ex t rem ely careful when using t he Requires New set t ing. Verify t hat t he t wo t ransact ions ( t he cr eat ing t ransact ion and t he one cr eat ed for y our obj ect ) do not j eopar dize consist ency if one abort s and t he ot her com m it s. You can also use Requires New when y ou want your obj ect t o cont rol t he dur at ion of t he t ransact ion because once t hat obj ect is released, t he t ransact ion ends. 4 .5 .6 Tr a n sa ct ion Su ppor t I D L Ex t e n sion When y ou im por t a COM com ponent int o t he COM Explorer, COM+ select s Not Suppor t ed as t he default configur at ion for y our 105
com ponent ’s t ransact ion suppor t . However , t r ansact ion support is an int r insic part of your COM+ com ponent design. COM+ com ponent s should specify in t he I DL file what t heir r equired t r ansact ion support is, using a dedicat ed I DL ex t ension. When y ou im port a COM+ com ponent t hat uses t he I DL ex t ension int o t he Com ponent Services Explorer, COM+ uses t he declared t ransact ion suppor t fr om t he com ponent ’s t ype librar y as t he init ial v alue. You can ov erride t hat value lat er . For exam ple, if you use t he TRANSACTION_REQUIRED at t r ibut e on your CoClass definit ion: [ uuid(94072015-7D6B-4811-BDB5-08983088D9C2), helpstring("MyComponent Class"), TRANSACTION_REQUIRED ] coclass MyComponent { [default] interface IMyInterface; }; COM+ select s t he Requir ed set t ing for t he com ponent when it is im port ed t o t he Com ponent Serv ices Explorer. The following at t r ibut es are also available: • • •
TRANSACTION_NOT_SUPPORTED TRANSACTION_SUPPORTED TRANSACTION_REQUIRES_NEW
Not e t hat t her e is no TRANSACTION_DISABLED at t r ibut e because t hat at t r ibut e is used m ost ly when im port ing exist ing COM com ponent s t o COM+ . To use t hese I DL ext ensions you have t o include t he m t x at t r .h file in y our I DL file.
ATL 7 Tr a nsa ct ion At t r ibut e I f you ar e using at t r ibut ed ATL 7 proj ect under Visual St udio.NET, you can t ak e adv ant age of precom piler - specific suppor t for COM+ 1.0 ser vices, using special at t r ibut es. I f you add a new class t o y our ATL 7 proj ect and select " ATL COM+ 1.0 Com ponent " fr om t he Add Class dialog, t he w izard will let you configure t r ansact ion support for y our class. Once you select t he t ransact ion suppor t ( for ex am ple, Requir ed) , t he at t r ibut ed class will have a cust om at t ribut e as par t of it s declarat ion: [coclass, //other attributes custom(TLBATTR_TRANS_REQUIRED,0)] class MyComponent : IMyInterface { //class declaration }
106
Befor e com piling your code, ATL 7 feeds y our sour ces t o a special pr ecom piler t hat parses t he at t ribut es and generat es convent ional, nonat t r ibut ed ATL sour ce files, including t he I DL file. The " new" sources are t hen fed t o t he convent ional C+ + com piler . I n t hat process, t he TLBATTR_TRANS_REQUI RED cust om at t ribut e is convert ed t o t he I DL TRANSACTI ON_REQUI RED ext ension.
4 .6 Vot in g on a Tr a n sa ct ion As m ent ioned before, a t ransact ional obj ect v ot es whet her t o com m it or abort t he t r ansact ion by set t ing t he v alue of a bit in t he cont ext obj ect . That bit is called t he consist ency bit . The nam e is appr opr iat e. Consist ency is t he only t ransact ion pr opert y under t he applicat ion’s obj ect s cont rol. COM+ can m anage at om icit y and t he resource m anager s guar ant ee isolat ion and durabilit y, but only t he obj ect s know whet her t he changes t hey m ake t o t he sy st em st at e are consist ent or if t hey encount er errors t hat m er it abort ing t he t r ansact ion. When COM+ cr eat es a t r ansact ional obj ect , it put s it in it s own privat e cont ext and set s t he cont ext obj ect consist ency bit t o TRUE. As a result , if t he obj ect m ak es no explicit at t em pt t o set t he consist ency bit t o FALSE, t he obj ect ’s vot es t o com m it t he t r ansact ion. An obj ect can act ually shar e it s cont ext wit h ot her obj ect s whose t ransact ion set t ing is set t o Disabled. The obj ect can set t he value of t he consist ency bit by accessing t he cont ext obj ect and get t ing hold of IContextState int erface, defined as: enum tagTransactionVote { TxCommit= 0, TxAbort = TxCommit + 1 }TransactionVote; interface IContextState : IUnknown { HRESULT SetDeactivateOnReturn([in] BOOL bDeactivate); HRESULT GetDeactivateOnReturn([out]BOOL* pbDeactivate); HRESULT SetMyTransactionVote ([in]TransactionVote txVote); HRESULT GetMyTransactionVote ([out]TransactionVote* ptxVote); }
107
IContextState is also discussed in Chapt er 3, in t he cont ext of deact iv at ing JI TA obj ect s. IContextState provides t he m et hod SetMyTransactionVote( ) used t o set t he t ransact ion v ot e. You can pass SetMyTransactionVote( ) t he enum v alue TxCommit, if you want t o com m it , or t he enum value TxAbort, if you want t o abor t t he t r ansact ion. SetMyTransactionVote( ) ret ur ns CONTEXT_E_NOTRANSACTION when called by a nont r ansact ional com ponent . Your obj ect should v ot e t o abort t he t ransact ion when it encount er s an er ror t hat m er it s abort ing t he t r ansact ion. I f all went well, your obj ect should vot e to com m it t he t ransact ion. Exam ple 4- 1 shows a t y pical vot ing sequence. The obj ect per form s som e work ( t he DoWork( ) m et hod) and, accor ding t o it s success or failur e, vot es t o com m it or abort t he t r ansact ion. I f y our com ponent decides t o abor t t he t r ansact ion, it should ret ur n an err or code indicat ing t o it s client t hat it abor t ed t he t ransact ion. The client can t hen decide t o ret ry t he t r ansact ional operat ion or handle t he error som e ot her way. This is why t he com ponent in Ex am ple 4- 1 ret ur ns CONTEXT_E_ABORTING from t he m et hod aft er abor t ing t he t r ansact ion. CONTEXT_E_ABORTING is t he st andard ret urned v alue fr om a com ponent t hat abort ed a t r ansact ion. Ex a m p le 4 - 1 . V ot ing on t h e t ra n sact ion ’s ou t com e by acce ssin g t h e I Con t e xt St a t e in t e rfa ce an d callin g Set M yTra n sa ct ion V ot e( )
STDMETHODIMP CMyComponent::MyMethod( { HRESULT hres = S_OK; hres = DoWork( ); //Vote on the transaction outcome
)
IContextState* pContextState = NULL; ::CoGetObjectContext(IID_IContextState,(void**)&pContextS tate); ASSERT(pContextState!= NULL);//Not a configured component if(FAILED(hres)) { hres = pContextState>SetMyTransactionVote(TxAbort); ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support hres = CONTEXT_E_ABORTING; } else {
108
hres = pContextState>SetMyTransactionVote(TxCommit); ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support } pContextState->Release( return hres;
);
} However, what should t he client do if an inner obj ect ( not t he root ) vot es t o abort t he t ransact ion? The root obj ect m ay not know t hat an inner com ponent has abor t ed t he t ransact ion ( and m ay st ill vot e t o com m it and ret ur n S_OK t o t he client ) . I f S_OK is allowed t o ret ur n t o t he client , t hen t he client nev er k nows t hat it s request was abor t ed. To prevent t his sit uat ion, t he int ercept or bet ween t he root obj ect and it s client det ect s t hat t he t ransact ion is alr eady doom ed if an inner obj ect vot es t o abort and t he r oot obj ect vot es t o com m it and t ries t o ret urn S_OK t o t he client ; it r et ur ns CONTEXT_E_ABORTED t o t he client inst ead. One int erest ing point regarding t ransact ion t er m inat ion involv es except ions. Any unhandled except ion in any obj ect in t he t r ansact ion ( not j ust t he r oot ) t er m inat es t he t ransact ion im m ediat ely.
4 .7 Tr a n sa ct ion a l Obj e ct Life Cycle I f a t ransact ion abor t s, t he int erm ediat e and pot ent ially inconsist ent st at e of t he syst em should be rolled back t o ensur e consist ency. The sy st em st at e is t he dat a in t he resour ce m anagers; it also consist s of t he st at e of all t he obj ect s t hat t ook par t in t he t r ansact ion. An obj ect ’s st at e is t he dat a m em ber s it holds. I f t he obj ect part icipat ed in an abort ed t r ansact ion, t hen t hat obj ect ’s st at e should be purged t oo. The obj ect is not allowed t o m aint ain t hat st at e, since it is t he product of act ivit ies t hat wer e rolled back. The problem is t hat once a t r ansact ion ends, even if t he obj ect vot es t o com m it , it does not know whet her t hat t ransact ion will act ually com m it . The DTC st ill has t o collect all t he r esour ce m anager s’ vot es, conduct t he fir st phase of t he t wo- phase com m it prot ocol, and ver ify t hat all of t he resource m anager s vot e t o com m it t he t ransact ion. While t his process t ak es place, t he obj ect m ust not accept any new client calls ( as part of a new t ransact ion) because t he obj ect w ould act on a syst em st at e t hat m ay roll back, which would j eopardize consist ency and isolat ion. To enforce consist ency and isolat ion, once a t ransact ion ends, regardless of it s out com e, COM+ releases all t he obj ect s t hat t ook
109
par t in it . COM+ does not count on obj ect s’ having t he discipline or know ledge t o do t he right t hing. Besides, ev en wit h good int ent ions, how would t he obj ect s know exact ly what part of t heir st at e t o purge? However, ev en t hough t he obj ect s are deact ivat ed and released, COM+ rem em bers t heir posit ion in t he gener al layout of t he t r ansact ion: who t he root was, w ho creat ed whom , point er s bet ween obj ect s, and t he cont ex t , apar t m ent , and pr ocess each obj ect belongs t o. When a new m et hod call from t he client com es int o an obj ect ( usually t o t he root obj ect ) t hat was deact ivat ed at t he end of a t r ansact ion, COM+ cr eat es a new t r ansact ion for t hat m et hod call and a new inst ance of t he obj ect . COM+ t hen for war ds t he call t o t he new inst ance. I f t he obj ect t r ies t o access ot her obj ect s in t he t r ansact ion, COM+ re- creat es t hem as well. I n short , COM+ st ar t s a new t ransact ion wit h new obj ect s in t he sam e t ransact ion lay out , also called a t ransact ion st r eam . The t r ansact ion it self is a t r ansient , short - lived ev ent ; t he lay out can per sist for long periods of t im e. Only w hen t he client explicit ly releases t he root w ill t he obj ect s really be gone and t he t r ansact ion lay out dest royed. 4 .7 .1 St a t e - Aw a r e Obj e ct s Because COM+ dest roy s any obj ect t hat t ook par t in a t ransact ion at t he end of t he t r ansact ion, t r ansact ional obj ect s have t o be st at eawar e, m eaning t hey m anage t heir st at e act iv ely . A st at e- aware obj ect is not t he sam e as a st at eless obj ect . First , as long as a t r ansact ion is in progress, t he obj ect is allowed t o m aint ain st at e in m em ory. Second, t he obj ect is allowed t o m aint ain st at e bet ween t r ansact ions, but t he st at e cannot be st or ed in m em ory or in t he filesy st em . Bet ween t r ansact ions, a t ransact ional obj ect should st or e it s st at e in a resource m anager . When a new t ransact ion st ar t s, t he newly creat ed obj ect should ret rieve it s st at e fr om t he resource m anager . Accessing t he r esour ce m anager causes it t o aut o- enlist w it h t hat t ransact ion. When t he t ransact ion ends, t he obj ect should st ore it s m odified st at e back in t he resource m anager . Now here is why you should go t hough all t his hassle: if t he t r ansact ion abort s, t he resource m anager will roll back all t he changes m ade dur ing t he t r ansact ion—in t his case, t he changes m ade t o t he obj ect st at e. When a new t ransact ion st art s, t he obj ect again ret rieves it s st at e from t he resour ce m anager and has a consist ent st at e. I f t he t ransact ion com m it s, t hen t he obj ect has a newly updat ed consist ent st at e. So t he obj ect does hav e st at e, as long as t he obj ect act ively m anages it . The only problem now is det er m ining when t he obj ect should st or e it s st at e in t he resource m anager . When t he obj ect is cr eat ed and
110
placed in a t r ansact ion, it is because som e ot her obj ect ( it s client ) t r ies t o invok e a m et hod call on t he obj ect . When t he call r et urns, it can be som e t im e unt il t he nex t m et hod call. Bet ween t he t w o m et hod invocat ions, t he r oot obj ect can be released or deact ivat ed, ending t he t ransact ion. COM+ r eleases t he obj ect , and t he obj ect would be gone wit hout ev er st or ing it s st at e back t o t he resour ce m anager . The only solut ion for t he obj ect is t o ret r ieve it s st at e at t he beginning of ever y m et hod call and save it back t o t he resource m anager at t he end of t he m et hod call. From t he obj ect ’s per spect ive, it m ust assum e t hat t he scope of every t ransact ion is t he scope of one m et hod call on it and t hat t he t ransact ion w ould end when t he m et hod ret urns. The obj ect m ust t her efor e also v ot e on t he t ransact ion’s out com e at t he end of every m et hod. Because fr om t he obj ect ’s perspect ive every m et hod call r epresent s a new t r ansact ion, and because t he obj ect m ust ret r ieve it s st at e fr om t he r esource m anager , every m et hod definit ion m ust cont ain som e param et er s t hat allow t he obj ect t o find it s st at e in t he resource m anager . Because m any obj ect s could be of t he sam e t ype accessing t he sam e r esource m anager , t he obj ect m ust hav e som e key t hat ident ifies it s st at e. That key m ust be provided by t he obj ect ’s client . Typical obj ect ident ifiers ar e account num bers and order num bers. For ex am ple, t he client creat es a new t r ansact ional order- processing obj ect , and on ev ery m et hod call t he client m ust provide t he order num ber as a param et er , in addit ion t o ot her par am et ers. Bet ween m et hod calls, COM+ dest r oy s and r e- cr eat es a new inst ance t o serve t he client . The client does not know t he differ ence because t he t wo inst ances hav e t he sam e consist ent st at e. Exam ple 4- 2 shows a generic im plem ent at ion of a m et hod on a t r ansact ional obj ect . A t r ansact ional obj ect m ust ret r iev e it s st at e at t he beginning of every m et hod and save it s st at e at t he end. The obj ect uses an obj ect ident ifier prov ided by t he client t o get and save it s st at e. The m et hod signat ur e cont ains an obj ect ident ifier par am et er used t o get t he st at e from a resource m anager wit h t he GetState( ) helper m et hod. The obj ect t hen per form s it s work using t he DoWork( ) helper m et hod. Then t he obj ect saves it s st at e back t o t he resource m anager using t he SaveState( ) m et hod, specifying it s ident ifier . Finally, t he obj ect v ot es on t he t r ansact ion out com e based of t he success of t he DoWork( ) m et hod. Ex a m p le 4 - 2 . I m plem e n t in g a m e t h od on a t r an sa ct ion al ob j e ct
STDMETHODIMP CMyComponent::MyMethod(PARAM objectIdentifier) { HRESULT hres = S_OK; 111
GetState(objectIdentifier); hres = DoWork( ); SaveState(objectIdentifier); //Vote on the transaction outcome IContextState* pContextState = NULL; ::CoGetObjectContext(IID_IContextState,(void**)&pContextS tate); ASSERT(pContextState!= NULL);//Not a configured component if(FAILED(hres)) { hres = pContextState>SetMyTransactionVote(TxAbort); ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support hres = CONTEXT_E_ABORTING; } else { hres = pContextState>SetMyTransactionVote(TxCommit); ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support } pContextState->Release( return hres;
);
} Not e t hat not all of t he obj ect ’s st at e can be saved by value t o t he resource m anager . I f t he st at e cont ains point ers t o ot her COM+ obj ect s, GetState( ) should creat e t hose obj ect s and SaveState( ) should release t hem . Sim ilarly, if t he st at e cont ains such resources as dat abase connect ion, GetState( ) should acquire a new connect ion and SaveState( ) should release t he connect ion. 4 .7 .2 Tr a n sa ct ions a n d JI TA I f t he obj ect goes t hrough t he t r ouble of r et rieving it s st at e and saving it on every m et hod call, why w ait unt il t he end of t he t r ansact ion t o dest roy t he obj ect ? The t ransact ional obj ect should be able t o signal t o COM+ t hat it can be deact ivat ed at t he end of t he m et hod call, even t hough t he t r ansact ion m ay not be over yet . I f t he obj ect is deact iv at ed bet ween m et hod calls, COM+ should r ecr eat e t he obj ect when a new m et hod call from t he client com es in. The behavioral requirem ent s for a st at e- aware t ransact ional obj ect and t he r equir em ent s of a well- behav ed JI TA obj ect are t he sam e. As discussed in Chapt er 3, a well- behaved JI TA obj ect should
112
deact iv at e it self at m et hod boundar ies, as well as ret riev e and st ore it s st at e on every m et hod call. Since COM+ already has an efficient m echanism for cont rolling obj ect act ivat ion and deact ivat ion ( JI TA) , it m akes per fect sense t o use JI TA t o m anage dest roying t he t r ansact ional obj ect and reconnect ing it t o t he client , as explained in Chapt er 3. Every COM+ t ransact ional com ponent is also a JI TA com ponent . When y ou configur e y our com ponent t o requir e a t ransact ion ( including Support ed) , COM+ configures t he com ponent t o requir e JI TA as well. You cannot configure your com ponent t o not r equire JI TA because COM+ disables t he JI TA checkbox. At t he end of a m et hod call, like any ot her JI TA obj ect , y our t r ansact ional obj ect can call IContextState::SetDeactivateOnReturn( ) t o set t he value of t he done bit in t he cont ext obj ect t o TRUE, signaling t o COM+ t o deact iv at e it , as shown in Exam ple 4- 3. Ex a m p le 4 - 3 . A t r an sact ion a l ob j ect d e act iva t in g it se lf a t t h e e nd of t h e m e t h od
STDMETHODIMP CMyComponent::MyMethod(PARAM objectIdentifier) { HRESULT hres = S_OK; GetState(objectIdentifier); hres = DoWork( ); SaveState(objectIdentifier); IContextState* pContextState = NULL; ::CoGetObjectContext(IID_IContextState,(void**)&pContextS tate); ASSERT(pContextState!= NULL);//Not a configured component if(FAILED(hres)) { hres = pContextState>SetMyTransactionVote(TxAbort); ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support hres = CONTEXT_E_ABORTING; } else { hres = pContextState>SetMyTransactionVote(TxCommit); ASSERT(hres != CONTEXT_E_NOTRANSACTION);//No transaction support
113
} hres = pContextState->SetDeactivateOnReturn(TRUE); pContextState->Release( ); return hres; } The done bit is set t o FALSE by default . I f y ou never set it t o TRUE, your obj ect is dest royed only at t he end of t he t r ansact ion or when it s client releases it . I f t he obj ect is t he root of a t ransact ion, selfdeact iv at ion signals t o COM+ t he end of t he t ransact ion, j ust as if t he client r eleased t he root obj ect . Of cour se, by com bining t r ansact ions wit h JI TA you gain all t he benefit s of JI TA: im proved applicat ion scalabilit y, t hroughput , and reliabilit y. 4 .7 .3 Colle ct in g Obj e ct s’ V ot e s Using JI TA has a side effect on y our obj ect ’s t r ansact ion vot e. When t he obj ect is deact iv at ed, t he t ransact ion could end while t he obj ect is not ar ound t o v ot e. Thus, t he obj ect m ust vot e before deact iv at ing it self. When a m et hod call ret urns, COM+ check s t he value of t he done bit . I f it is TRUE, COM+ checks t he value of t he consist ency bit , t he obj ect ’s v ot e. COM+ collect s t he obj ect s’ vot es during t he t r ansact ion. Each t r ansact ion has a doom ed flag, which if set t o TRUE doom s a t r ansact ion t o abort . COM+ set s t he v alue of a new t ransact ion’s doom ed flag t o FALSE. When an obj ect is deact iv at ed and it s vot e was t o com m it , COM+ does not change t he curr ent value of t he doom ed flag. Only if t he vot e was t o abor t will COM+ change t he doom ed flag t o TRUE. As a result , once set t o TRUE, t he doom ed flag value will never be FALSE again, and t he t r ansact ion is t ruly doom ed. When t he root obj ect is deact ivat ed/ released, COM+ st ar t s t he t wophase com m it pr ot ocol only if t he doom ed flag is set t o FALSE. Not e t hat COM+ does not wast e t im e at t he end of a t ransact ion polling obj ect s for t heir v ot e. COM+ already k nows t heir v ot e via t he doom ed flag. 4 .7 .4 Th e I Obj e ct Con t e x t I n t e r f a ce The cont ext obj ect suppor t s a legacy MTS int erface, called IObjectContext, defined as: interface IObjectContext : IUnknown { HRESULT CreateInstance([in]GUID* rclsid,[in] GUID* riid,[out,retval]void** ppv); HRESULT SetComplete( ); HRESULT SetAbort( ); HRESULT EnableCommit( ); HRESULT DisableCommit( );
114
BOOL IsInTransaction( ); BOOL IsSecurityEnabled( ); HRESULT IsCallerInRole([in]BSTR bstrRole,[out,retval]BOOL* pfIsInRole); }; IObjectContext is wort h m ent ioning only because m ost of t he COM+ docum ent at ion and exam ples st ill use it inst ead of t he new COM+ int er face, IContextState. IObjectContext has t wo m et hods used t o vot e on a t ransact ion out com e and t o cont r ol obj ect deact ivat ion. Calling SetComplete( ) set s t he consist ency and done bit s t o TRUE. SetComplete( ) set s t he vot e t o com m it and get s t he obj ect deact iv at ed once t he m et hod r et ur ns. SetAbort( ) set s t he vot e t o abor t t he t ransact ion and set s t he done bit t o TRUE, causing t he obj ect t o deact ivat e when t he m et hod r et ur ns. COM+ obj ect s should av oid using IObjectContext and should use IContextState inst ead. I ContextState is fine- t uned for COM+ because it set s one bit at a t im e. I t also verifies t he pr esence of a t ransact ion—it r et ur ns an err or if t he obj ect is not part of a t r ansact ion. COM+ obj ect s writ t en in VB 6.0 hav e no way of accessing IContextState dir ect ly. They have t o go t hrough IObjectContext first and quer y it for IContextState, as shown in Exam ple 4- 4. Obj ect s writ t en in Visual Basic.NET can access IContextState dir ect ly. Ex a m p le 4 - 4 . Qu e ryin g I Obj e ct Con t ex t for I Con t ex t St a t e
Dim objectContext As ObjectContext Dim contextState As IContextState Set objectContext = GetObjectContext ’QueryInterface for IContextState: Set contextState = objectContext contextState.SetMyTransactionVote TxCommit 4 .7 .5 M e t h od Aut o- D e a ct iva t ion As shown in Chapt er 3, y ou can configure any m et hod on a JI TA obj ect t o aut om at ically deact iv at e t he obj ect w hen it r et urns. Configuring t he m et hod t o use aut o- deact ivat ion changes t he done bit from it s default v alue of FALSE t o TRUE. Because t he default value for t he consist ency bit is TRUE, unless y ou change t he cont ex t obj ect bit s pr ogr am m at ically, aut o- deact ivat ion aut om at ically result s in a vot e t o com m it t he t ransact ion. However, COM+ exam ines t he HRESULT t hat t he m et hod ret urns. I f t he HRESULT indicat es failure, t hen t he int er cept or set s t he
115
consist ency bit t o FALSE, as if you v ot ed t o abor t . This behavior giv es you a new program m ing m odel for vot ing and deact ivat ing your obj ect : if you select aut o- deact ivat ion for a m et hod, don’t t ake any effort t o set any cont ex t obj ect bit s. I nst ead, use t he m et hod’s ret ur ned HRESULT: • •
I f it is S_OK, it is as t hough you vot ed t o com m it . ( S_FALSE would also vot e t o com m it .) I f it indicat es failur e, it is as t hough you vot ed t o abor t .
When y ou use aut o- deact iv at ion, t he program m ing m odel becom es m uch m or e elegant and concise, and shown in Ex am ple 4- 5. Wit h aut o- deact iv at ion, t he obj ect does not hav e t o explicit ly v ot e on t he t r ansact ion’s out com e or deact ivat e it self. Com pare t his wit h Exam ple 4- 3. Bot h have t he sam e effect , but not e how elegant , readable, and concise Exam ple 4- 5 is. Ex a m p le 4 - 5 . Usin g m e t h od a u t o- d ea ct iva t ion
STDMETHODIMP CMyComponent::MyMethod(PARAM objectIdentifier) { HRESULT hres = S_OK; GetState(objectIdentifier); hres = DoWork( ); SaveState(objectIdentifier); return hres; } Addit ionally, t he obj ect ’s client should ex am ine t he r et urned HRESULT. I f it indicat es failure, t hen it also indicat es t hat t he obj ect vot ed t o abor t t he t r ansact ion; t he client should not wast e any m ore t im e on t he t r ansact ion because it is doom ed. 4 .7 .6 Obj e ct Life Cycle Ex a m ple The following sim ple ex am ple dem onst rat es t he im port ant concept s discussed in t his sect ion. Suppose a nont r ansact ional client cr eat es Obj ect A, configured wit h t r ansact ion support set t o Required. Obj ect A creat es Obj ect B, which also requires a t r ansact ion. The dev eloper s of Obj ect A and Obj ect B wr ot e t he code so t hat t he obj ect s vot e and get t hem selves deact iv at ed on m et hod boundar ies. The client calls t wo m et hods on Obj ect A and releases it . Obj ect A t hen releases Obj ect B. When t he client cr eat es Obj ect A, COM+ not es t hat t he client does not hav e a t ransact ion and t hat Obj ect A needs t ransact ion suppor t , so COM+ creat es a new t r ansact ion for it , m aking Obj ect A t he root of t hat t r ansact ion. Obj ect A t hen goes on t o creat e Obj ect B, and Obj ect B shar es Obj ect A’s t ransact ion. Not e t hat Obj ect B is in a
116
separ at e cont ext because t ransact ional obj ect s cannot shar e a cont ext . Now t he t r ansact ion layout is est ablished. The t ransact ion lay out per sist s unt il t he client releases Obj ect A, t he r oot of t his t r ansact ion. Not e t hat bot h t he client and t he obj ect s hav e references t o cross- cont ext int ercept or s, not t o act ual obj ect s. While a call from t he client is in pr ogr ess, bot h obj ect s exist ( see Figure 4- 9) and t he t ransact ion lay out host s an act ual t ransact ion. Figu re 4 - 9 . Tr a n sa ct ion layou t w h ile a t ra n sact ion is in p rogr ess
However, bet ween t he t wo m et hod calls from t he client , only t he t r ansact ion layout is m aint ained; no obj ect s or a t r ansact ion ar e in progress, only int ercept ors and cont ex t s ( see Figure 4- 10) . When t he second call com es in, COM+ cr eat es Obj ect A, and Obj ect A ret rieves it s st at e fr om t he r esource m anager . When Obj ect A accesses Obj ect B t o help it pr ocess t he client request , COM+ cr eat es Obj ect B and hook s it up wit h t he int ercept or Obj ect A is using ( see Figure 4- 9) . When t he call com es t o Obj ect B, it t oo ret rieves it s st at e fr om t he r esource m anager . When t he m et hod ret ur ns from Obj ect B, Obj ect B deact iv at es it self; when t he m et hod ret ur ns t o t he client , Obj ect A deact iv at es it self. When t he client releases it s r eference t o Obj ect A, t he t r ansact ion layout is dest royed, along wit h t he cont ext s and t he int ercept ors. Figu re 4 - 1 0 . Tr an sact ion la you t b e t w e e n m e t h od ca lls
117
4 .8 D esign in g Tr a n sa ct ion a l Com pon en t s I ncorpor at ing corr ect t r ansact ion support in y our com ponent is an int egr al par t of y our design and cannot be done as an aft ert hought . Suppor t ing t ransact ions is far fr om sim ply select ing t he corr ect radio but t on in t he Com ponent Serv ices Ex plorer . I n part icular , y our obj ect has t o be st at e- aware, act ively m anage it s st at e, and cont r ol it s own act iv at ion and deact ivat ion, as described in previous sect ions. You should also design your int erfaces t o suppor t t r ansact ions and t o acquir e r esour ces in a par t icular or der . 4 .8 .1 D e signin g Tr a nsa ct ion a l I nt e r fa ce s I nt er face design is an im por t ant fact or in designing t ransact ional com ponent s. Fr om t he obj ect ’s per spect iv e, m et hod calls dem arcat e t r ansact ions, so you should avoid coupling int erface m et hods t o each ot her. Each m et hod should cont ain enough param et er s for t he obj ect t o per for m it s w ork and decide whet her t he t ransact ion should com m it or abort . I n t heory , you could build a t ransact ional com ponent t hat vot es on t he t ransact ion out com e only aft er receiving a few m et hod calls. However , in pr act ice, a t ransact ion should not span m ult iple m et hod calls. You already saw t hat a t r ansact ional obj ect uses JI TA and should deact ivat e it self at m et hod boundar ies. COM+ check s t he obj ect ’s v ot e once it is deact iv at ed. I f t he int er face t he obj ect im plem ent s requir es m or e t han one m et hod call for t he obj ect t o decide on it s v ot e, t hen t he obj ect could not deact ivat e it self; it m ust wait for anot her call fr om t he client . What should t he obj ect do if t he t ransact ion suddenly ends ( because t he root was deact ivat ed or t he t r ansact ion t im ed out ) and t he ant icipat ed call fr om t he client never com es? Wait ing for addit ional inform at ion from t he client has a serious effect on ov er all applicat ion t hroughput . While y our t r ansact ion is in progress, t he r esource m anagers involved lock out all ot her
118
t r ansact ions from t he dat a being m odified by your t ransact ion. The ot her t r ansact ions are blocked unt il y our t r ansact ion com m it s or abor t s. The longer you wait for client calls t hat m ay never com e, t he m or e your applicat ion’s t hroughput will suffer. Consider, for exam ple, a poorly designed int erface used t o handle cust om er orders: [ helpstring("Bad design of IOrder interface"), ] interface IOrder : IUnknown { HRESULT SetOrder([in]DWORD dwOrderNumber); HRESULT SetItem([in]DWORD dwItemNumber); HRESULT SetCustomerAccount([in]DWORD dwCustomerAccount); HRESULT ProcessOrder( ); }; The int erface designer int ends for t he client t o call t he Set( ) m et hods, supplying t he obj ect wit h t he or der param et ers, and t hen call ProcessOrder( ). The pr oblem wit h t his design is t hat t he t r ansact ional obj ect cannot vot e on t he t r ansact ion out com e unless t he client calls all t he Set( ) m et hods and t hen t he ProcessOrder( ) m et hod, in t hat sequence. There is no clear delineat ion of t r ansact ion boundaries in t his int erface design. The correct way t o design t he int erface while m aint aining t r ansact ion sem ant ics is: [ helpstring("Correct design of IOrder interface"), ] interface IOrder : IUnknown { HRESULT ProcessOrder([in]DWORD dwOrderNumber,[in]DWORD dwItemNumber, [in]DWORD dwCustomerAccount); }; This int erface is also a lot easier t o im plem ent . The order num ber is used t o ident ify t he obj ect and allow it t o ret r ieve it s cor responding st at e fr om t he r esource m anager—in t his case, t he orders dat abase: STDMETHODIMP COrder::ProcessOrder(DWORD dwOrderNumber,DWORD wItemNumber, DWORD dwCustomerAccount) { HRESULT hres = S_OK; GetState(dwOrderNumber);//retrieve the state of the corresponding //order object hres = DoProcessOrder(wItemNumber,dwCustomerAccount); SaveState(dwOrderNumber);
119
// Using auto-deactivation. No need to vote explicitly. return hres; } The second int erface design yields bet t er perform ance as well, because t her e are fewer calls t o t he obj ect fr om t he client m achine, which m ay be across t he net wor k. 4 .8 .2 Acqu ir ing Re sour ce s Suppose you have t wo t ransact ions, T1 and T2, t hat ex ecut e in par allel, and bot h r equire access t o t wo resour ce m anagers, RM1 and RM2. Suppose T1 has acquired RM1, and T2 has acquir ed RM2. What would happen if T1 t ries t o access RM2, and T2 t r ies t o access RM1? You would have a deadlock. Neit her t r ansact ion is able t o proceed. Each needs a r esource t he ot her holds t o com plet e it s work . Each is block ed and never frees t he resource m anager it holds. The solut ion t o t his deadly em br ace is t o be m indful about t he or der in which obj ect s in y our t r ansact ion t ry t o acquir e resour ces. You can av oid t he deadlock by always t ry ing t o acquir e t he r esour ces in t he sam e order. I n t he previous ex am ple, if bot h t r ansact ions t ry t o acquire RM1 and t hen RM2, t hen t he first one t o act ually acquire RM1 will cont inue on t o acquire RM2; t he second t r ansact ion will be blocked, as it wait s for RM1 t o be r eleased.
4 .9 N on t r a n sa ct iona l Clie n t s Consider t he sit uat ion in which a nont r ansact ional client creat es a few t r ansact ional obj ect s, all configured t o require t ransact ions. The client would lik e t o scope all it s int eract ions wit h t he obj ect s it cr eat es under one tr ansact ion—in essence, t o funct ion like t he root of t hat t r ansact ion. The problem is t hat t he client is not configured t o r equire t r ansact ions ( m aybe it is a legacy com ponent or m ay be it is not ev en a com ponent , such as a for m or a script client ) , so it cannot have a t r ansact ion t o include t he obj ect s it creat es. On t he ot her hand, t he obj ect s requir e t ransact ion suppor t t o operat e proper ly, so for ever y obj ect t he client cr eat es, COM+ creat es a t r ansact ion. As a result , even if t he client int ended t o com bine t he work of m ult iple COM+ obj ect s int o a single t ransact ion, t he net result would be m ult iple t r ansact ions ( see Figur e 4- 11) . The r eal problem now is t hat each t ransact ion can com m it or abort independent ly. The operat ions t he client per form s on t he syst em ( using t he obj ect s) are no longer at om ic, so t he client j eopardizes sy st em consist ency. Furt her m or e, ev en if all obj ect s were under one 120
t r ansact ion, how would t he client v ot e t o com m it or abort t hat t r ansact ion? Figu r e 4 - 1 1 . A n on t ra n sact ion a l clie n t e n ds u p w it h m u lt iple t r an sa ct ion s in st e a d of on e
Ther e is an elegant and sim ple solut ion t o t his predicam ent . The solut ion is t o int roduce a m iddlem an—a t r ansact ional com ponent t hat cr eat es t he obj ect s on behalf of t he client . The m iddlem an cr eat es t he obj ect s and ret ur ns int er face point er s back t o t he client . The m iddlem an obj ect s also should pr ov ide t he client w it h abilit y t o com m it or abort t he t r ansact ion. These m iddlem an requirem ent s ar e gener ic. Therefore, COM+ provides a r eadym ade m iddlem an called t he t ransact ion cont ex t com ponent . As part of t he COM+ Ut ilit ies applicat ion, COM+ provides t w o com ponent s ( one for VB 6.0 and one for C+ + ) , each suppor t ing a slight ly different int erface. A VB 6.0 client should use t he ITransactionContext int erface, cr eat able via t he prog- I D TxCtx.TransactionContext ( or t he class nam e TransactionContext) . A C+ + client should use t he ITransactionContextEx int er face, which is creat able via t he class I D CLSID_TransactionContextEx. The t wo int erfaces ar e defined as: interface ITransactionContext : IDispatch { HRESULT CreateInstance([in]BSTR pszProgId,[out, retval]VARIANT* pObject); HRESULT Commit( ); HRESULT Abort( ); }; interface ITransactionContextEx : IUnknown { HRESULT CreateInstance([in]GUID* rclsid,[in]IID* riid, [out,retval]void** pObject); HRESULT Commit( );
121
HRESULT Abort( ); }; These int er faces allow t he client t o cr eat e new com ponent inst ances and com m it or abort t he t r ansact ion. The t wo t r ansact ion cont ex t com ponent s ar e configur ed t o require a new t ransact ion, so t hey are alw ays t he r oot of t hat t ransact ion. This configurat ion also prevent s y ou from m isusing t he t ransact ion cont ext obj ects by enlist ing t hem in an exist ing t r ansact ion. All obj ect s creat ed by t he t r ansact ion cont ex t obj ect share t he sam e t r ansact ion ( see Figur e 4- 12) . Figu r e 4 - 1 2 . Usin g a m idd le m a n , a n on t r an sact ion a l clie n t e n ds u p w it h on e t r a n sa ct ion
All t he client has t o do is creat e t he t ransact ion cont ext obj ect , and t hen use it t o creat e t he ot her obj ect s v ia t he CreateInstance( ) m et hod. I f t he client w ant s t o com m it t he t ransact ion, it m ust explicit ly call t he Commit( ) m et hod. Once t he client calls t he Commit( ) m et hod, t he t ransact ion ends on r et urn fr om t he Commit( ) m et hod. I f one of t he int ernal obj ect s v ot es t o abort t he t r ansact ion before t he client calls Commit( ), t he client ’s call t o Commit( ) ret urns wit h t he err or code of CONTEXT_E_ABORT, indicat ing t hat t he t ransact ion was already abor t ed. The client can chose t o st art a new t r ansact ion or handle t he err or in som e ot her m anner. I f t he client does not call Commit( ), t he t ransact ion is abor t ed, even if all t he part icipat ing obj ect s vot ed t o com m it . This abort ion is int ent ional, t o force t he client t o voice it s opinion on t he work done by t he obj ect s it cr eat ed. Only t he client knows whet her t heir com bined work w as consist ent and legit im at e. Appar ent ly , when t he client cr eat es t he t r ansact ion cont ex t obj ect , t he t r ansact ion cont ex t obj ect set s t he consist ency bit t o FALSE and never deact iv at es it self. As a result , t he t ransact ion is doom ed unless t he client calls
122
Commit( ), which causes t he t ransact ion cont ext obj ect t o change t he bit back t o TRUE and deact ivat e it self, t hus ending t he t r ansact ion. The client can abor t t he com bined t ransact ion by calling t he Abort( ) m et hod. The t ransact ion ends on ret ur n from t he Abort( ) m et hod. The client is also responsible for r eleasing t he references it has on t he int er nal obj ect s cr eat ed by t he t r ansact ion cont ex t obj ect . I t is a good pr act ice t o do so even t hough t hese obj ect s ar e released when t he t ransact ion ends. Exam ple 4- 6 shows how t o use t he t r ansact ion cont ex t obj ect . I n t he ex am ple, t he client creat es t he t ransact ion cont ext obj ect and t hen uses it t o creat e t hree t ransact ional obj ect s ( as in Figur e 412) . The client v ot es t o com m it or abort t he t ransact ion, based on t he com bined success of t he m et hod inv ocat ions on t he t hr ee obj ect s. Ex a m p le 4 - 6 . Usin g t h e t r an sa ct ion con t ex t obj e ct t o cr e at e t h r ee t r an sa ct ion al ob j e ct s
HRESULT hres1 = S_OK; HRESULT hres2 = S_OK; HRESULT hres3 = S_OK; IMyInterface* pObj1= NULL; IMyInterface* pObj2= NULL; IMyInterface* pObj3= NULL; ITransactionContextEx* pTransContext = NULL; ::CoCreateInstance(CLSID_TransactionContextEx,NULL,CLSCTX _ALL, IID_ITransactionContextEx,(void**)&pTransContext); pTransContext>CreateInstance(CLSID_MyComponent,IID_IMyInterface,(void* *)&pObj1); pTransContext>CreateInstance(CLSID_MyComponent,IID_IMyInterface,(void* *)&pObj2); pTransContext>CreateInstance(CLSID_MyComponent,IID_IMyInterface,(void* *)&pObj3); hres1 = pObj1->MyMethod( hres2 = pObj2->MyMethod( hres3 = pObj3->MyMethod(
); ); );
if(S_OK == hres1 && S_OK == hres2 && S_OK == hres3) pTransContext->Commit( ); else 123
pTransContext->Abort( pObj1->Release( ); pObj2->Release( ); pObj3->Release( ); pTransContext->Release(
);
);
4 .1 0 Tr a n sa ct ion s a nd Obj ect Poolin g As discussed in Chapt er 3, t o speed up perfor m ance, y our pooled obj ect acquires expensive r esources, such as dat abase connect ions, at cr eat ion t im e and holds ont o t hem w hile pooled. The problem is t hat one of t he r equir em ent s for resource m anagers is aut oenlist m ent in t ransact ions. When an obj ect creat es a resource such as a dat abase connect ion, t he connect ion ( act ually t he r esource m anager ) aut o- enlist s w it h t he obj ect ’s t ransact ion. A pooled obj ect only creat es t he resour ces once, and t hen t he obj ect is called out of t he pool t o serve client s. Every t im e t he obj ect is ret r ieved fr om t he pool, it could pot ent ially be part of a differ ent t r ansact ion. I f t he pooled obj ect is for ced t o re- creat e t he expensive resour ces it holds t o allow t hem t o aut o- enlist , t hat would negat e t he whole point of using obj ect pooling. Unfort unat ely, t he only w ay t o com bine t r ansact ions wit h a pooled obj ect t hat holds references t o resour ce m anagers is t o giv e up on aut o- enlist m ent . The pooled obj ect has t o m anually enlist t he resources it holds in t he t r ansact ions it par t icipat es wit h. The pooled obj ect m ust follow t hese st eps: 1. The obj ect m ust im plem ent t he IObjectControl int erface. The obj ect needs t o m anually enlist t he resour ce m anagers it holds w hen it is placed in an act ivat ion cont ext in it s im plem ent at ion of IObjectControl::Activate( ). The obj ect also needs t o per for m operat ions in IObjectControl::Deactivate( ) and IObjectControl::CanBePooled( ), explained lat er . 2. Aft er creat ing t he connect ion t o t he resource m anager , t he pooled obj ect t urns off t he r esource m anager’s aut oenlist m ent . This st ep r equires program m ing against t he resource m anager API . All resource m anager s suppor t t his funct ionalit y, alt hough in slight ly differ ent way s and sy nt ax. 3. When t he obj ect is called out of t he pool t o ser ve a client and is placed in a COM+ cont ex t , it m ust det ect whet her a t r ansact ion is in progress. This det ect ion is done eit her by calling IObjectContextInfo::IsInTransaction( ) or calling IObjectContextInfo::GetTransactionId( ). I f t he cont ext t he obj ect is placed in is not part of a t r ansact ion, t he
124
ret ur ned t ransact ion I D is GUID_NULL. I f a t r ansact ion is in progress, t he obj ect m ust m anually enlist any resource m anager it holds. Enlist ing m anually is done in a resour cespecific m anner. For exam ple, in ODBC, t he obj ect should call SQLSetConnectAttr( ) w it h t he SQL_COPT_SS_ENLIST_IN_DTC at t r ibut e. Not e t hat IObjectControl::Activate( ) is called befor e t he act ual call fr om t he client is allowed t o access t he obj ect . The client call is execut ed against an obj ect wit h enlist ed r esource m anager s. 4. The obj ect m ust reflect t he curr ent st at e of it s resources and indicat e in IObjectControl::CanBePooled( ) when it can’t be reused ( if a connect ion is bad) . Ret urning FALSE from CanBePooled( ) doom s a t ransact ion. Clearly , m ixing r esour ce m anagers wit h pooled obj ect s is not for t he faint of hear t . Besides labor ious program m ing, m anually enlist ing all resources t he obj ect holds ev ery t im e t he obj ect is called fr om t he pool im plies a needless per form ance penalt y if t he obj ect is called t o serve a client in t he sam e t r ansact ion as t he pr ev ious act ivat ion. COM+ is aware of t he per form ance penalt y and it provides a sim ple solut ion. As y ou saw in Chapt er 3, COM+ m aint ains a pool per com ponent t ype. However, if a com ponent is configured t o use obj ect pooling and requir e a t ransact ion, COM+ m aint ains t r ansact ion- specific pools for obj ect s of t hat t y pe. COM+ act ually opt im izes obj ect pooling: when t he client r equest ing an obj ect has a t ransact ion associat ed wit h it , COM+ scans t he pool for an available obj ect t hat is already associat ed wit h t hat t r ansact ion. I f an obj ect wit h t he right t r ansact ion affinit y is found, it is ret ur ned t o t he client . Ot herwise, an obj ect fr om t he general pool is ret urned. I n essence, t his sit uat ion is equiv alent t o m aint aining special subpools cont aining obj ect s wit h affinit y for a par t icular t r ansact ion in progress. Once t he t ransact ion ends, t he obj ect s from t hat t r ansact ion’s pool ar e r et urned t o t he gener al pool wit h no t ransact ion affinit y , r eady t o ser ve any client . Wit h t his feat ur e, a t ransact ional- pooled obj ect can r eliev e t he per form ance penalt y. Befor e m anually enlist ing it s resources in a t r ansact ion, it should first check t o see whet her it has alr eady enlist ed t hem in t hat t r ansact ion. I f so, t here is no need t o enlist t hem again. Your obj ect can achiev e t hat by keeping t r ack of t he last t r ansact ion I D and com par ing it t o t he curr ent t r ansact ion I D using IObjectContextInfo::GetTransactionId( ). Exam ple 4- 7 shows a pooled obj ect t hat t akes adv ant age of COM+ subpooling. I n t he obj ect ’s im plem ent at ion of IObjectControl::Activate( ), it get s t he cur rent t ransact ion I D.
125
The obj ect verifies t hat a t ransact ion is in progress ( t he t ransact ion I D is not GUID_NULL) and t hat t he curr ent t ransact ion I D is different fr om t he t r ansact ion I D saved dur ing t he previous act ivat ion. I f t his t r ansact ion is new, t hen t he obj ect enlist s a r esour ce m anager it holds m anually. To m anually enlist t he resource, t he obj ect passes t he curr ent t r ansact ion obj ect ( in t he form of ITransaction*) t o t he privat e helper m et hod EnlistResource( ). The obj ect saves t he curr ent t ransact ion I D in it s im plem ent at ion of IObjectControl::Deactivate( ). The obj ect uses t he privat e helper m et hod IsResourceOK( ) in IObjectControl::CanBePooled( ) t o v er ify t hat it ret urns t o t he pool only if t he r esource m anager is in a consist ent st at e. Ex a m p le 4 - 7 . A t r an sact ion a l pooled obj e ct m an u a lly e n list in g a r e sou rce m a n a g er it h olds b e t w e e n act iva t ion s
HRESULT CMyPooledObj::Activate( ) { HRESULT hres = S_OK; GUID guidCurrentTras = GUID_NULL; hres = ::CoGetObjectContext(IID_IObjectContextInfo, (void**)&m_pObjectContextInfo); hres = m_pObjectContextInfo>GetTransactionId(&guidCurrentTras); if(guidCurrentTras!= GUID_NULL && guidCurrentTras != m_guidLastTrans) { ITransaction* pTransaction = NULL; hres = m_pObjectContextInfo>GetTransaction(&pTransaction); hres = EnlistResource(pTransaction);//Helper Method } return hres; } void CMyPooledObj::Deactivate( ) { //Save the current transaction ID m_pObjectContextInfo>GetTransactionId(&m_guidLastTrans); //if no transaction, m_guidLastTrans will be GUID_NULL m_pObjectContextInfo->Release( ); } BOOL CMyPooledObj::CanBePooled( ) { return IsResourceOK( );//Helper Method }
126
Not e t hat t hough t he obj ect is a t r ansact ional obj ect , it m aint ains st at e across t ransact ions and act iv at ions. This m aint enance is possible because t he obj ect is not r eally dest royed ( only ret urned t o t he pool) and it s int er nal st at e does not j eopardize syst em consist ency . COM+ does subpooling regardless of w het her your t ransact ionalpooled obj ect m anages it s own resource m anager s. I f your t r ansact ional- pooled obj ect does not m anually enlist r esource m anager s, t hen you can j ust ignore t he subpooling.
4 .1 1 Com pe n sa t in g Tr a nsa ct ions Som e business operat ions have a logical undo. Consider t he way bank s handle bad checks. When y ou deposit a bad check at t he ATM, t he bank adds t he am ount of t he check t o y our account . When t he bank discover s the check is bad, it undoes t he deposit by deduct ing an ident ical am ount from your account and r et urns t he check t o y ou in t he m ail. This logical undo is called a com pensat ing t r ansact ion. Not ev er y t ransact ion has a com pensat ing t ransact ion, but if it does, y ou should use caut ion w hen incor por at ing com pensat ing t r ansact ions int o your applicat ion. I t is ver y t ricky t o use com pensat ing t r ansact ions w it hout j eopardizing sy st em consist ency . For exam ple, im agine t hat aft er deposit ing t he check, you apply for a loan. The bank’s loan consult ant check s your balance and decides t o grant y ou t he loan based on t he new increased balance. Once t he bank execut es t he com pensat ing t r ansact ion, t he syst em is in an inconsist ent st at e—t he account balance is corr ect , but a loan program is in progress—one t hat should not hav e been launched based on t he cor rect ed balance. The bank could, of course, per form a com pensat ing t r ansact ion for t he loan applicat ion, ex cept t hat in t he m eant im e you m ight have used t hat loan t o st ar t a new business, and so on. As you can see, once t he cat is out of t he bag, it is difficult t o com pensat e in a com pr ehensiv e and consist ent m anner . I f com pensat ing t r ansact ions ar e bad, why bot her wit h t hem at all? Com pensat ing t ransact ions are necessary because t hey enable you t o deal efficient ly wit h t r ansact ions whose nor m al execut ion t im e is unaccept able. Even t hough t he bad check m ay bounce aft er t wo day s, t he bank does not expect a cust om er t o w ait at t he ATM for t wo day s unt il t he check is clear ed. Addit ionally , it is unrealist ic t o keep a lock on t he cust om er 's account for t wo days because no ot her oper at ion on t he account can t ak e place unt il t he deposit ing t r ansact ion is done. The bank has t o t ak e t he chance and use a com pensat ing t r ansact ion as a safet y net . The bank , in t his case, t r ades t ransact ion t hroughput for a sm all, calculat ed r isk in syst em consist ency .
127
I n gener al, com pensat ing t ransact ions are useful when t he t r ansact ion for which t hey com pensat e is pot ent ially long. Com pensat ing t ransact ions offer a high t hroughput alt ernat ive, allowing you t o m aint ain locks in t he resource m anager s for a m inim um am ount of t im e.
4 .1 2 Tr a n sa ct ion Ex e cu t ion Tim e Transact ion execut ion t im e should be m inim al. The reason is obv ious: a t r ansact ion occupies expensiv e resour ces. As long as t he t r ansact ion ex ecut es, no ot her t ransact ion can access t hose resources. Every resour ce m anager t he t ransact ion accesses has t o lock r elevant dat a, isolat ing t hat t r ansact ion from t he rest of t he world. As long as t he lock s are held, nobody else can access t he dat a. The m or e t ransact ions per second your applicat ion can process, t he bet t er it s scalabilit y and t hr oughput . Transact ion execut ion usually requires, at m ost , a few seconds. For lengt hy operat ions, consider using a shor t t ransact ion backed up by a com pensat ing t r ansact ion. COM+ allows you t o configure a m ax im um execut ion t im e for your t r ansact ions. I f your t ransact ion r eaches t hat t im eout , COM+ abort s it aut om at ically. Transact ion t im eout s pr ev ent resour ce m anager deadlock s from hanging t he sy st em . Event ually, one of t he t wo t r ansact ions deadlocking each ot her w ould reach t he t im eout and abor t , allowing t he ot her t r ansact ion t o proceed. You can configure t wo kinds of t r ansact ion t im eout s. The fir st is a m achine- wide param et er called t he global t r ansact ion t im eout . The global t im eout applies t o all t r ansact ions on t hat m achine. You configure t he global t im eout by right - click ing on t he My Com put er icon in t he Com ponent Ser vices Explorer , select ing Propert ies fr om t he cont ex t m enu, and select ing t he Opt ions t ab ( see Figur e 4- 13) . The default t im eout is set t o 60 seconds, but you can set it t o any value y ou like, up to 999 seconds. A global t im eout set t o zero m eans an infinit e t im eout . Tr ansact ions on t hat m achine never t im e out . I nfinit e t im eout is useful m ost ly for debugging, when you want t o t r y t o isolat e a problem in your business logic by st epping t hr ough your code and you do not want t he t r ansact ion you debug t o t im e out while y ou figure out t he pr oblem . Be ex t rem ely careful wit h infinit e t im eout in all ot her cases because it m eans t here are no safeguar ds against t ransact ion deadlocks. Figu re 4 - 1 3 . Se t t in g glob al t ra n sa ct ion t im e ou t
128
You can also configure t ransact ion t im eout at t he com ponent level, on it s Tr ansact ions t ab. Com ponent - level t ransact ion t im eout is disabled by default , and you have t o explicit ly enable it . Com ponent - level t ransact ion t im eout m eans t hat any t r ansact ion t his com ponent is part of m ust end wit hin t he t im e specified, or else COM+ abor t s it . Obviously , t he com ponent - level t im eout is effect iv e only if it is less t han t he global t im eout . The default com ponent lev el t im eout is set by COM+ t o zero, which indicat es infinit y. You can use com ponent - level t im eout in t w o cases. The fir st case is during developm ent , when you want t o t est t he way your applicat ion handles abor t ed t ransact ions. By set t ing t he com ponent lev el t im eout t o a sm all v alue ( such as one second) , y ou cause your t r ansact ion t o fail and can t hus observe your er ror handling code. The second case in which you set t he com ponent - lev el t ransact ion t im eout t o be less t han t he global t im eout is when y ou believe t hat t he com ponent is involved in m ore t han it s fair shar e of resour ce cont ent ion, result ing in deadlock s. I n t hat case, y ou should abor t t he t r ansact ion as soon as possible and not w ait for t he global t im eout t o expire.
4 .1 3 Tr a cin g Tr a n sa ct ions Som et im es, during developm ent , or per haps during deploym ent for logging purposes, you m ay want t o t r ace t he curr ent t r ansact ion I D under which your obj ect execut es. COM+ prov ides you w it h t w o ways t o r et rieve t he t r ansact ion I D, program m at ically and adm inist rat ively, using t he Com ponent Services Explorer . To t race t he curr ent t ransact ion I D pr ogr am m at ically, you should use IObjectContextInfo::GetTransactionId( ). Ex am ple 4- 8 shows how t o t race t he cur rent t ransact ion I D t o t he out put window in t he debugger . Ex a m p le 4 - 8 . Tr a cin g t h e cu rr e n t t ra n sa ct ion I D t o t h e ou t pu t w indow
129
HRESULT hres = S_OK; GUID guidTransactionID = GUID_NULL; IObjectContextInfo* pObjectContextInfo = NULL; hres = ::CoGetObjectContext(IID_IObjectContextInfo, (void**)&pObjectContextInfo); ASSERT(pObjectContextInfo != NULL); //a non-configure object maybe?
hres = pObjectContextInfo>GetTransactionId(&guidTransactionID);
pObjectContextInfo->Release(
);
if(guidTransactionID == GUID_NULL) { ATLTRACE("The object does not take part in a transaction"); } else { USES_CONVERSION; WCHAR pwsGUID[150]; ::StringFromGUID2(guidTransactionID,pwsGUID,150); ATLTRACE("The object takes place in transaction with ID %s ",W2A(pwsGUID)); } As long as a t r ansact ion is in progress, y ou can v iew it s t ransact ion I D in t he Com ponent Services Explor er when using t he adm inist rat ive m et hod. Under t he My Com put er icon in t he Com ponent Services Explorer is t he Dist ribut ed Transact ion Coordinat or ( DTC) folder . Expand t he DTC folder and select t he Transact ion List it em . The right pane in t he Com ponent Services Explorer cont ains a list of all t he t r ansact ions ex ecut ing on your m achine ( see Figur e 4- 14) . Figu r e 4 - 1 4 . Th e t ra n sact ion list vie w
130
The Com ponent Serv ices Explorer present s a few bit s of inform at ion on every t ransact ion. The St at us colum n cont ains t he t y pe of t he root com ponent and t he st at us of t he t r ansact ion, and t he Unit of Work I D colum n cont ains t he t r ansact ion I D.
4 .1 4 I n - Dou bt Tr a n sa ct ion s Som et im es COM+ ( act ually , t he DTC) is unable t o decide on t he fat e of a t ransact ion. This indecisiveness is usually t he result of som e unexpect ed cat ast r ophe. One possible cat ast rophe is net wor k failure aft er t he r oot obj ect is deact ivat ed, but befor e t he DTC could conduct t he t wo- phase com m it pr ot ocol wit h rem ot e resour ce m anager s. Anot her possible cat ast rophe is when a resource m anager ’s m achine cr ashes in t he m iddle of t he t wo- phase com m it prot ocol. I n t hose cases, t he t ransact ion is said t o be in- doubt . COM+ cannot decide on t he fat e of in- doubt t ransact ions. I t is up t o t he syst em adm inist r at or t o m anually resolv e t hose t ransact ions, using t he Com ponent Ser vices Explorer. COM+ list s t he in- doubt t r ansact ions under t he DTC folder, on t he Transact ion List pane. An in- doubt t ransact ion has t he com m ent ( I n Doubt ) in it s st at us colum n. The sy st em adm inist rat or should right - click on t he in- doubt t r ansact ion and select Resolve from t he pop- up m enu. COM+ offer s t hr ee opt ions t o r esolve a t ransact ion: Com m it , Abor t , or Forget ( see Figure 4- 15) . Figu r e 4 - 1 5 . Re solvin g in - dou b t t r a n sa ct ion s
131
When t he adm inist r at or select s Com m it or Abort , COM+ inst ruct s all accessible r esour ce m anagers t hat t ook par t in t he t r ansact ion t o com m it or abort , r espect ively. Lat er on, when t he rest of t he resource m anager s becom e av ailable, your sy st em adm inist rat or should use an adm inist rat ive ut ilit y t o launch a com pensat ing t r ansact ion on t hose resources. The int erest ing resolving opt ion is Forget . By choosing t o forget about t he t r ansact ion, your adm inist r at or inst r uct s COM+ t o do absolut ely not hing wit h t his t ransact ion besides rem ove it fr om t he list . The adm inist rat or is willing t o accept t he inconsist ent st at e t he sy st em is in, and does not wish t o com m it or abor t t he t ransact ion. For get t ing a t r ansact ion m ay be useful in som e esot er ic scenarios. I m agine t hat w hile a t ransact ion was in doubt , som e adm inist r at or m anually changed ent ries in t he dat abase because he did not wish t o w ait for t he t r ansact ion t o be r esolved. I n such a case, your applicat ion adm inist r at or m ay choose t o for get about t he or iginal t r ansact ion and accept t he cur rent st at e.
4 .1 5 Tr a n sa ct ion St a t ist ics The Com ponent Serv ices Explorer can show you various t r ansact ions st at ist ics. You view t he st at ist ics by select ing t he Transact ion St at ist ics it em in t he DTC folder ( see Figure 4- 16) . The st at ist ics view cont ains various num ber s r egarding t he cur rent ly execut ing t ransact ions, as well as aggregat ed num ber s result ing fr om all t ransact ions t hat t ook place since t he last m achine r eboot . Figu r e 4 - 1 6 . Th e Tr a n sa ct ion St at ist ics it em
132
The following list cont ains explanat ions of t he v ar ious st at ist ics: Act iv e The t ot al num ber of cur rent ly execut ing t ransact ions. Max. Act ive The m axim um num ber of t ransact ions t hat wer e act iv e concurr ent ly since t he last reboot . This num ber can be used as a crude t hroughput indicat or. I n Doubt The t ot al num ber of t ransact ions current ly in doubt . Com m it t ed The t ot al num ber of t ransact ions com m it t ed since t he last reboot . Abort ed The t ot al num ber of t ransact ions abort ed since t he last reboot . For ced Com m it The t ot al num ber of t ransact ion t hat w ere in doubt t hat t he adm inist rat or resolv ed by for cing t o com m it . A v alue ot her t hen zer o is usually t he result of a cat ast rophe t hat was resolved m anually . For ced Abort The t ot al num ber of t ransact ions t hat w ere in doubt t hat t he adm inist rat or resolv ed by for cing t o abor t . A value ot her t hen zero is usually t he result of a cat ast rophe t hat was r esolved m anually. Unknown The t ot al num ber of t ransact ions whose fat e is unk nown. Tot al The t ot al num ber of t ransact ions cr eat ed since t he last reboot . The st at ist ics ar e useful when you t r y t o calibrat e various applicat ion param et ers, such as pool sizes, t o m axim ize t hr oughput . An im port ant t hroughput indicat or is t he num ber of t ransact ions processed in a given am ount of t im e. You can get t hat num ber and
133
qualit y m et r ics, such as t he num ber of abort ed t ransact ions, fr om t he st at ist ics v iew.
4 .1 6 COM+ Tr a n sa ct ion s Pit fa lls I ’ll end t his chapt er by point ing out a few m or e pit falls you should be aw are of when designing and developing t r ansact ional com ponent s in COM+ . Som e of t hese pit falls have already been im plied elsewher e in t his chapt er , but elabor at ing on a pit fall is always a good idea. 4 .1 6 .1 Acce ssin g N on t r a nsa ct ion a l Re sou r ce s A t ransact ional com ponent should av oid accessing resources t hat are not resource m anagers. Typical exam ples are t he filesyst em , t he Regist r y, net wor k calls, and user int eract ion such as print out s or m essage box es. The reason is obvious—if t he t r ansact ion abort s, changes m ade t o t hose t ransact ion- ignorant r esources will persist and j eopardize syst em consist ency. 4 .1 6 .2 Pa ssin g Subr oot Obj e ct s t o Clie n t s You should avoid passing subroot obj ect s t o any client out side your t r ansact ion, be it t he client t hat creat ed t he r oot or any ot her client . You have t o avoid t his by design because COM+ allows you t o st um ble int o t he pit fall. The problem wit h sharing subroot obj ect s wit h client s out side of y our t r ansact ion is t hat at any m om ent t he client t hat creat ed t he root obj ect can release t he r oot obj ect . A COM+ t r ansact ion requir es a r oot t o funct ion, and t he r oot designat ion does not change, no m at t er how t he t r ansact ion is st ar t ed. Wit h t he r oot gone, t he t ransact ion layout is defect iv e. I n Figure 4- 17, any call from Client B t o Obj ect 2 w ill fail w it h t he er ror code CONTEXT_E_OLDREF. The only t hing Client B can do is r elease it s r efer ence t o Obj ect 2. Figu re 4 - 1 7 . Avoid p assin g or sh a rin g sub root obj e ct s w it h a n y clien t ou t sid e t h e t ra n sa ct ion
4 .1 6 .3 Acce ssin g Obj e ct s Ou t side t h e Tr a n sa ct ion
134
You should avoid accessing COM+ obj ect s out side y our t r ansact ion, whet her t hose obj ect s are par t of anot her t ransact ion or not . Look at t he obj ect s layout in Figure 4- 18. Figu r e 4 - 1 8 . Acce ssin g COM + ob j e ct s ou t side you r t r a n sa ct ion ca n j eopa r diz e syst e m con sist en cy
I n t his figure, Obj ect 1 has access t o Obj ect 2 and Obj ect 3, bot h out side it s t ransact ion. The pr oblem is t hat Transact ion A could abor t and Transact ion B could com m it . Obj ect 3 act s based on it s int er act ion wit h an obj ect fr om an abort ed t ransact ion, and t herefore Obj ect 3 j eopar dizes sy st em consist ency when it s t r ansact ion com m it s. Sim ilar ly , when Obj ect 1 accesses Obj ect 2 ( which does not have a t ransact ion at all) , Obj ect 2 m ay operat e based on inconsist ent st at e if Tr ansact ion A abort s. I n addit ion, t he int er act ion bet ween Obj ect 1 and Obj ect 2 is not well defined. For exam ple, should Obj ect 1 abor t it s t ransact ion if Obj ect 2 ret ur ns an err or ? For t hese reasons, obj ect s should only access ot her obj ect s wit hin t he sam e t ransact ion.
135
Cha pt e r 5 . COM + Concur r e ncy M ode l Em ploying m ult iple t hreads of ex ecut ion in y our applicat ion opens t he way for m any benefit s im possible t o achiev e using j ust a single t hr ead. These benefit s include: Responsive user int erface Your applicat ion can process user r equest s ( such as print ing or connect ing t o a rem ot e m achine) on a differ ent t hread t han t hat of t he user int erface. I f it wer e done on t he sam e t hread, t he user int er face w ould appear t o hang unt il t he ot her request s were processed. Because t he user int erface is on a differ ent t hread, it can cont inue t o r espond t o t he user’s request . Enhanced perform ance I f t he m achine your applicat ion r uns on has m ult iple CPUs and t he applicat ion is r equired t o per form m ult iple calculat ionint ensive independent operat ions, t he only way t o use t he ext ra processing power is t o ex ecut e t he operat ions on differ ent t hreads. I ncreased t hroughput I f your applicat ion is requir ed t o process incom ing client request s as fast at it can, y ou oft en spin off a num ber of work er t hr eads t o handle r equest s in parallel. Asy nchronous m et hod calls I nst ead of blocking t he client while t he obj ect processes t he client r equest , t he obj ect can delegat e t he work t o anot her t hr ead and r et ur n cont rol t o t he client im m ediat ely . I n gener al, whenever you hav e t wo or m or e operat ions t hat can t ake place in parallel and are different in nat ur e, using m ult it hr eading can bring significant gains t o y our applicat ion. The problem is t hat int roducing m ult it hr eading t o your applicat ion opens up a can of wor m s. You have t o worry about t hreads deadlock ing t hem selves while cont est ing for t he sam e resour ces, sy nchr onize access t o obj ect s by concurrent m ult iple t hreads, and be pr epared t o handle obj ect m et hod re- ent r ancy. Mult it hreading bugs and defect s are not oriously har d t o det ect , reproduce, and elim inat e. They oft en involve rare r ace condit ions ( in w hich m ult iple t hr eads writ e and read shar ed dat a wit hout appropriat e access sy nchr onizat ion) , and fix ing one problem oft en int roduces anot her. Writ ing robust , high perfor m ance m ult it hr eading obj ect - orient ed code is no t riv ial m at t er. I t r equires a great deal of sk ill and discipline on behalf of t he developers. Clearly t here is a need t o pr ov ide som e concurr ency m anagem ent serv ice t o y our com ponent s so you can focus on t he business problem at hand, inst ead of on m ult it hr eading synchr onizat ion issues. The classic COM concurrency m anagem ent m odel addresses
136
t he problem s of developing m ult it hreaded obj ect - orient ed applicat ions. However, t he classic COM solut ion has it s own set of deficiencies. COM+ concurr ency m anagem ent ser vice addresses t he problem s wit h t he classic COM solut ion. I t also pr ov ides y ou wit h adm inist rat ive suppor t for t he ser vice via t he Com ponent Services Explorer. This chapt er first briefly exam ines t he way classic COM solves concurr ency and synchr onizat ion pr oblem s in classic obj ect - or ient ed program m ing, and t hen int roduces t he COM+ concurr ency m anagem ent m odel, showing how it im pr oves classic COM concurr ency m anagem ent . The chapt er ends by descr ibing a new Windows 2000 t hr eading m odel, t he neut r al t hr eaded apar tm ent , and how it relat es t o COM+ com ponent s.
5 .1 Obj e ct - Or ie n t e d Pr ogr a m m in g a n d Mu lt iple Th r ea ds The classic COM t hr eading m odel was designed t o addr ess t he set of problem s inherent wit h obj ect s ex ecut ing in different t hr eads. Consider, for exam ple, t he sit uat ion depict ed in Figure 5- 1. Under classic obj ect - orient ed program m ing, t wo obj ect s on different t hr eads t hat w ant t o int er act w it h each ot her have t o worry about sy nchr onizat ion and concur rency. Figu r e 5 - 1 . Obj e ct s e xe cu t in g on t w o diffe re n t t h re a ds
Obj ect 1 resides in Thr ead A and Obj ect 2 resides in Thr ead B. Suppose t hat Obj ect 1 want s t o inv ok e a m et hod of Obj ect 2, and t hat m et hod, for what ev er reason, m ust run in t he cont ext of Thread B. The pr oblem is t hat , ev en if Obj ect 1 has a point er t o Obj ect 2, it is useless. I f Obj ect 1 uses such a point er t o invok e t he call, t he m et hod execut es in t he cont ext of Thr ead A. This behav ior is t he direct result of t he im plem ent at ion language used t o code t he obj ect s. Progr am m ing languages such as C+ + ar e com plet ely t hread- oblivious—t her e is not hing in t he language it self t o denot e a specific execut ion cont ext , such as a t hr ead. I f you have a point er t o an obj ect and you inv oke a m et hod of t hat obj ect , t he com piler places t he m et hod's param et ers and r et urn address on t he calling t hr ead's st ack—in t his case, Thr ead A's st ack . That does not hav e t he int ended effect of ex ecut ing t he call in t he cont ext of 137
Thread B. Wit h a dir ect call, knowledge t hat t he m et hod should hav e ex ecut ed on anot her t hr ead rem ains in t he design docum ent , on t he whit eboard, or in t he m ind of t he program m er. The classic obj ect - orient ed program m ing ( OOP) solut ion is t o post or send a m essage t o Thread B. Thr ead B would process t he m essage, inv oke t he m et hod on Obj ect 2, and signal Thr ead A when it finished. Meanwhile, Obj ect 1 would have had t o block it self and wait for a signal or event from Obj ect 2 signifying t hat t he m et hod has com plet ed execut ion. This solut ion has several disadv ant ages: you have t o handcr aft t he m echanism , t he likelihood of m ist akes ( result ing in a deadlock) is high, and you ar e for ced t o do it over and over again ev er y t im e you have obj ect s on m ult iple t hreads. The m ore acut e problem is t hat t he OOP solut ion int roduces t ight coupling bet ween t he t w o obj ect s and t he synchronizat ion m echanism . The code in t he t wo obj ect s has t o be awar e of t heir execut ion cont ext s, of t he way t o post m essages bet ween obj ect s, of how t o signal event s, and so on. One of t he core principals of OOP, encapsulat ion or inform at ion hiding, is violat ed; as a result , m aint enance of classic m ult it hreaded obj ect - orient ed program s is har d, expensive, and error- pr one. That is not all. When developers st art ed developing com ponent s ( pack aging obj ect s in binar y unit s, such as DLLs) , a classic problem in dist r ibut ed com put ing raised it s head. The idea behind com ponent - orient ed developm ent is building sy st em s out of wellencapsulat ed binary ent it ies, which you can plug or unplug at will lik e Lego bricks. Wit h com ponent - orient ed developm ent , you gain m odular it y, ex t ensibilit y , m aint ainabilit y , and r eusabilit y. Developers and sy st em designers want ed t o get away from m onolit hic obj ect - or ient ed applicat ions t o a collect ion of int eract ing binary com ponent s. Figure 5- 2 shows a product t hat consist s of com ponent s. The applicat ion is const ruct ed fr om a set of com ponent s t hat int er act w it h one anot her. Each com ponent was im plem ent ed by an independent vendor or t eam . Howev er , what should be done about t he synchr onizat ion r equirem ent s of t he com ponent s? What happens if Com ponent s 3 and 1 t ry t o access Com ponent 2 at t he sam e t im e? Could Com ponent 2 handle it ? Will it crash? Will Com ponent 1 or Com ponent 3 be blocked? What effect would t hat hav e on Com ponent 4 or 5? Because Com ponent 2 was developed as a st andalone com ponent , it s developer could not possibly know what t he specific runt im e envir onm ent for t he com ponent s would be. Wit h t hat lack of knowledge, m any quest ions arise. Should t he com ponent be defensive and prot ect it self fr om m ult iple t hreads accessing it ? How can it part icipat e in an applicat ion- wide sy nchr onizat ion m echanism t hat m ay be in place? Per haps Com ponent 2 will never be accessed sim ult aneously by t wo t hr eads
138
in t his applicat ion; however , Com ponent 2’s developer cannot k now t his in advance, so it m ay choose t o always pr ot ect t he com ponent , t aking an unnecessary perform ance hit in m any cases for t he sak e of avoiding deadlock s. Figu r e 5 - 2 . Ob j ect s pa cka g ed in bin a ry u nit s h a ve n o w a y of k n ow in g ab ou t t h e syn ch r oniza t ion n ee d s of ot h e r obj e ct s in ot h e r un it s
5 .2 Apa r t m e n t s: Th e Cla ssic COM Solu t ion The solut ion used by classic COM is decept ively sim ple: each com ponent declar es it s synchr onizat ion needs. Classic COM m akes sure t hat inst ances ( obj ect s) of t hat class always r eside in an execut ion cont ext t hat fit s t heir declar ed r equir em ent s, hence t he t erm apar t m ent . A com ponent declares it s sy nchronizat ion needs by assigning a value t o it s ThreadingModel nam ed- value in t he Regist ry . The v alue of ThreadingModel det er m ines t he com ponent ’s t hr eading m odel. The available values under classic COM are Apartment, Free, Both or no value at all. Com ponent s t hat set t heir t hreading m odel t o Apartment or leave it blank indicat e t o COM t hat t hey cannot handle concurrent access. COM places t hese obj ect s in a single- t hreaded env ironm ent called a single- t hr eaded apar t m ent ( STA) . STA obj ect s alway s ex ecut e on t he sam e STA t hr ead, and t herefore do not have t o worry about concurr ent access fr om m ult iple t hreads. Com ponent s t hat ar e capable of handling concur rent access fr om m ult iple client s on m ult iple t hreads set t heir t hreading m odel t o Free. COM places such obj ect s in a m ult it hr eaded apart m ent ( MTA) . Com ponent s t hat w ould lik e t o alway s be in t he sam e apar t m ent as t heir client set t heir t hreading m odel t o Both. Not e t hat a Both com ponent m ust be capable of handling concurr ent access from m ult iple client s on m ult iple t hreads because it s client m ay be in t he MTA. As discussed in Chapt er 2, classic COM m ar shals away t he t hread differ ences bet ween t he client and an obj ect by placing a proxy and st ub pair in bet ween. The pr ox y and st ub pair blocks t he calling t hr ead, per form s a cont ex t swit ch, builds t he calling st ack on t he
139
obj ect ’s t hread, and calls t he m et hod. When t he call is finished, cont rol ret ur ns t o t he calling t hr ead t hat w as blocked. Alt hough apart m ent s solv e t he problem of m et hods execut ing out side t heir t hr eads, t hey cont r ibut e t o ot her problem s, specifically: •
•
•
•
• •
•
•
Classic COM achiev es synchr onizat ion by having an obj ect - t ot hr ead affinit y. I f an obj ect always execut es on t he sam e t hr ead, t hen all access t o it is synchronized. But what if t he obj ect does not car e about t hr ead affinit y, but only requires sy nchr onizat ion? That is, as long as no m ore t han one t hread accesses t he obj ect at a giv en t im e, t he obj ect does not car e which t hread accesses it . The STA m odel int roduces a sit uat ion called obj ect st ar vat ion. I f one obj ect in a STA hogs t he t hr ead ( t hat is, per form s lengt hy processing in a m et hod call) t hen all ot her obj ect s in t he sam e STA cannot ser ve t heir client s because t hey m ust execut e on t he sam e t hread. Sharing t he sam e STA t hread is an ov erkill of pr ot ect ion—calls t o all obj ect s in a STA are serialized; not only can client s not access t he sam e obj ect concurrent ly, but t hey can't access differ ent obj ect s in t he sam e t hr ead concur rent ly. Even if a developer goes t hrough t he t rouble of m aking it s obj ect t hread- safe ( and m arks it as using t he Free t hreading m odel) , if t he obj ect 's client is in anot her apart m ent , t he obj ect st ill m ust be accessed via a pr oxy - st ub and incur a per form ance penalt y. Sim ilar ly, all access t o an obj ect m arked as Both t hat is loaded in a STA is serialized for no reason. I f your applicat ion cont ains a client and an obj ect each in differ ent apart m ent s, y ou pay for t hr ead cont ex t - swit ch overhead. I f t he calling pat t ern is fr equent calls t o m et hods wit h shor t execut ion t im es, it could kill y our applicat ion's per form ance. MTA obj ect s hav e t he pot ent ial of deadlock. Each call int o t he MTA com es in on a differ ent t hread. MTA obj ect s usually lock t hem selves for access while t hey ar e ser ving a call. I f t wo MTA obj ect s ser ve a call and t ry t o access each ot her , a deadlock occurs. Local ser vers t hat host MTA obj ect s face esot eric race condit ions when t he pr ocess is shut down while t hey are handling new act ivat ion request s.
5 .3 Act ivit ies: Th e COM + I nnova t ion The t ask for COM+ was not only t o solv e t he classic OOP problem s but also t o addr ess t he classic COM concurr ency m odel deficiencies
140
and m aint ain back war d com pat ibilit y. I m agine a client calling a m et hod on a com ponent . The com ponent can be in t he sam e cont ext as t he client , in anot her apart m ent or a process on t he sam e m achine, or in a pr ocess on anot her m achine. The called com ponent m ay in t ur n call ot her com ponent s, and so on, cr eat ing a st r ing of nest ed calls. Ev en t hough you cannot point t o a single t hr ead t hat carr ies out t he calls, t he com ponent s involv ed do shar e a logical t hr ead of ex ecut ion. Despit e t he fact t hat t he logical t hr ead can span m ult iple t hr eads, processes, and m achines, t here is only one root client . There is also only one t hread at a t im e execut ing in t he logical t hread, but not necessarily t he sam e phy sical t hr ead at all t im es. The idea behind t he COM+ concurr ency m odel is sim ple, but powerful: inst ead of achiev ing sy nchronizat ion t hrough physical t hr ead affinit y, COM+ achieves sy nchr onizat ion t hr ough logical t hr ead affinit y. Because in a logical t hr ead t here is j ust one phy sical t hr ead ex ecut ing in any given point in t im e, logical t hread affinit y im plies physical t hreads synchronizat ion as well. I f a com ponent is guar ant eed not t o be accessed by m ult iple logical t hr eads at t he sam e t im e, t hen synchronizat ion t o t hat com ponent is guarant eed. Not e t hat t her e is no need t o guarant ee t hat a com ponent is always accessed by t he sam e logical t hread. All COM+ prov ides is a guar ant ee t hat t he com ponent is not accessed by m or e t han one logical t hr ead at a t im e. A logical t hread is also called a causalit y, a nam e t hat em phasizes t he fact t hat all of t he nest ed calls t rigger ed by t he root client share t he sam e cause—t he r oot client 's request on t he t opm ost obj ect . Due t o t he fact t hat m ost of t he COM+ docum ent at ion r efer s t o a logical t hr ead as causalit y, t he r est of t his chapt er uses causalit y t oo. COM+ t ags each causalit y wit h it s own unique I D—a GUI D called t he causalit y I D. To pr ev ent concur rent access t o an obj ect by m ult iple causalit ies, COM+ m ust associat e t he obj ect wit h som e sort of a lock, called a causalit y lock. However, should COM+ assign a causalit y lock per obj ect ? Doing so m ay be a wast e of r esources and pr ocessing t im e, if by design t he com ponent s ar e all m eant t o par t icipat e in t he sam e act ivit y on behalf of a client . As a r esult , it is up t o t he com ponent dev eloper t o decide how t he obj ect is associat ed wit h causalit ybased lock s: whet her t he obj ect needs a lock at all, whet her it can share a lock wit h ot her obj ect s, or whet her it requires a new lock. COM+ groups t oget her com ponent s t han can share a causalit ybased lock . This gr ouping is called an act ivit y. I t is im port ant t o underst and t hat an act iv it y is only a logical t erm and is independent of pr ocess, apart m ent , and cont ex t : obj ect s fr om different cont ex t s, apart m ent s, or processes can all share t he sam e act ivit y ( see Figure 5- 3) .
141
Figu re 5 - 3 . Act ivit ie s ( ind ica t ed b y da sh e d lin es) a re in de pen dent of con t e x t s, a pa rt m en t s, a n d pr oce sse s
Wit hin an act ivit y, concur rent calls from m ult iple causalit ies ar e not allowed and COM+ enfor ces t his requir em ent . Act iv it ies ar e very useful for MTA obj ect s and for neut r al t hreaded apart m ent ( NTA) obj ect s, a new t hr eading m odel discussed at t he end of t he chapt er; t hese obj ect s m ay require sy nchr onizat ion, but not physical t hread affinit y wit h all it s lim it at ions. STA obj ect s are sy nchronized by vir t ue of t hread affinit y and do not benefit from act ivit ies. 5 .3 .1 Ca u sa lit y- Ba se d Lock To achiev e causalit y - based synchronizat ion for obj ect s t hat t ak e par t in an act iv it y, COM+ m aint ains a causalit y- based lock for each act ivit y . The act ivit y lock can be owned by at m ost one causalit y at a t im e. The act iv it y lock keeps t rack of t he causalit y t hat cur rent ly owns it by t racking t hat causalit y’s I D. The causalit y I D is used as an ident ify ing k ey t o access t he lock . When a causalit y ent ers an act ivit y , it m ust t ry t o acquir e t he act ivit y lock first by pr esent ing t he lock wit h it s I D. I f t he lock is alr eady ow ned by a different causalit y ( it will hav e a different I D) , t he lock blocks t he new causalit y t hat t r ies t o ent er t he act ivit y . I f t he lock is free ( no causalit y owns it or t he lock has no causalit y I D associat ed wit h it ) , t he new causalit y will own it . I f t he causalit y already owns t hat lock , it w ill not be blocked, which allows for callbacks. The lock has no t im eout associat ed wit h it ; as a r esult , a call from out side t he act ivit y is blocked unt il t he cur rent causalit y ex it s t he act ivit y . I n t he case of m ore t han one causalit y t ry ing t o ent er t he act ivit y , COM+ places all pending causalit ies in a queue and let s t hem ent er in t he act iv it y in order . The act ivit y lock is effect ive process- wide only. When an act ivit y flows from Pr ocess 1 t o Pr ocess 2, COM+ allocat es a new lock in Process 2 for t hat act iv it y, so t hat at t em pt s t o access t he local obj ect s in Process 2 w ill not hav e t o pay for expensive cross- process or cross- m achine look ups. 142
An int er est ing observat ion is t hat a causalit y - based lock is unlik e any ot her Win32 API - prov ided locks. Nor m al lock s ( crit ical sect ions, m ut exes, and sem aphores) ar e all based on a phy sical t hr ead I D. A nor m al physical t hr ead- based lock records t he physical t hr ead I D t hat owns it , block ing any ot her physical t hread t hat t ries t o access it , all based on phy sical t hr ead I Ds. The causalit y- based lock let s all t he phy sical t hr eads t hat t ak e part in t he sam e logical t hread ( sam e causalit y) go t hrough; it only block s t hr eads t hat call from different causalit ies. There is no docum ent ed API for t he causalit y lock . Act iv it y- based sy nchr onizat ion solves t he classic COM deadlock of cy clic calling—if Obj ect 1 calls Obj ect 2, which t hen calls Obj ect 3, which t hen calls Obj ect 1, t he call back t o Obj ect 1 would go t hr ough because it shar es t he sam e causalit y, even if all t he obj ect s execut e on differ ent t hreads. 5 .3 .2 Act ivit ie s a n d Con t e x t s So how does COM+ know which act ivit y a given obj ect belongs t o? What propagat es t he act iv it y acr oss cont ex t s, apart m ent s, and processes? Like alm ost ev er yt hing else in COM+ , t he pr oxy and st ub pair does t he t r ick. COM+ m aint ains an ident ifying GUI D called t he act iv it y I D for every act ivit y . When a client cr eat es a COM+ obj ect t hat want s t o t ake par t in an act iv it y and t he client has no act ivit y associat ed wit h it , COM+ generat es an act iv it y I D and st or es it as a propert y of t he cont ext obj ect ( discussed in Chapt er 2) . A COM+ cont ext belongs t o at m ost one act ivit y at any given t im e, and m ay be none at all. The obj ect t hat cr eat ed t he act iv it y I D is called t he root of t he act ivit y . When t he r oot obj ect cr eat es anot her obj ect in a different cont ext —say Obj ect 2—t he proxy t o Obj ect 2 grabs t he act ivit y I D fr om t he cont ext obj ect and passes it t o t he st ub of Obj ect 2, pot ent ially acr oss processes and m achines. I f Obj ect 2 r equir es sy nchr onizat ion, it s cont ex t uses t he act ivit y I D of t he root .
5 .4 COM+ Con figu r a t ion Se t t in gs Every COM+ com ponent has a t ab called Concurrency on it s proper t ies page t hat let s y ou set t he com ponent sy nchronizat ion requir em ent s ( see Figur e 5- 4) . The possible v alues ar e: • • • • •
Disabled Not Support ed Suppor t ed Required Requires New
143
Figu r e 5 - 4 . Th e Con cu r r en cy t a b le t s you con figu r e you r com pon e n t ’s syn ch ron iz a t ion r e qu ire m en t s
The sy nchronizat ion is act iv it y based, as explained befor e. These set t ings ar e used t o decide in which act iv it y t he obj ect w ill reside in relat ion t o it s cr eat or . As y ou m ay suspect , t he way t he sy nchr onizat ion v alues operat e is com plet ely analogous t o t he t r ansact ion support configurat ion v alues, discussed in Chapt er 4. An obj ect can r eside in any of t hese act iv it ies: • • •
I n it s creat or’s act ivit y : t he obj ect shares a lock wit h it s cr eat or. I n a new act iv it y: t he obj ect has it s own lock and st ar t s a new causalit y. I n no act ivit y at all: t here is no lock, so concurr ent access is allowed.
An obj ect ’s act ivit y is det erm ined at creat ion t im e, based on t he act ivit y of t he creat or and t he configur ed r equirem ent of t he obj ect . For ex am ple, if t he obj ect is configured t o hav e a sy nchronizat ion set t ing of Required, it will shar e it s creat or’s act ivit y if it has one. I f t he creat or does not have an act iv it y, t hen COM+ creat es a new act ivit y for t he obj ect . The effect s of t his sy nchronizat ion support are defined in Table 5- 1. 7DEOH'HWHUPLQDQWVRIDQREMHFW VDFWLYLW\ 2EMHFWV\QFKURQL]DWLRQVXSSRUW
,VFUHDWRULQDFWLYLW\"
7KHREMHFWZLOOWDNHSDUWLQ
Disabled/ Not Support ed
No
No Act iv it y
Support ed
No
No Act iv it y
Required
No
New Act iv it y
Required New
No
New Act iv it y
Disabled/ Not Support ed
Yes
No Act iv it y
Support ed
Yes
Creat or ’s Act iv it y
Required
Yes
Creat or ’s Act iv it y
Required New
Yes
New Act iv it y
144
Figure 5- 5shows an ex am ple of act ivit y flow. I n t he figure, a client t hat does not t ak e part in an act ivit y cr eat es an obj ect configured wit h Synchr onizat ion = Requir ed. Since t he obj ect r equir es an act ivit y and it s creat or has none, COM+ m ak es it t he root of a new act ivit y . The r oot t hen goes on t o cr eat e five m ore obj ect s. Two of t hem , configured wit h Synchronizat ion = Required and Sy nchr onizat ion = Suppor t ed, are placed in t he sam e act ivit y as t he root . The t w o com ponent s configur ed wit h Sy nchr onizat ion = Not Suppor t ed and Synchronizat ion = Disabled will hav e no act ivit y . The last com ponent is configur ed wit h Sy nchr onizat ion = Requir es New, so COM+ creat es a new act ivit y for it , m ak ing it t he root of it s own act ivit y . Figu r e 5 - 5 . Alloca t ing ob j e ct s t o a ct ivit ie s b ase d on t h e ir con figu r at ion an d t h e act ivit y of t he ir cr ea t or
You m ay be asking y our self why COM+ bases t he decision on t he obj ect ’s act iv it y part ly on t he obj ect ’s creat ing client . The heurist ic t echnique COM+ uses is t hat t he calling pat t er ns, int er act ions, and sy nchr onizat ion needs bet ween obj ect s usually closely m at ch t heir cr eat ion r elat ionship. An act ivit y last s as long as t he part icipat ing obj ect s ex ist , and it s lifet im e is independent of t he causalit ies t hat ent er and leav e it . A causalit y is a t ransient ent it y t hat last s only as long as t he client ’s call is in pr ogr ess. The act ivit y t o causalit y relat ionship is analogous t o t he t r ansact ion layout t o t ransact ion r elat ionship descr ibed in Chapt er 4. 5 .4 .1 Syn ch r on iza t ion D isa ble d When y ou choose t o disable synchr onizat ion suppor t , y ou are inst ruct ing COM+ t o ignore t he sy nchronizat ion r equirem ent s of t he com ponent in det erm ining cont ex t for t he obj ect . As a r esult , t he obj ect m ay or m ay not share it s cr eat or’s cont ext .
145
You can use t he Disabled set t ing when m igr at ing a classic COM com ponent t o COM+ . I f t hat com ponent was built t o operat e in a m ult it hr eaded environm ent , it alr eady has a synchronizat ion m echanism of som e sor t , and you m ust disable t he sy nchronizat ion at t r ibut e t o m aint ain t he old behav ior . I n addit ion, if you disable sy nchronizat ion on a com ponent , t hat com ponent should never access a r esource m anager because it m ight r equire t he act ivit y I D for it s ow n int ernal locking. 5 .4 .2 Syn ch r on iza t ion N ot Suppor t ed An obj ect set t o Not Support ed nev er par t icipat es in an act iv it y, regardless of causalit y . The obj ect m ust provide it s own sy nchr onizat ion m echanism . This set t ing is only av ailable for com ponent s t hat ar e nont r ansact ional and do not use JI TA. I recom m end av oiding t his set t ing because it offer s not hing t o t he dev eloper ex cept rest r ict ions. 5 .4 .3 Syn ch r on iza t ion Suppor t e d An obj ect set t o Support ed will share it s cr eat or ’s act iv it y if it has one, and will have no synchr onizat ion of it s own if t he cr eat or does not hav e one. This is t he least useful set t ing of t hem all because t he obj ect m ust provide it s own sy nchronizat ion m echanism in case it s cr eat or does not hav e an act ivit y . You m ust m ak e sur e t hat t he m echanism does not int er fere wit h COM+ act iv it ies when COM+ pr ovides sy nchr onizat ion. As a result , it is m or e difficult t o dev elop t he com ponent . 5 .4 .4 Syn ch r on iza t ion Re qu ir e d When an obj ect is set t o Requir ed, all calls t o t he obj ect will be sy nchr onized, and t he only quest ion is whet her your obj ect will hav e it s own act ivit y or shar e it s creat or’s act ivit y . When COM+ cr eat es t he obj ect , it looks at t he act iv it y st at us of it s cr eat or. I f t he cr eat or has an act iv it y, COM+ ex t ends t he creat or ’s act ivit y boundary t o include t he new obj ect . Ot herwise, COM+ creat es a new act iv it y. I f y ou don’t car e about hav ing your own act ivit y, always use t his set t ing. 5 .4 .5 Syn ch r on iza t ion Re qu ir e s N e w When an obj ect is set t o Requir es New, t he obj ect m ust hav e a new act ivit y , dist inct fr om t he cr eat or’s act iv it y, and hav e it s own lock. The obj ect will nev er shar e it s cont ex t wit h it s creat or . I n fact , t his is one of t he sure w ay s of ensur ing t hat your obj ect will alw ays be cr eat ed in it s ow n cont ex t . 146
5 .4 .6 Re qu ir e d Ve r sus Re qu ir e s N e w Deciding t hat y our obj ect requires sy nchronizat ion is usually st raight forward. I f y ou ant icipat e m ult iple client s on m ult iple t hr eads t r ying t o access your obj ect and you don’t want t o wr it e your own sy nchronizat ion m echanism , y ou need sy nchronizat ion. The m ore difficult quest ion t o answer is whet her y our obj ect should requir e it s own act ivit y lock or whet her y ou should configur e it t o use t he lock of it s cr eat or. Try basing your decision on t he calling pat t erns t o y our obj ect . Consider t he calling pat t ern in Figure 5- 6. Obj ect 2 is configur ed wit h sy nchronizat ion set t o Required and is placed in t he sam e act ivit y as it s cr eat or , Obj ect 1. I n t his exam ple, besides creat ing Obj ect 2, Obj ect 1 and Obj ect 2 do not int er act wit h each ot her . Figu re 5 - 6 . Sh a rin g act ivit ie s e n a ble ca lls t o b e acce p t e d from an ot h er clie n t
While Client 1 accesses Obj ect 1, Client 2 com es along, want ing t o call m et hods on Obj ect 2. Because Client 2 has a differ ent causalit y, it w ill be blocked. I n fact , it could hav e safely accessed Obj ect 2, since it does not violat e t he synchronizat ion requir em ent for t he cr eat ing obj ect , Obj ect 1. On t he ot her hand, if you were t o configur e Obj ect 2 t o r equire it s own act iv it y by set t ing t he Synchronizat ion t o Requires New , t he obj ect could process calls from ot her client s at t he sam e t im e as Obj ect 1 ( see Figure 5- 7) . Figu re 5 - 7 . I n t h is ca llin g pa t t er n , h a ving a se pa ra t e a ct ivit y for t h e cr e at e d ob j ect e n a b les it t o ser vice it s client s m ore e fficie n t ly
147
However, calls from t he cr eat or obj ect ( Obj ect 1) t o Obj ect 2 will now pot ent ially block and will be m or e expensive because t hey m ust cr oss cont ex t boundaries and pay t he overhead of t rying t o acquire t he lock .
5 .5 Act ivit ies a nd JI TA Com ponent s t hat use JI TA ar e required t o be accessed by one client at a t im e. I f t wo client s could call a JI TA com ponent sim ult aneously, one would be left st randed when t he obj ect was deact iv at ed at t he t im e t he fir st m et hod call r et urned. COM+ enforces synchronizat ion on com ponent s t hat use JI TA. The Concur rency t ab for com ponent s t hat have JI TA enabled will only allow you t o set your com ponent t o Required or Requir es New. I n ot her words, t he com ponent m ust share t he act iv it y of it s creat or or require a new act ivit y. The ot her opt ions are disabled on t he Concurr ency t ab. Once y ou disable JI TA, you can set synchr onizat ion t o ot her values.
5 .6 Act ivit ies a nd Tr a n sa ct ion s Transact ional obj ect s also allow access t o t hem by only one client at a t im e. Synchronizat ion is required t o prevent t he case in w hich one client on one t hr ead t r ies t o com m it a t ransact ion while anot her client on a second t hread t ries t o abort it . As a r esult , ev ery t r ansact ion should have a sy nchr onizat ion lock associat ed w it h it . On t he ot her hand, hav ing m ore t han one lock in a giv en t r ansact ion is undesir able—spinning off a new act ivit y for an obj ect t hat is added t o an exist ing t r ansact ion m eans alway s paying for t he overhead for check ing t he act ivit y lock before accessing t he obj ect . That check is redundant because no t wo causalit ies are allowed in t he sam e t r ansact ion any way . I n fact , w hen an obj ect r equires a new t ransact ion, it could st ill reuse t he sam e causalit y lock of it s cr eat or and allow t he act ivit y t o flow int o t he new t ransact ion. COM+ t herefore enforces t he fact t hat a given t r ansact ion can only be part of one act ivit y ( not e t hat an act ivit y can st ill host m ult iple t r ansact ions) . I n addit ion, as discussed in Chapt er 4, t ransact ional obj ect s always use JI TA ( COM+ aut om at ically enables JI TA for a t ransact ional obj ect ) . The use of JI TA is only opt ional for nont r ansact ional obj ect s. Table 5- 2 sum m arizes t he sy nchr onizat ion v alues as a product of t he t r ansact ion and JI TA set t ing. Not e t hat t he only case when a t r ansact ional com ponent can st ar t a new act iv it y is when t hat com ponent is also configured t o be t he r oot of a new t r ansact ion. 7DEOH&RPSRQHQW VDYDLODEOHV\QFKURQL]DWLRQVHWWLQJV
148
7UDQVDFWLRQVHWWLQJ
-,7$VHWWLQJ
$YDLODEOHV\QFKURQL]DWLRQVHWWLQJ
Disabled
Off
All
Not Support ed
Off
All
Disabled
On
Required or Requires New
Not Support ed
On
Required or Requires New
Support ed
On
Required
Required
On
Required
Requires New
On
Required or Requires New
5 .7 Tr a cin g Act ivit ie s COM+ m ak es it easy for an obj ect t o ret r ieve it s act ivit y ident it y , using t he cont ext obj ect int erface IObjectContextInfo, wit h t he m et hod: HRESULT GetActivityID(GUID* pguidActivityID); I f t he obj ect does not t ak e part in an act ivit y , t he m et hod r et ur ns GUID_NULL. Ret rieving t he act ivit y I D is useful for debugging and t r acing purposes. Exam ple 5- 1 dem onst r at es act ivit y I D t r acing. Ex a m p le 5 - 1 . Tr a cin g t h e a ct ivit y I D
HRESULT hres = S_OK; GUID guidActivityID = GUID_NULL; IObjectContextInfo* pObjectContextInfo = NULL; hres = ::CoGetObjectContext(IID_IObjectContextInfo, (void**)&pObjectContextInfo); ASSERT(pObjectContextInfo != NULL);//a non-configure object maybe? hres = pObjectContextInfo>GetActivityId(&guidActivityID); pObjectContextInfo->Release(
);
if(guidActivityID == GUID_NULL) { TRACE("The object does not take part in an activity"); } else { USES_CONVERSION; WCHAR pwsGUID[150]; ::StringFromGUID2(guidActivityID,pwsGUID,150);
149
TRACE("The object takes place in activity with ID %s",W2A(pwsGUID)); } COM+ provides t he act iv it y I D via anot her int erface, called IObjectContextActivity, obt ained by calling CoGetObjectContext( ). IObjectContextActivity has j ust one m et hod, GetActivityId( ), used exact ly lik e t he m et hod of t he sam e nam e in t he ex am ple.
5 .8 Th e N e u t r a l Th r ea ded Apa r t m e n t The neut ral t hreaded apart m ent ( NTA) is a new t hr eading m odel available only on Windows 2000. Alt hough it is not specific t o COM+ ( classic COM obj ect s can also t ake advant age of t he NTA) , t he NTA is t he recom m ended t hreading m odel for m ost COM+ obj ect s t hat do not have a user int er face. The NTA has evolved t o address a deficiency in t he classic COM MTA t hr eading m odel: suppose y ou have an STA client accessing an MTA obj ect . Under classic COM, all cross- apar t m ent calls hav e t o be m ar shaled via a proxy/ st ub pair . Even t hough t he obj ect could have handled t he call on t he client STA t hread, t he call is m arshaled. The st ub perform ed an expensive t hread cont ext swit ch t o an RPC t hr ead t o access t he MTA obj ect s. Ther e was clear ly a need for an apar t m ent t hat every t hr ead in t he process could ent er wit hout paying a heavy per for m ance penalt y. This is what t he NTA is: an apar t m ent t hat every COM- aware t hr ead can ent er. I n every process, t her e is exact ly one NTA. The NTA is subdiv ided ( like any ot her apart m ent ) int o cont ex t s. COM obj ect s t hat r eside in t he NTA set t heir t hreading m odel v alue in t he Regist ry t o Neutral. Much lik e an MTA obj ect , an obj ect m ar ked as neut r al will r eside in t he NTA, r egardless of it s creat or’s apar t m ent . Calls int o t he NTA are m ar shaled, but only light - weight prox ies are used ( t o do cr oss COM+ cont ext m arshaling, if needed) because no t hr ead- cont ext swit ch is inv olv ed. A m et hod call on an NTA obj ect is ex ecut ed on t he caller ’s t hread, be it STA or MTA based. No t hr ead calls t he NTA hom e, and t he NTA cont ains no t hr eads, only obj ect s. Thr eads can’t call CoInitializeEx( ) wit h a flag saying NTA, and no such flag exist s. When y ou cr eat e a t hread, you st ill m ust assign it t o an STA of it s own or t o t he MTA. 5 .8 .1 Th e N TA a n d Ot he r COM Th r e a ding M ode ls When y ou m ark your obj ect as Neutral, it will alway s r eside in t he NTA, regar dless of t he locat ion of it s cr eat ing client . When you m ar k your obj ect as Both, if t he obj ect ’s creat or is an NTA obj ect , t he
150
obj ect will reside in t he NTA as well. I f y our NTA obj ect cr eat es ot her obj ect s m ar ked as Apartment, t he locat ion of t he creat ing t hr ead m ay affect s wher e t hose obj ect s reside. Table 5- 3 present s t he pot ent ial r esult s when NTA client s creat e ot her obj ect s. I t also shows t he result ing obj ect apart m ent , based on t he obj ect t hr eading m odel and t he t hread t he NTA client runs on. You can also see from Table 5- 3 t hat com ponent s m ar ked as Neutral will always be in t he NTA, regardless of t he apart m ent of t heir creat or . 7DEOH$SDUWPHQWDFWLYDWLRQSROLF\ 2EMHFWLV?&OLHQWLV
$SDUWPHQW
)UHH
%RWK
1HXWUDO1RWVSHFLILHG
STA, not m ain
Cur r ent STA
MTA Cur r ent STA NTA
Main STA
Main STA
Main STA
MTA Main STA
NTA
Main STA
MTA
Host STA
MTA MTA
NTA
Main STA
Neut ral ( on STA t hread) On t hat STA t hr ead MTA NTA
NTA
Main STA
Neut ral ( on MTA t hread) Host STA
NTA
Main STA
MTA NTA
The NTA m odel obey s t he COM rule specifying t hat all obj ect s m ust be m arshaled out side t he apart m ent / cont ext boundary, j ust like any ot her apar t m ent . I f you have t o m anually m ar shal an obj ect out side t he NTA, use t he Global I nt er face Table ( t he GI T) or t he GI T wrapper class, pr esent ed in Chapt er 2. Finally, t he NTA offer s im pr oved DCOM perform ance because incom ing calls from rem ot e m achines t o NTA obj ect s can execut e dir ect ly on t he t hread t hat handles t he incom ing r em ot e call, wit hout a t hr ead cont ext swit ch. 5 .8 .2 COM + a n d Th r e a din g M ode l Your COM+ com ponent should r un in t he STA if any one of t he following st at em ent s is valid: •
•
•
•
Your COM+ com ponent displays a user int erface or it relies on hav ing a m essage loop pum p m essages t o it . Your com ponent relies on t he STA t hread m essage pum p. Your COM+ com ponent uses Thread Local St orage ( TLS) , a t hr ead- specific heap allocat ed off t he t hr ead st ack . I t m ust run in t he STA because TLS relies on having t he t hread affinit y t he STA provides. Your com ponent was prov ided by a t hir d par ty as a COM com ponent and m ar ked as Apartment. You want t o im por t it t o y our COM+ applicat ion so t hat it shar es your applicat ion set t ings, such as secur it y and process, and is part of your applicat ion’s MSI file. You should not change t he t hr eading m odel, because y ou do not know how m uch t hr ead affinit y t he com ponent requires. Your com ponent is developed using Visual Basic 6.0.
151
Your COM+ com ponent should use t he Both t hr eading m odel if t he cr eat ing client is in t he STA or MTA, but not t he NTA; it m akes v ery fr equent m et hod calls; and t he calls hav e shor t durat ion. By using Both, you will av oid cr oss- apar t m ent m ar shaling, an overhead t hat m ay hinder perform ance under t his scenar io. I n all ot her cases, y our COM+ com ponent should use t he Neutral t hr eading m odel. You will need t o use act iv it y- based sy nchr onizat ion t o pr ovide sy nchr onizat ion t o your com ponent . You should avoid using t he Free t hr eading m odel for your com ponent because r unning in t he NTA will offer t he sam e t hr oughput wit hout t he addit ional t hread cont ext swit ch inv olv ed wit h calls int o t he MTA. Only legacy com ponent s im por t ed int o COM+ should use Free as t he t hreading m odel.
5 .9 Su m m a r y Act iv it y- based sy nchr onizat ion is a sim ple and elegant concurr ency m anagem ent serv ice t hat pr ov ides bot h an adm inist rat iv e suppor t and a st r aight forward pr ogr am m ing m odel. For m ost cases, if your design calls for using m ult it hreading, configure y our com ponent t o requir e synchronizat ion, and COM+ will do t he rest . That w ay, you can devot e your dev elopm ent effor t t o t he business pr oblem ( inst ead of t he synchronizat ion issues) , and t he r esult ing code is robust . COM+ synchronizat ion is alm ost a form al way of elim inat ing pot ent ially har d- t o- solv e synchronizat ion defect s. The first five chapt er s present t he basic COM+ com ponent serv ices: applicat ion act iv at ion, inst ance m anagem ent , t ransact ion suppor t , and concurr ency m anagem ent . The r est of t he chapt ers descr ibe higher- lev el COM+ serv ices ( securit y , queued com ponent s, and loosely coupled event s) . I call t hese ser vices " high lev el" because t hey all r ely and int eract wit h t he basic services. Befor e you learn t hese high- level services, you need t o be fam iliar wit h program m at ic configur at ion of COM+ services, t he subj ect of t he nex t chapt er.
152
Cha pt e r 6 . Pr ogr a m m ing t he COM + Ca t a log COM+ st or es t he inform at ion about y our applicat ions, your com ponent s’ configur at ion and physical locat ions, global m achine set t ings, and every ot her bit of dat a COM+ r equires t o operat e in a reposit or y called t he COM+ Cat alog. The Cat alog exposes COM+ int erfaces and com ponent s t hat allow you t o access t he infor m at ion it st ores. Any t hing y ou can do visually wit h t he Com ponent Ser vices Explorer , y ou can do program m at ically as well—from ex por t ing COM+ applicat ions t o doing fine- gr ained configur at ion such as enabling aut o- deact ivat ion on a m et hod. I n fact , t he Com ponent Serv ices Ex plorer and t he var ious w izar ds are m er ely handy user- int er face wr appers around t he Cat alog int er faces and obj ect s. This chapt er cover s t he COM+ Cat alog progr am m ing m odel and provides you wit h useful code sam ples you can use as a st art ing point for aut om at ing all t ask s of adm inist rat ing COM+ applicat ions and services.
6 .1 W h y Pr ogr a m t h e Ca t a log? Som e of t he m or e adv anced feat ur es of COM+ lack suppor t in t he Com ponent Services Explorer and are available only by configur ing your com ponent s progr am m at ically. These feat ures are largely t ied in wit h COM+ Ev ent s ( discussed in Chapt er 9) and include COM+ event s filt er ing and m anaging t ransient subscr ipt ions t o COM+ event s. Program m ing t he COM+ Cat alog gives y ou access t o m uch m or e t han advanced services. By learning t o pr ogr am t he Cat alog, y ou can pr ovide y our syst em adm inist r at or s wit h helper ut ilit ies t hat aut om at e t edious t asks. These helper s int er act w it h t he underlying Cat alog on t he adm inist rat or s' behalf, sav ing t hem t he t rouble of learning how t o use t he Com ponent Serv ices Explorer and present ing t hem wit h fam iliar t er m inology from t he applicat ion dom ain. A t y pical ex am ple is adding a new user t o t he syst em : y ou can creat e a ut ilit y t o pr ogr am m at ically add t he user t o an appr opr iat e role, wit hout r equiring t he adm inist rat or t o launch and int er act w it h t he Com ponent Ser vices Explor er ( role- based secur it y is discussed in Chapt er 7) . You can even creat e a ut ilit y t o enable your syst em adm inist r at or t o r em ot ely deploy , adm inist er , and configure y our product 's com ponent s and applicat ions on differ ent m achines ( by accessing t hose m achines' Cat alogs) while rem aining at his desk .
153
You can also capt ur e user input or deploy m ent - specific inform at ion during your applicat ion set up and fine- t une y our applicat ion configurat ion in t he Cat alog. The user sees j ust one inst allat ion process because all access t o t he Cat alog can be done program m at ically . Finally, dur ing your com ponent developm ent , you benefit gr eat ly fr om aut om at ing such t asks as st ar t ing and shut t ing down applicat ions. You will see an exam ple of t hat lat er in t he chapt er .
6 .2 Th e Ca t a log Pr ogr a m m ing M ode l The infor m at ion st or ed in t he Cat alog is st ruct ur ed sim ilarly t o it s lay out in t he Com ponent Ser vices Explorer. Dat a it em s in t he Cat alog are m or e or less where you w ould expect t o find t hem according t o t heir v isual represent at ion. I n gener al, folders in t he Com ponent Services Explorer ( such as applicat ions, roles, com ponent s, and int erfaces) correspond t o COM+ Cat alogcollect ions. A cat alog collect ion is a collect ion of it em s of som e unifor m kind. Every collect ion has a st r ing ident ifying it , called t he collect ion nam e. One ex am ple of a cat alog collect ion is t he Applications collect ion. The it em s in a collect ion are called cat alog obj ect s. You can add or rem ove cat alog obj ect s in a collect ion, j ust as you can add or rem ove it em s in a Com ponent Services Explorer folder. For exam ple, when y ou add a cat alog obj ect t o t he Applications collect ion, you are act ually adding a COM+ applicat ion. Every cat alog obj ect in a collect ion exposes propert ies t hat you can read or configure. The cat alog obj ect proper t ies ar e sim ilar or ident ical t o t he propert ies available on t he proper t ies page in t he Com ponent Services Explorer for t hat par t icular it em t ype. For exam ple, t he pr opert ies of a cat alog obj ect fr om t he Applications collect ions are COM+ applicat ion proper t ies—such as act iv at ion m ode ( server or librar y) or idle t im e m anagem ent t im eout s. Essent ially , all y ou ever do wit h t he COM+ Cat alog is locat e t he collect ion y ou are int erest ed in, it erat e over it s cat alog obj ect s, find t he obj ect you ar e look ing for, m odify it s pr opert ies, and save your changes. I n pr act ice, t he Cat alog's program m ing m odel is unifor m , whet her you it er at e ov er t he Applications collect ion or t he Components collect ion of a specific applicat ion. The Cat alog exposes a hierarchy of predefined collect ions and obj ect s, and you program against t hose collect ions and obj ect s. The Cat alog int erfaces are dual COM int er faces, which enables y ou t o call t hem fr om w it hin adm inist rat ion script s. Abst r act ed, t he Cat alog design pat t ern is depict ed in Figure 6- 1. Each cat alog collect ion m ay cont ain m any cat alog obj ect s. A collect ion's sole purpose is t o allow you t o it erat e over t he obj ect s it
154
cont ains. A collect ion has no proper t ies you can configur e, m uch lik e how a folder in t he Com ponent Services Explorer has no proper t ies. You only set t he proper t ies of cat alog obj ect s. Each cat alog obj ect has a set of proper t ies and m et hods you can inv oke. Each cat alog obj ect can also give y ou access t o ot her collect ions associat ed wit h it . For ex am ple, in t he Applications collect ion, every applicat ion obj ect has a Components collect ion associat ed wit h it , analogous t o t he Com ponent s folder under ev ery applicat ion in t he Com ponent Ser vices Explor er. As you can see in Figure 6- 1, t he Cat alog has a r oot obj ect . The root is special k ind of a cat alog obj ect , and t he Cat alog has only one root obj ect . The root obj ect also has proper t ies and m et hods you can call. The root obj ect giv es you access t o t op- level collect ions such as t he Applications collect ion. The root obj ect is your gat eway t o t he COM+ Cat alog and is available as a COM obj ect . Figu r e 6 - 1 . Th e COM + Ca t a log d esign pa t t e r n
All t hree obj ect t ypes ( collect ion, obj ect , and root ) support t hr ee differ ent int erfaces. Ev ery cat alog collect ion support s t he ICatalogCollection int er face, and ev ery cat alog obj ect suppor t s t he ICatalogObject int erface. The ICatalogCollection int erface is designed t o it erat e over a collect ion of ICatalogObject int er face point ers. The ICatalogObject allows y ou t o access t he obj ect ’s proper t ies by r eferring t o each pr opert y by a pr edet erm ined nam e ( an ident ify ing st r ing) . I n addit ion, each cat alog obj ect has a k ey t hat y ou use t o get t he collect ions associat ed wit h t hat cat alog obj ect . The Cat alog r oot support s a t hir d int erface called ICOMAdminCatalog, wit h special r oot - level m et hods and proper t ies. The ICOMAdminCatalog int er face let s y ou access t he t op- level collect ions. When accessing t he t op- level collect ions, t here is no need for a key because t her e is only one r oot obj ect . The goal of t his design pat t er n is t o have an ex t r em ely ext ensible program m ing m odel. Because all collect ions and obj ect s suppor t t he sam e int erfaces, regardless of t he act ual collect ion or obj ect , t hey are all accessed and m anipulat ed t he sam e way . I f in t he fut ur e t here is a need t o define new collect ions ( such as new ser vices in fut ure v ersions of COM+ ) , t he sam e st r uct ur e and program m ing 155
m odel would be able t o define and use t he new collect ions and cat alog obj ect s.
6 .3 Ca t a log St r uct u r e This sect ion discusses t he Cat alog st ruct ur e and t he nam es of t he it em s in it , not t he sem ant ics of t hese it em s. Som e of t hese it em s hav e already been covered in t he previous chapt er s, and som e ar e covered in subsequent chapt er s. The COM+ Cat alog’s act ual st ruct ur e, from t he root down t o t he com ponent level, is m apped out in Figur e 6- 2. Each collect ion has a predefined ident ify ing nam e, wher eas cat alog obj ect s’ nam es ar e defined by t he user . The root of t he Cat alog giv es you access t o t op- level collect ions such as t he Applications and TransientSubscription collect ions ( see Chapt er 9) . You can also access less useful collect ions such as t he com m unicat ion prot ocols used by DCOM or all of t he in- proc ser vers ( COM obj ect s in a DLL) inst alled on t he m achine. Anot her t op- level collect ion shown in Figure 6- 2 is t he ComputerList collect ion—a list of all t he com put ers t hat t he Com ponent Services Explor er is configured t o m anage. Figu re 6 - 2 . Th e COM + Ca t alog st ru ct u re , fr om t h e r oot d ow n t o t h e com p on en t le vel
The Applications collect ion, as t he nam e im plies, cont ains all t he COM+ applicat ions inst alled on t he m achine. A cat alog obj ect in t he Applications collect ion allows y ou t o set t he pr opert ies of a par t icular COM+ applicat ion. I t also giv es you access t o t wo ot her
156
collect ions: t he Roles and t he Components collect ions. As m ent ioned previously , every folder in t he Com ponent Ser vices Explorer corresponds t o a cat alog collect ion. Just as every applicat ion in t he Com ponent Services Explorer has a Roles and Com ponent s subfolder , a cat alog obj ect r epresent ing an applicat ion can give y ou access t o t hese t wo collect ions. The Roles collect ion cont ains a cat alog obj ect for each role defined in t he applicat ion. Chapt er 7 discusses role- based secur it y at lengt h. Ever y cat alog obj ect in t he Roles collect ion let s y ou set it s proper t ies ( such as t he role nam e and descr ipt ion) and giv e y ou access t o a collect ion of user s associat ed wit h t hat role, called t he UsersInRole collect ion. Ever y cat alog obj ect in t he UsersInRole collect ion r epr esent s a user t hat was added t o t hat r ole. As y ou can see in Figure 6- 2, t he obj ect s in t he UsersInRole collect ion do not hav e any collect ions associat ed wit h t hem . The Components collect ion cont ains a cat alog obj ect for each com ponent in t he applicat ion. You can program m at ically configur e all t he proper t ies available on t he pr opert ies page of a com ponent in t he Com ponent Ser vices Explor er. Ev ery com ponent cat alog obj ect can give y ou access t o t hr ee collect ions: t he InterfacesForComponent collect ion, t he SubscriptionForComponent collect ion, and t he RolesForComponent collect ion ( see Figur e 6- 3) . Figu r e 6 - 3 . Eve ry com pon e n t ca t alog obj e ct h a s a n ela b or at e st ru ct u r e u n de r it
The InterfacesForComponent collect ion cont ains a cat alog obj ect for every int erface t he com ponent suppor t s. Every int er face cat alog
157
obj ect gives you access t o it s pr opert ies and t o t wo collect ions—one is called t he RolesForInterface collect ion, used t o it er at e over t he roles t hat wer e gr ant ed access for t his int erface, and t he second collect ion is t he MethodsForInterface collect ion. The MethodsForInterface collect ion cont ains a cat alog obj ect for each m et hod on t hat int er face. Each m et hod cat alog obj ect can give you access t o it s propert ies and t o t he roles associat ed wit h t hat m et hod, in a collect ion called RolesForMethod. Going back t o t he collect ions accessible from every com ponent cat alog obj ect , t he RolesForComponent collect ion let s you access t he roles associat ed wit h t hat com ponent , and t he SubscriptionsForComponent collect ion cont ains a cat alog obj ect per a subscript ion t o a COM+ Event ( discussed in Chapt er 9) . Ev ery subscript ion obj ect is associat ed wit h t w o collect ions—t he PublisherProperties and t he SubscriberProperties collect ion. The only role obj ect s t hat have collect ions of users associat ed wit h t hem ar e in t he Roles collect ion accessible from every applicat ion obj ect ( see Figur e 6- 2) . The com ponent , int er face, and m et hod lev el r ole obj ect s do not hav e user collect ions associat ed wit h t hem ( see Figure 6- 3) . One m ore bit of COM+ Cat alog t riv ia—every cat alog obj ect always has at least t hr ee collect ions associat ed wit h it : t he RelatedCollectionInfo, PropertyInfo, and ErrorInfo collect ions. These collect ions wer e om it t ed fr om Figure 6- 2 and Figure 6- 3 for t he sak e of clarit y . The RelatedCollectionInfo collect ion is used for adv anced it er at ions over t he Cat alog, allow ing you t o wr it e gener ic recursive it erat ion code t hat discov er s at runt im e which collect ions a part icular cat alog obj ect is associat ed wit h. The PropertyInfo collect ion is used t o r et rieve inform at ion about t he proper t ies t hat a specified collect ion support s. The ErrorInfo collect ion can pr ovide ext ensive err or infor m at ion for dealing wit h er ror s in m et hods t hat updat e m or e t han one cat alog obj ect at once, so you can find out exact ly which obj ect caused t he err or . This chapt er does not discuss t hese t hr ee adv anced collect ions. When program m ing against t he COM+ Cat alog st r uct ure, y ou need not m em orize t he Cat alog int r icat e st ruct ure. You can j ust follow t he int uit iv e st ruct ure of t he Com ponent Serv ices Explorer and sim ply pr ovide t he corr ect collect ion nam e, while using Figur es 6- 2 and 6- 3 as reference navigat ion m aps.
158
6 .4 I n t er a ct in g w it h t h e Ca t a log Besides underst anding t he Cat alog physical st ruct ure, you need t o be fam iliar wit h how t o int eract wit h t he t hree Cat alog int erfaces and obj ect t ypes ( r oot , collect ion, and obj ect ) . This sect ion will walk you t hr ough a few pr ogr am m ing exam ples and dem onst r at e m ost of what you need t o know when program m ing t he Cat alog. 6 .4 .1 Th e Ca t a log Root Obj e ct The st ar t ing point for everyt hing you do w it h t he Cat alog is t he r oot obj ect . You cr eat e t he root obj ect wit h t he class I D of CLSID_COMAdminCatalog ( or t he prog- I D of COMAdmin.COMAdminCatalog) and obt ain an int er face point er t o t he ICOMAdminCatalogint erface. You use t he ICOMAdminCatalogint erface point er t o eit her invoke r oot - level m et hods or access one of t he t op- level collect ions by calling t he GetCollection( ) m et hod, defined as: [id(1)] HRESULT GetCollection([in]BSTR bstrCollectionName, [out,retval]IDispatch** ppCatalogCollection); You can use ICOMAdminCatalog::GetCollection( ) t o access only t he t op- level collect ions ( such as Applications) show n in Figure 62. Accessing lower level collect ions is done different ly, and you will see how shor t ly. GetCollection( ) ret urns an ICatalogCollection point er t o t he specified collect ion. Once you get t he collect ion you want , you can release t he r oot obj ect. Exam ple 6- 1 shows how t o access t he Applications collect ion by cr eat ing t he r oot obj ect and calling ICOMAdminCatalog::GetCollection( ). Ex a m p le 6 - 1 . Acce ssin g a t op- le ve l colle ct ion su ch a s Ap plica t ion s
HRESULT hres = S_OK; ICOMAdminCatalog* pCatalogRoot = NULL; ICatalogCollection* pApplicationCollection = NULL; hres = ::CoCreateInstance(CLSID_COMAdminCatalog,NULL,CLSCTX_ALL, IID_ICOMAdminCatalog,(void**)&pCatalogRoot); hres = pCatalogRoot>GetCollection(_bstr_t("Applications"), (IDispatch**)&pApplicationCollection);
159
pCatalogRoot->Release( more
); //You don’t need the root any
/* use pApplicationCollection */ Lat er, you will see ot her uses for t he ICOMAdminCatalog int er face besides j ust accessing a t op- level collect ion. 6 .4 .2 Th e I Ca t a logColle ct ion I nt e r fa ce Every collect ion in t he COM+ Cat alog im plem ent s t he ICatalogCollection int er face. As m ent ioned previously , t he ICatalogCollection int er face is used t o it er at e ov er a collect ion of cat alog obj ect s. The ICatalogCollection int er face suppor ts several m et hods and propert ies. The m ain m et hods it support s ar e Populate( ), Add( ), Remove( ), SaveChanges( ), and GetCollection( ). The m ain proper t ies ar e Count and Item. Aft er obt aining a collect ion int er face ( be it a t op- lev el or a lowerlev el collect ion) , t he first t hing you need t o do is call t he Populate( ) m et hod. The Populate( ) m et hod reads t he inform at ion fr om t he Cat alog int o t he collect ion obj ect y ou are holding, populat ing t he collect ion w it h dat a for all t he it em s cont ained in t he collect ion. I f you want t o change t he collect ion by adding or rem ov ing a cat alog obj ect , use t he Add( ) or Remove( ) m et hods. The Add( ) m et hod is defined as: [id(2)] HRESULT Add([in]IDispatch* pCatalogObject); I t accept s j ust one param et er —a point er t o t he cat alog obj ect y ou wish t o add t o t he collect ion. The Count pr opert y r et urns t he num ber of obj ect s in t he collect ion and m ust be prefixed by a get_ when accessed from C+ + ( t her e are plent y of ex am ples lat er in t he chapt er) . The Item propert y is defined as: [id(1),propget] HRESULT Item([in] long lIndex, [out,retval]IDispatch** ppCatalogObject); This pr opert y r et urns a point er t o a cat alog obj ect , given it s index . Collect ion indexes ar e zer o- based, not one- based, m eaning t he first elem ent has index zero and t he last has index count-1. You can now wr it e a for loop t hat it erat es over t he ent ire collect ion, ret riev ing one it em at a t im e. Once you hav e a point er t o a cat alog obj ect , you can read and change it s nam ed proper t ies. The Remove( ) m et hod is defined as: [id(3)] HRESULT Remove(long lIndex); I t accept s an index in t he collect ion ident ify ing t he obj ect you wish t o r em ove. What ev er change you m ak e t o t he collect ion ( adding or r em oving obj ect s or m odifying obj ect pr opert ies) will not t ake effect unless you call t he SaveChanges( ) m et hod. I t is a com m on pit fall t o wr it e
160
code t hat it erat es cor rect ly over a collect ion, m odifies it , and releases all t he obj ect s pr operly—but for get s t o call SaveChanges( ). Next t im e your Cat alog adm inist rat ion code ex ecut es and no apparent change has t ak en place, go back and m ake sure you called SaveChanges( ). Finally, t he GetCollection( ) m et hod is defined as: [id(4] HRESULT GetCollection([in] BSTR bstrCollectionName, [in] VARIANT varObjectKey), [out,retval]IDispatch** ppCollection); This m et hod is used t o ret r iev e a cat alog collect ion associat ed wit h a par t icular cat alog obj ect . As explained pr ev iously, a cat alog obj ect can have cat alog collect ions associat ed wit h it ( see Figures 6- 2 and 6- 3) . The cat alog obj ect int er face has no m eans for prov iding t hose collect ions; y ou get t hem by calling GetCollection( ) on t he collect ion cont aining t he obj ect . GetCollection( ) accept s a k ey value as a par am et er, so t hat it can ident ify t he obj ect whose collect ion y ou wish t o access. Not e t hat ICOMAdminCatalog::GetCollection( ) did not require a key because t he t op- level collect ions ar e already nam ed uniquely. I n t he case of a lower level collect ion, m any obj ect s will hav e collect ions associat ed wit h t hem , all nam ed t he sam e. For exam ple, if you it erat e over t he Applications collect ion, you will find t hat each it em ( a cat alog obj ect ) is an applicat ion and each of t hem has a Components collect ion. I f y ou want t o access t he Components collect ion of a par t icular applicat ion, you need t o call ICatalogCollection::GetCollection( ) on t he Applications collect ion int erface, passing in t he k ey t o t he par t icular applicat ion whose Components collect ion you wish t o access. 6 .4 .3 Th e I Ca t a logObj e ct I n t e r fa ce Every cat alog obj ect support s t he ICatalogObject int erface, allowing you t o configure t he obj ect 's propert ies. All cat alog obj ect s suppor t t hr ee predefined r ead- only propert ies: Key, Name, and Valid, defined as: [id(2),propget] HRESULT Key([out,retval]VARIANT* pvarKey); [id(3),propget] HRESULT Name([out,retval]VARIANT* pvarName); [id(5),propget] HRESULT Valid([out,retval]VARIANT_BOOL* pbValid); The Name propert y cont ains t he nam e of t he obj ect . For ex am ple, if t he obj ect is a COM+ applicat ion, t he nam e will be t he applicat ion's nam e. The Valid pr opert y ret urns TRUE if t he obj ect was read successfully from t he COM+ Cat alog when it s cont aining collect ion
161
was populat ed. The Key proper t y ret ur ns a unique key ident ifying t his obj ect , used t o access all t he collect ions associat ed w it h t hat obj ect . I n addit ion, all cat alog obj ect s support , accor ding t o t heir specific t y pe, nam ed v alue pr opert ies. These propert ies ar e accessible v ia one r ead- wr it e pr opert y called t he Value proper t y, defined as: [propget, id(1)] HRESULT Value([in]BSTR bstrPropName, [out,retval]VARIANT* pvarValue); [propput, id(1)] HRESULT Value([in]BSTR bstrPropName,[in]VARIANT varNewValue); Each cat alog obj ect ( applicat ion, com ponent ) has a predefined set of nam ed propert ies and pr edefined enum values for t hose proper t ies. For ex am ple, every cat alog obj ect in t he Applications collect ion represent s a COM+ applicat ion and has a nam ed value proper t y called Activation t hat cont rols w het her t he applicat ion should be act ivat ed as a libr ar y or serv er applicat ion. The predefined enum values for t he Activation proper t y ar e COMAdminActivationInproc and COMAdminActivationLocal. The ICatalogObject int er face also suppor t s t wo not - so- useful helper m et hods, IsPropertyReadOnly( ) and IsPropertyWriteOnly( ), int ended t o be used dur ing generic it erat ion, when you do not k now t he exact behav ior of a proper t y you are accessing. 6 .4 .4 Usin g t he Ca t a log I n t e r f a ces You have probably had as m uch dry t heory as y ou can t ake, and an exam ple can go a long way t o dem onst rat e t he point . Ex am ple 6- 2 shows m any of t he point s covered so far in t his chapt er. Suppose you want t o program m at ically set a COM+ applicat ion ( called MyApp) t o be a libr ar y COM+ applicat ion. Ex am ple 6- 2 uses Visual Basic t o it er at e over t he Applications collect ion, looking for t he MyApp COM+ applicat ion, and set s it s act ivat ion m ode t o a librar y applicat ion. Ex a m p le 6 - 2 . V isu al Ba sic ex a m p le of findin g a n applica t ion a nd se t t ing it s a ct iva t ion m ode
Dim Dim Dim Dim Dim
catalog As ICOMAdminCatalog applicationCollection As ICatalogCollection applicationCount As Long i As Integer ’Application index application As ICatalogObject
Set catalog = New COMAdminCatalog
162
Set applicationCollection = catalog.GetCollection("Applications") Set catalog = Nothing ’You don’t need the root any more
’Read the information from the catalog Call applicationCollection.Populate applicationCount = applicationCollection.Count(
)
For i = 0 To applicationCount - 1 ’Get the current application Set application = applicationCollection.Item(i) If application.Name = "MyApp" Then application.Value("Activation") = COMAdminActivationInproc applicationCollection.SaveChanges End If Set application = Nothing i = i + 1 Next i Set applicationCollection = Nothing Fir st , creat e a Cat alog root obj ect , t he catalogRoot obj ect . Then inv oke it s GetCollection( ) m et hod, asking for an ICatalogCollection int er face point er t o t he Applications collect ion. Next , release t he r oot obj ect, because it is no longer needed. Then populat e t he applicat ion collect ion obj ect and find out how m any applicat ions y ou have ( t he Count pr opert y) . The for loop it erat es over t he applicat ions and get s one applicat ion at a t im e, in t he form of an ICatalogObject obj ect , using t he collect ion’s Item proper t y . You t hen check if t he cat alog obj ect ’s nam e is MyApp. I f it is, set it s Activation nam ed pr opert y t o t he predefined enum v alue of COMAdminActivationInproc. Aft er m aking t he change t o t he applicat ion obj ect , call SaveChanges( ) on t he Applications collect ion obj ect t o save t he change. Exam ple 6- 3 does t he sam e t hing as Exam ple 6- 2, ex cept it is writ t en in C+ + inst ead of Visual Basic. Ex a m p le 6 - 3 . C+ + e x a m ple of fin din g an applica t ion a nd se t t ing it s act iva t ion m od e
HRESULT hres = S_OK; ICOMAdminCatalog* pCatalog = NULL; ICatalogCollection* pApplicationCollection = NULL; long nApplicationCount = 0; int i = 0; //Application index hres = ::CoCreateInstance(CLSID_COMAdminCatalog,NULL,CLSCTX_ALL,
163
IID_ICOMAdminCatalog,(void**)&pCatalog); hres = pCatalog->GetCollection(_bstr_t("Applications"), (IDispatch**)&pApplicationCollection); pCatalog->Release( ); //You don’t need the root any more hres = pApplicationCollection->Populate( information from the catalog hres = pApplicationCollection>get_Count(&nApplicationCount);
); //Read the
for(i=0;iget_Item(i,(IDispatch**)&pApplication); _variant_t varAppName; _variant_t varActivation((bool)COMAdminActivationInproc); hres = pApplication->get_Name(&varAppName); if(_bstr_t("MyApp") == _bstr_t(varAppName)) { long ret = 0; hres = pApplication>put_Value(_bstr_t("Activation"),varActivation); hres = pApplicationCollection->SaveChanges(&ret); } pApplication->Release( ); } pApplicationCollection->Release( ); A v alid quest ion y ou ar e probably ask ing is, " How do I k now what t he predefined nam ed pr opert ies and enum values are for t he proper t y I want t o configur e?" The answer is sim ple: t he Plat for m SDK docum ent at ion ( av ailable in t he MSDN Library, under Com ponent Services/ COM+ ( Com ponent Serv ices) / Refer ence/ COM+ Adm inist r at ion Refer ence) cont ains a com pr ehensive list of every nam ed propert y and it s corr esponding enum values ( or data t y pe and r ange, if applicable) . Anot her point wort h dem onst rat ing wit h an exam ple is using t he Key proper t y of a cat alog obj ect t o access a r elat ed collect ion. Suppose you would like t o pr int t o t he t race window all t he com ponent s in all t he applicat ions. You would use t he Key pr opert y of every COM+ applicat ion t o access it s Components collect ion. Exam ple 6- 4 shows t he TraceTree( ) m et hod t hat it er at es ov er t he Applications collect ion, calling t he TraceComponents( ) m et hod t o it erat e over an applicat ion com ponent collect ion.
164
Ex a m p le 6 - 4 . Tr a cin g a ll t h e com p on en t s in e ve r y COM + a pp lica t ion
#include "COMadmin.h" void TraceTree( ) { HRESULT hres = S_OK; ICOMAdminCatalog* pCatalog = NULL; ICatalogCollection* pApplicationCollection = NULL; long nApplicationCount = 0; hres = ::CoCreateInstance(CLSID_COMAdminCatalog,NULL,CLSCTX_ALL, IID_ICOMAdminCatalog,(void**)&pCatalog); hres = pCatalog>GetCollection(_bstr_t("Applications"), (IDispatch**)&pApplicationCollection); pCatalog->Release( ); //You don’t need the root any more //Read the information from the catalog hres = pApplicationCollection->Populate( hres = pApplicationCollection>get_Count(&nApplicationCount);
);
//Iterate over the Applications collection for(int i=0;iget_Item(i,(IDispatch**)&pApplication); hres = pApplication->get_Name(&varAppName); TRACE("The components in application \"%s\" are: \n", (char*)(_bstr_t(varAppName)); TraceComponents(pApplicationCollection,pApplication); pApplication->Release( ); } pApplicationCollection->Release( ); } void TraceComponents(ICatalogCollection* pApplicationCollection,
165
ICatalogObject* pApplication) { HRESULT hres = S_OK; ICatalogCollection* pComponentCollection = NULL; long nComponentCount = 0; _variant_t varAppKey; //Get the Component collection for this application. Need the key first hres = pApplication->get_Key(&varAppKey); hres = pApplicationCollection>GetCollection(_bstr_t("Components"), varAppKey,(IDispatch**)&pComponentCollection); //Read the information from the catalog hres = pComponentCollection->Populate( ); hres = pComponentCollection>get_Count(&nComponentCount);
for(int j=0;jget_Item(j,(IDispatch**)&pComponent); hres = pComponent->get_Name(&varCompName); //Ugly, but works: TRACE(" %d. %s \n" ,j+1,(char*)(_bstr_t(varCompName)); pComponent->Release( ); } pComponentCollection->Release( ); } The out put fr om Exam ple 6- 4 should look sim ilar t o t his ( depending, of cour se, on t he applicat ions inst alled on your m achine) : The components in application "COM+ Utilities" are: 1. TxCTx.TransactionContext 2. TxCTx.TransactionContextEx 3. RemoteHelper.RemoteHelper 4. QC.Recorder.1 5. QC.ListenerHelper.1 The components in application "MyApp" are: 1. MyApp.MyComponent.1 2. MyObj2.MyObj2.1 3. Subscriber.MyEvent.1 4. EventClass.MyEvent.1 The components in application "COM+ QC Dead Letter Queue Listener" are: 166
1. QC.DLQListener.1 The components in application "Logbook" are: 1. LogBootEvent.LogbookEventClass.1 2. LogBook.ComLogHTML.1 3. LogBook.COMLogXML.1 The components in application "System Application" are: 1. Mts.MtsGrp.1 2. COMSVCS.TrackerServer 3. EventPublisher.EventPublisher.1 4. Catsrv.CatalogServer.1 The first par t of Ex am ple 6- 4, t he TraceTree( ) m et hod, cr eat es t he root obj ect , get s t he t op- level Applications collect ion, populat es it , and ret r ieves t he num ber of applicat ions ( using t he Count proper t y) . I t t hen it er at es ov er t he Applications collect ion, get t ing one cat alog obj ect at a t im e, t racing it s nam e, and passing it t o t he TraceComponents( ) m et hod. The TraceComponents( ) t r aces out all t he com ponent s associat ed wit h t hat applicat ion. Not e t hat it is not sufficient t o pass t o t he TraceComponents( ) m et hod j ust t he applicat ion cat alog obj ect . You hav e t o pass in as a par am et er t he Applications collect ion as well. Recall t hat w hen you want t o access a Collect ion 2 associat ed wit h Obj ect 1 ( cont ained in Collect ion 1) , you get Collect ion 2 fr om Collect ion 1, which cont ains Obj ect 1. This is why TraceComponents( ) accept s pApplicationCollection as a par am et er: void TraceComponents(ICatalogCollection* pApplicationCollection, ICatalogObject* pApplication) TraceComponents( ) t hen calls get_Key( ) on t he applicat ion cat alog obj ect passed in and, using t hat key , accesses t he applicat ion obj ect ’s Components collect ion. Next , TraceComponents( ) populat es t he Components collect ion, get s it s count , and it erat es over it , t r acing one com ponent nam e at a t im e. When wr it ing code as in Ex am ple 6- 4, which it er at es ov er collect ions and nest ed collect ions, it is very im port ant t o nam e your var iables correct ly t o m ake y our code readable. ICatalogCollection* pCollection is a poor variable nam e, but ICatalogCollection* pApplicationCollection is a m eaningful and r eadable nam e t hat convey s exact ly which collect ion it is point ing t o. Now y ou should be get t ing t he feel of how t r uly generic and ext ensible t he COM+ Cat alog pr ogr am m ing m odel really is. The sam e ICatalogCollection int erface is used t o it erat e ov er every collect ion, and t he sam e ICatalogObject int erface is used t o configure and access all t he par am et ers in t he Cat alog, be it an applicat ion- or a m et hod- lev el propert y . 6 .4 .5 Sa vin g Ch a n ge s
167
When y ou m ake changes t o a collect ion ( adding or rem ov ing cat alog obj ect s) or t o obj ect s in it ( configuring propert ies) , y ou have t o call ICatalogCollection::SaveChanges( ) t o com m it t hem . You can also discar d changes you m ade t o a collect ion, but did not com m it yet , by calling Populate( ) again. When y ou call ICatalogCollection::SaveChanges( ), all obj ect s and all propert ies on all t he obj ect s are writ t en t o t he Cat alog at once, as an at om ic oper at ion. The only problem wit h t his program m ing m odel is t hat t he Cat alog pr esent s a last - wr it er- wins behavior—t he obj ect is sav ed in t he Cat alog precisely t he way t he last writ er configur ed it . This m eans t hat t her e is a pot ent ial for conflict s and cont ent ions bet ween t wo applicat ions t hat m odify t he sam e dat a set , because neit her has a lock on t he it em s in t he Cat alog. 6 .4 .6 Obj e ct Pr op e r t ie s I n t e r de pe n de n cie s Som et im es, a part icular v alue of a cat alog obj ect nam ed propert y depends on t he values of ot her nam ed proper t ies. For exam ple, when t he Transaction nam ed pr opert y of a com ponent is set t o t he value of COMAdminTransactionRequired or COMAdminTransactionRequiresNew, t he v alue of t he JustInTimeActivation nam ed proper t y m ust be set t o TRUE. This is no sur prise because all t ransact ional com ponent s r equir e JI TA t o be t urned on ( as well as r equiring synchronizat ion) . The COM+ Cat alog is awar e of all t he proper t ies' int erdependencies and will enfor ce consist ency whenev er it deem s it fit . I f you t ry t o set a nam ed pr opert y in a way t hat conflict s wit h anot her , an er ror will occur. For exam ple, if you t r y t o t urn JI TA off on a t r ansact ional com ponent ( by set t ing it t o FALSE) , SaveChanges( ) will fail. One effect of hav ing a sm ar t Cat alog is t hat som e pr opert ies m ight be changed for you w it hout you explicit ly set t ing t hem . For exam ple, if you set t he Transaction nam ed pr opert y t o t he v alue of COMAdminTransactionRequired, t he Cat alog t ur ns JI TA on and set s t he value of t he Synchronization proper t y t o COMAdminSynchronizationRequired.
6 .5 Fea t u r es of COM Adm in Ca t a log Ther e is m ore t o t he Cat alog root obj ect t han pr ov iding you wit h access t o t he t op- level collect ions. The ICOMAdminCatalogint er face suppor t s 22 m et hods, pr ov iding you wit h m any useful feat ures t hat allow you t o: •
Connect t o t he Cat alog root obj ect on a rem ot e m achine
168
• • • • • • • • •
I nst all a new COM+ applicat ion Export an exist ing COM+ applicat ion St art or shut down a COM+ applicat ion I nst all com ponent s int o COM+ applicat ions Obt ain infor m at ion r egarding event classes St art , st op, or refr esh load balancing rout ing ( load balancing is not av ailable in st andar d inst allat ions of COM+ ) Check t he st at us of a COM+ service ( cur rent ly, only load balancing) Back up t he COM+ Cat alog infor m at ion t o a specific file Rest ore t he Cat alog from a specific file
For ex am ple, you oft en need t o program m at ically adm inist er a COM+ Cat alog on a r em ot e m achine, dur ing deploy m ent or for aut om at ing rem ot e adm inist rat ion of ser vers. To do so, you would use t he ICOMAdminCatalog::Connect( ) m et hod, defined as: [id(2)] HRESULT Connect([in]BSTR bstrMachineName, [out,retval]IDispatch** pRemoteRootCollection) The first par am et er t o Connect( ) is t he rem ot e m achine nam e, and t he second is an out param et er —a point er t o a root collect ion on t he r em ot e m achine. Aft er calling Connect( ), t he ICOMAdminCatalog you are holding st art s affect ing t he rem ot e m achine t o which you have connect ed—calls m ade on it s m et hods adm inist er t he rem ot e m achine. You can also use t he pRemoteRootCollection par am et er t o gain access t o r em ot e t oplev el collect ions, as shown in Ex am ple 6- 5. Ex a m p le 6 - 5 . Acce ssin g a t op- le ve l cat a log collect ion on a re m ot e m a ch in e
HRESULT hres = S_OK; ICOMAdminCatalog* pCatalog = NULL; ICatalogCollection* pRemoteAppCollection = NULL; ICatalogCollection* pRemoteRootCollection = NULL; //Creating a local catalog hres = ::CoCreateInstance(CLSID_COMAdminCatalog,NULL,CLSCTX_ALL, IID_ICOMAdminCatalog,(void**)&pCatalog); //Connecting to the remote machine hres = pCatalog->Connect(_bstr_t("RemoteMachineName"), (IDispatch**)&pRemoteRootCollection); pCatalog->Release(
);///No need for it anymore
169
_variant_t varKey("");//Key value will be ignored //Getting the "Applications" collection on the remote machine hres = pRemoteRootCollection>GetCollection(_bstr_t("Applications"),varKey, (IDispatch**)&pRemoteAppCollection); pRemoteRootCollection->Release( remote root collection anymore
);//No need for the
/* use pRemoteAppCollection */ pRemoteAppCollection->Release( ); Anot her exam ple of what y ou can do wit h t he r oot obj ect is shut t ing down and st art ing up COM+ applicat ions. The ICOMAdminCatalog int er face support s t he StartApplication( ) and ShutdownApplication( ) m et hods, defined as: [id(16)] HRESULT StartApplication(BSTR strAppName); [id(8)] HRESULT ShutdownApplication(BSTR strAppName); St art ing up an applicat ion program m at ically is helpful in t he case of queued com ponent s ( you will see why in Chapt er 8) , and shut t ing down COM+ applicat ions is ex t r em ely useful during dev elopm ent . When y ou are doing a t est - debug- fix- build- r et est cy cle, you oft en discover a problem t hat you can fix on t he spot . However , you cannot r ebuild y our com ponent s as long as t he applicat ion t hat host s t hem is running because t he applicat ion m aint ains a lock on t he DLL. A COM+ applicat ion m ay be running even when idle ( t he default is t hree m inut es) , so you hav e t o shut down t he applicat ion using t he Com ponent Ser vices Explorer. Aft er a while, t his becom es ver y annoy ing. The sit uat ion is even worse if you hav e a num ber of int er act ing COM+ applicat ions and y ou have t o shut t hem all down—for ex am ple if you want t o change a header file, a lib, or a com ponent t hey all use.
Re plica t ing t he COM + Ca t a log I f your pr oduct consist s of m ore t han one COM+ applicat ion, you m ay want t o act ually clone t he ent ire COM+ Cat alog on a m achine where t he product is inst alled and use t he clone as an inst allat ion. COM+ allows y ou t o r eplicat e all COM+ set t ings from a giv ing source com put er t o one or m ore t arget com put er s, using a ut ilit y called COMREPL. COMREPL is t y pically used t o replicat e a m ast er configur at ion and deploy it on a set of ident ically configured com put ers. Anot her pot ent ial use for COMREPL is for product configur at ion m anagem ent pur poses.
170
COMREPL is a crude com m and line- driven ut ilit y : COMREPL All COM+ applicat ions on t he m ast er com put er ar e replicat ed t o t he t arget com put er s, except t he COM+ preinst alled applicat ions. I n addit ion, all COM+ applicat ions pr eviously inst alled on t he t arget com put er s will be delet ed as part of t he replicat ion process. So how about building a ut ilit y t hat uses ICOMAdminCatalog::ShutdownApplication( ) t o shut down t he applicat ion specified on t he com m and line—or all of t he COM+ applicat ions on y our m achine, if no applicat ion nam e was specified? I call t his ut ilit y Nuk e'm , and I even have a special icon on m y Visual St udio t oolbar t hat I click before every build, j ust t o purge all t he running applicat ions from m y m achine and st ar t a fr esh build and t est cycle. Nuk e'm cont ains a light C+ + wr apper class around t he ICOMAdminCatalog int erface, called CCatalogAdmin. Exam ple 66 shows it s Shutdown( ) m et hod, which shut s down t he specified applicat ion and, if none is specified, shut s dow n all t he COM+ applicat ions. Ex a m p le 6 - 6 . Th e CCa t a logAd m in ::Sh u t D ow n ( ) m e t h od
HRESULT CCatalogAdmin::ShutDown(BSTR bstrAppName) { //m_pCatalog is a member of the class, initialized in the constructor if(_bstr_t(bstrAppName) != _bstr_t("")) { return m_pCatalog>ShutdownApplication(bstrAppName); } else//Shut down all the applications { HRESULT hres = S_OK; ICatalogObject* pApplication = NULL; ICatalogCollection* pApplicationCollection = NULL; long nApplicationCount = 0; int i = 0;//Application index //Get the application collection hres = m_pCatalog>GetCollection(_bstr_t("Applications"), (IDispatch**)&pApplicationCollection); hres = pApplicationCollection->Populate( hres = pApplicationCollection>get_Count(&nApplicationCount);
);
171
for(i=0;iget_Item(i, (IDispatch**)&pDispTemp); _variant_t varName; hres = pApplication->get_Name(&varName); _bstr_t bstrName(varName); //No point in killing the system app, //since it will start up again immediately if(bstrName != _bstr_t("System Application")) { hres = m_pCatalog>ShutdownApplication(bstrName); } pApplication->Release( ); } pApplicationCollection->Release( ); return hres; } } The Nuk e’m ut ilit y is available from t his book ’s web sit e, ht t p: / / www.or eilly .com / cat alog/ com dot net svs/ .
6 .6 Th e COM + Ca t a log a n d Tr a nsa ct ion s The COM+ Cat alog is a resour ce m anager. When a com ponent t hat t akes part in a t r ansact ion t r ies t o access t he Cat alog, t he Cat alog aut o- enlist s in t hat t ransact ion. As a r esult , all t he configur at ion changes m ade wit hin t he scope of t hat t ransact ion will be com m it t ed or abort ed as one at om ic oper at ion, even acr oss m ult iple cat alogs on m ult iple m achines, accor ding t o t he t r ansact ion success. The m ain advant age of hav ing t he COM+ Cat alog t ake par t in your t r ansact ions is t hat it enor m ously sim plifies deploym ent on m ult iple m achines. I m agine a sit uat ion in w hich y ou writ e an elaborat e inst allat ion scr ipt t hat t r ies t o access and inst all y our product on m ult iple m achines. The problem is t hat alm ost anyt hing in a dist ribut ed inst allat ion scenario can go wrong—from net work failures t o securit y t o disk space. Because all t he inst allat ion at t em pt s are scoped under one t r ansact ion, you can guarant ee t hat all serv er m achines ar e left wit h ident ical configur at ions—eit her t he inst allat ion succeeded on all of t hem , or t he changes wer e rolled back and t he servers are left j ust as t hey wer e before you t r ied t o inst all t he product .
172
Anot her benefit of hav ing t he Cat alog as a r esource m anager is dealing wit h pot ent ial cont ent ions and conflict s bet ween t wo differ ent applicat ions t hat t ry t o access and m odify t he Cat alog at t he sam e t im e. To ensure t he t ransact ion’s isolat ion, when one t r ansact ion m ak es a change t o t he Cat alog, t he Cat alog will block all writ er s from ot her t ransact ions unt il t he cur rent t ransact ion com m it s or abort s. ( COM+ w ill abort t he t r ansact ion if a deadlock sit uat ion exist s because of t he block ing.) While a t ransact ion m odifies t he Cat alog, r eaders fr om wit hin t hat t r ansact ion will read t he dat a as if it w ere com m it t ed. Readers from out side t he t r ansact ion will not be blocked, and t he dat a t hey see will not reflect any int er im changes m ade wit hin t he fir st t ransact ion unt il t hat t r ansact ion act ually com m it s. You should avoid st art ing a new COM+ applicat ion ( eit her program m at ically or m anually via t he Com ponent Services Explorer) t hat r elies on infor m at ion t hat is not yet com m it t ed. One last point r egarding t ransact ions and t he COM+ Cat alog: y ou can pr ogr am m at ically invoke calls t hat access t he filesyst em , such as expor t ing a COM+ applicat ion. The problem is t hat t he filesy st em and t he Windows I nst aller do not part icipat e in t r ansact ions. I f your t r ansact ion abort s, y ou will have t o roll back t hose changes m anually t o m aint ain consist ency .
6 .7 Su m m a r y Program m ing t he COM+ Cat alog is not hing m ore t han underst anding t he Cat alog program m ing m odel and nav igat ing down t he Cat alog st r uct ure, using t he Com ponent Services Ex plorer or t he Cat alog st ruct ur e diagr am s in t his chapt er as reference guide. This chapt er focused on t he Cat alog st r uct ure, not on t he sem ant ics of t he it em s it cont ains. Alt hough t he Cat alog int er faces were designed for script ing languages, you can access t hem fr om C+ + as well, and t he r esult ing code is j ust as concise. Som e COM+ serv ices feat ures are available only by accessing t he Cat alog program m at ically ( in par t icular , som e feat ures of COM+ Event s, discussed in Chapt er 9) , so k nowing how t o work wit h t he Cat alog is an essent ial sk ill. Fur t herm ore, aut om at ing m undane and r epet it ive dev elopm ent and deploym ent t ask s by pr ogram m ing direct ly against t he COM+ Cat alog is fairly easy .
173
Cha pt e r 7 . COM + Se cur it y Per haps not hing epit om izes t he differences bet w een developing a dist ribut ed ent erpr ise- wide syst em using COM+ and dev eloping one using DCOM m or e t han t he COM+ securit y serv ice. DCOM securit y is not or ious for being com plex and hard t o lear n. Even t hough DCOM uses a sim ple and elegant secur it y progr am m ing and configurat ion m odel, t he sheer volum e of t echnical det ails and t he inherent difficult y of dist ribut ed syst em s securit y put s DCOM securit y out side t he reach of m any developer s. COM+ m ak es using securit y enj oyable by pr oviding an easy- t o- use adm inist rat ive securit y infrast r uct ure. COM+ securit y is based on an int uit iv e new securit y concept called role- based secur it y. Role- based securit y great ly sim plifies t he m anagem ent and configur at ion of your applicat ion’s securit y . Of all com ponent ser vices prov ided by COM+ , securit y is m y fav or it e. COM+ securit y m akes it possible for you t o leave all secur it y- r elat ed funct ionalit y out side t he scope of your com ponent s and configure securit y adm inist rat ively. Roles are used for access cont rol, and declarat ive at t r ibut es are used for t he rem aining securit y set t ings. I f t he adm inist rat ive configurat ions ar e t oo coarse for your part icular needs and y ou st ill want t o have pr ogr am m at ic cont rol ov er securit y , COM+ provides an easy- t o- use program m at ic way t o finet une secur it y. I n fact , COM+ secur it y solves classic dist ribut ed com put ing problem s t hat are difficult and would r equire m uch work t o solve on your own. Ev en wit h a single- m achine applicat ion, COM+ securit y provides elegant solut ions for adm inist rat ion and configurat ion issues. This chapt er cover s basic securit y concept s, but it avoids ( as m uch as possible) t he gory det ails of low- lev el securit y m anipulat ion and COM+ securit y im plem ent at ion. I nst ead, I ’ll focus on how best t o use t he secur it y service, what t he available opt ions are, t he t r adeoffs bet ween t hem , and t heir configurat ion pit falls.
7 .1 Th e N e ed for Se cu r it y Who needs secur it y? You do. Alm ost nobody t oday develops a st andalone, single- m achine, self- cont ained applicat ion. Applicat ions t oday are dist ribut ed bet ween m ult iple m achines. Som e applicat ions hav e a user int erface; ot her s execut e business logic and int er act wit h ot her applicat ions. Your dat abase is probably on a separat e set of m achines alt oget her. The word " secur it y" is int rinsic t o t he wor d " dist ribut ed" —m eaning t hat t he m om ent y ou dist r ibut e your applicat ion, secur it y raises it s head.
174
Securit y provides way s t o ver ify t hat a par t icular user has sufficient cr edent ials t o per form an operat ion. Secur it y is t he w ay you v erify t hat t he users are who t hey say t hey are. Securit y is t he way you prot ect your sy st em from innocent user m ist akes and m alicious at t acks. For ex am ple, im agine a hospit al pat ient infor m at ion sy st em . I n t his sy st em , not all users on all t erm inals are cr eat ed equal. Only doct or s can sign a deat h cer t ificat e or change a dose of m edicine. Nurses can updat e pat ient par am et ers, such as t em per at ur e or t he last t im e t he pat ient t ook m edicine. Hospit al cler ks can v iew som e inform at ion and bill t he pat ient ’s insur ance com pany. However , a cler k should not be allowed t o alt er anyt hing considered m edical infor m at ion, not even accident ally . A securit y infrast r uct ure prov ides an easy way t o configur e t hese cr edent ials and access cont rols. When a doct or logs on at a nurse’s st at ion, y ou want t o giv e t he doct or pr oper access, even t hough t he access is fr om t he nurse’s st at ion. You should prot ect t he privacy of t he pat ient infor m at ion so m alicious par t ies—on t he inside or out side— cannot gain access t o it . You want t o be able t o easily change who is allowed t o do what and avoid har dcoding securit y policies in your applicat ion. As t he sy st em and t he dom ain change ( new hospit al regulat ions or new users) , you want t o reconfigure t he syst em securit y wit hout r ecoding it . Securit y in a m odern syst em is not an aft ert hought . You m ust design secur it y int o y our COM+ applicat ion and com ponent s from day one, m uch t he sam e w ay you design concurr ency and t hr eading m odels, fact or out y our int er faces, and allocat e int erfaces t o com ponent s. I f you don't , at best y our applicat ion will not work. At worst , you will int roduce securit y breaches int o your syst em , allowing crit ical applicat ion logic t o go ast ray and face dat a corr upt ion or inconsist ency. Essent ially , lack of securit y is a failur e t o deliver t he r obust syst em y our cust om er pays for . When dealing wit h secur it y, you should alw ays assum e t hat som ebody will event ually find and t ake advant age of a securit y hole.
7 .2 Ba sic Se cu r it y Ter m s To m ake t he m ost of t he secur it y configurat ions COM+ has t o offer, you need t o be fam iliar wit h a few basic t erm s and concept s. The rest of t his chapt er m akes frequent use of t hese t erm s. 7 .2 .1 Se cur it y I de n t it y A securit y ident ity is account can be local COM+ ent it y, be it a associat ed wit h it so
a valid account used t o ident ify a user. The or an account on a dom ain ser ver. Every client or an obj ect , m ust hav e an ident it y t hat COM+ can det erm ine what t hat ent it y is
175
capable of accessing. I n Windows, all obj ect s in t he sam e process share t he sam e ident it y, unless t hey m ake an ex plicit at t em pt t o assum e a differ ent ident it y. You can configur e a COM+ serv er applicat ion t o always r un under a par t icular ident it y or t o run under t he ident it y of t he user who is curr ent ly logged on t hat Windows st at ion. Obj ect s from a COM+ library applicat ion run under t he ident it y of t he host ing process by default . 7 .2 .2 Aut he n t ica t ion Aut hent icat ion has t wo facet s. The first is t he pr ocess by which COM+ ver ifies t hat t he callers are who t hey claim t o be. The second is t he pr ocess by which COM+ ensures t he int egr it y of t he dat a sent by t he callers. COM+ aut hent icat ion relies on t he underlying securit y provider —in m ost cases Windows 2000 built - in securit y . I n t he Window s default secur it y provider, t he challenge/ r esponse prot ocol is used t o aut hent icat e t he caller's ident it y. Given t hat all callers m ust have a securit y ident it y, if t he caller s are who t hey say t hey are, t hen t hey m ust know t he account passwor d. That password is also known t o t he dom ain ser ver . The securit y provider does not want t o ask t he callers dir ect ly for t heir passwords because a m alicious t hird part y can sniff t he net work t o discover t he password. I nst ead, t o aut hent icat e t he callers, t he securit y provider encodes a random block of dat a wit h t he account passwor d and sends it t o t he caller s, asking t hem t o decode t he encr ypt ed block using t he passwor d and send t he r esult back. This process is t he challenge. I f t he r et urned block, t he r esponse, is t he sam e as t he original unencr ypt ed block , t hen t he caller s are aut hent icat ed. Aut hent icat ing caller ident it y is only one problem . The ot her problem is t hat dat a passed in a m et hod call can be int er cept ed, copied, alt ered, or cor rupt ed by a m alicious t hird part y . Under COM+ , bot h t he caller and t he obj ect hav e a r ange of choices t o det er m ine how secure t he connect ion bet ween t hem should be. To aut hent icat e dat a int egrit y , COM+ can use one of t wo t echniques: it can append a checksum t o every net w ork pack et , m ak ing sur e t hat t he dat a is not t am pered wit h dur ing t ransport , or it can encr ypt all inform at ion in t he pack et . Bot h kinds of aut hent icat ion ( ident it y and dat a int egr it y) are, in m ost cases, com plet ely t r ansparent t o bot h t he caller and t he obj ect and done aut om at ically by COM+ . However , t here is a clear t r adeoff bet ween secur it y and per for m ance ( when and t o what ext ent t o aut hent icat e) , and it is up t o you t o choose and configure t he proper aut hent icat ion lev el for your applicat ion. 7 .2 .3 Aut hor iz a t ion Aut horizat ion is t he process of det er m ining what t he caller is allowed t o access in t he sy st em . Aut horizat ion is also called access 176
cont rol. COM+ uses role- based securit y ( discussed in t he following sect ion) t o let you define access cont r ol at t he com ponent , int er face, and m et hod lev els. Access cont rol is used t o protect obj ect s and resources against unaut hor ized access by client s. I f a user who is not gr ant ed access t o a com ponent t ries t o inv oke a m et hod on t hat com ponent , t he m et hod invocat ion fails wit h t he err or code E_ACCESSDENIED ( " Per m ission Denied" in Visual Basic) . You configur e access cont rol adm inist rat ively using t he Com ponent Serv ices Explorer. Pr ogr am m at ically, you can st ill fine- t une access and execut ion of a m et hod based on t he caller’s ident it y and ot her infor m at ion such as t he m et hod param et er s and obj ect st at e. Not e t hat aut horizat ion is not relat ed t o aut hent icat ion. Aut horizat ion assum es t hat t he caller is already aut hent icat ed and is only concer ned wit h whet her t he caller can access t his obj ect . I t is not concer ned wit h whet her t he caller is r eally who he or she claim s t o be. 7 .2 .4 La un ch Se cu r it y Launch secur it y cont r ols which user s are allowed t o cr eat e a new obj ect in a new process. Unlike DCOM, COM+ does not prov ide a dedicat ed way t o cont r ol launch securit y. This is done int ent ionally t o avoid a com m on DCOM secur it y pit fall—allowing a user t o launch a process, but forget t ing t o gr ant t he user access t o t he obj ect s inside! As a result , t he user could call CoCreateInstance( ) t o launch t he pr ocess, but would be denied access t o m et hods, including being unable t o call Release( ) on t he obj ect . The process is ult im at ely orphaned, and t he user has t o shut it down m anually or rely on COM garbage collect ion t o event ually shut t he process down. I n COM+ , even if t he client is not gr ant ed access t o t he obj ect , ( but is a m em ber of at least one role defined for t he applicat ion) , t he client can st ill launch a new process wit h a new obj ect inside and can call t he IUnknown m et hods on t he obj ect , including Release( ). The client cannot access m et hods on any ot her int er face, how ever. 7 .2 .5 I m p e r son a t ion Aut horizat ion and aut hent icat ion prot ect t he obj ect from being accessed by unaut hor ized and unaut hent icat ed users. This prot ect ion ensures t hat when an obj ect is ask ed t o perfor m an operat ion, t he inv oking client has per m ission t o access t he syst em and t he call was not init iat ed by an adversary client . Howev er, how should t he client be pr ot ect ed fr om m alicious obj ect s? What prevent s t he server fr om assum ing t he client 's ident it y and cr edent ials and causing har m ? I s t he serv er even allowed t o lear n t he ident it y of t he calling client ? By set t ing t he im per sonat ion level,
177
COM+ let s caller s indicat e what t hey allow obj ect s t o do wit h t heir securit y ident it y. The im personat ion level indicat es t he degree t o which t he server can im personat e t he calling client . Set t ing t he im personat ion lev el can be done adm inist rat ively and program m at ically on t he client side; at t em pt ing t o im per sonat e t he client can only be done program m at ically by t he serv er.
7 .3 Role - Ba sed Se cu r it y The cornerst one of COM+ access cont rol is r ole- based secur it y. A role is a sy m bolic cat egory of user s w ho share t he sam e securit y privileges. When y ou assign a r ole t o an applicat ion r esource, y ou grant access t o t hat r esour ce t o whoever is a m em ber of t hat r ole. 7 .3 .1 Con figu r ing Role - Ba se d Se cu r it y The best way t o explain role- based securit y is by dem onst r at ion. Suppose you have a COM+ bank ing applicat ion. The applicat ion cont ains one com ponent , t he bank com ponent . The bank com ponent support s t wo int er faces t hat allow users t o m anage bank account s and loans, defined as: interface IAccountsManager : IUnknown { HRESULT TransferMoney([in]int nSum,[in]DWORD dwAccountSrc, [in]DWORD dwAccountDest); HRESULT OpenAccount([out,retval]DWORD* pdwAccount); HRESULT CloseAccount([in]DWORD dwAccount); HRESULT GetBalance([in]DWORD dwAccount,[out,retval]int* pnBalance); }; interface ILoansManager : IUnknown { HRESULT Apply([in]DWORD dwAccount,[out,retval]BOOL* pbApproved); HRESULT CalcPayment([in]DWORD dwSum,[out,retval]DWORD* pdwPayment); HRESULT MakePayment([in]DWORD dwAccount,[in]DWORD dwSum); }; Dur ing t he r equir em ent s- gat her ing phase of t he pr oduct dev elopm ent , y ou discovered t hat not every user of t he applicat ion should be able t o access every m et hod. I n fact , t here are four kinds of user s: •
The bank m anager , t he m ost powerful user, can access all m et hods on all int er faces of t he com ponent .
178
•
•
•
The bank t eller can access all m et hods of t he IAccountsManager int er face, but is not aut hor ized t o deal wit h loans. I n fact , t he applicat ion is r equired t o prevent a t eller from accessing any ILoansManager int erface m et hod. Sim ilar ly, t he loan consult ant can access any m et hod of t he ILoansManager int er face, but a consult ant is never t r ained t o be a t eller and m ay not access any IAccountsManager int er face m et hod. A bank cust om er can access som e of t he m et hods on bot h int er faces. A cust om er can t r ansfer funds bet ween account s and find t he balance on a specified account . However, a cust om er cannot open a new account or close an exist ing one. The cust om er can m ak e a loan paym ent , but cannot apply for a loan or calculat e t he paym ent s.
I f you wer e t o enforce t his set of securit y r equirem ent s on your own, you would face an im plem ent at ion night m are. You would have t o m anage a list of who is allowed t o access what and t ight ly couple t he obj ect s t o t he secur it y policy . The obj ect s would hav e t o verify who t he caller is and whet her t he caller has t he right cr edent ials t o access t hem . The result ing solut ion would be fr agile. I m agine t he work you would have t o do if t hese r equirem ent s wer e t o change. For t unat ely, COM+ m ak es m anaging such a securit y access policy easy . Aft er im port ing t he bank com ponent int o a COM+ applicat ion ( be it a serv er or a libr ary applicat ion) , y ou need t o define t he appr opr iat e roles for t his applicat ion. Every COM+ applicat ion has a folder called Roles. Ex pand t he Roles folder, r ight click on it , and select New from t he cont ex t m enu. Ty pe Bank Manager int o t he dialog box t hat com es up and click OK. I n t he Roles folder , y ou should see a new it em called Bank Manager. Add t he r est of t he roles: Customer , Teller , and Loans Consultant . The applicat ion should look like Figure 7- 1. Figu re 7 - 1 . Th e Role s fold er of t h e ba n k ap plicat ion
179
You can now add user s t o each r ole. You can add any user wit h an account on t he m achine or t he dom ain. Every r ole has a User s folder under which y ou add regist er ed users from your dom ain or t he m achine local users. For exam ple, navigat e t o t he User s folder of t he Cust om er role, right - click t he Users folder , and select New fr om t he Cont ext m enu. I n t he dialog box, select t he user s who ar e par t of t he Cust om er r ole, such as Joe Cust om er ( see Figure 7- 2) . You can populat e t his r ole and t he rem aining roles in t he bank applicat ion wit h t heir user s. Figu r e 7 - 2 . Popu la t ing a role w it h u ser s
The next st ep is t o gr ant access t o com ponent s, int erfaces, and m et hods for t he various roles in t he applicat ion, according t o t he 180
bank applicat ion r equirem ent s. Display t he bank com ponent proper t ies page and select t he Securit y t ab. The t ab cont ains t he list of all roles defined for t his applicat ion. Check t he Manager r ole t o allow a m anager access t o all int erfaces and m et hods on t his com ponent ( see Figur e 7- 3) . When you select a role at t he com ponent level, t hat r ole can access all int erfaces and m et hods of t hat com ponent . Make sure t hat t he " Enfor ce com ponent level access check " checkbox under Aut hor izat ion is select ed. This checkbox, y our com ponent access secur it y swit ch, inst ruct s COM+ t o v er ify part icipat ion in r oles befor e accessing t his com ponent . Figu re 7 - 3 . Se le ct in g a role a t t h e com p on en t le vel
Nex t , configur e securit y at t he int erface level. Display t he IAccountsManager int er face propert ies page, and select t he Securit y t ab. Select t he Teller role t o grant access t o all m et hods in t his int er face t o any m em ber of t he Teller role ( see Figure 7- 4) . The upper port ion of t he int er face securit y t ab cont ains inherit ed roles — roles t hat wer e grant ed access at t he com ponent level, and t hus access t o t his int er face as well. Even if t he Bank Manager r ole is not checked at t he IAccountsManager int erface level, t hat r ole can st ill access t he int erface. Figu re 7 - 4 . Gr a n t in g a cce ss t o a role a t t h e in t e rfa ce le vel
181
Sim ilar ly, configur e t he ILoansManager int erface t o grant access t o t he Loans Consult ant role. The Bank Manager should also be inherit ed in t hat int er face. Not e t hat t he Loans Consult ant cannot access any m et hod on t he IAccountsManager int er face, j ust as t he requir em ent s st ipulat e. Finally, you can configur e access r ight s at t he m et hod level. A cust om er should be able t o invoke t he GetBalance( ) and TransferMoney( ) m et hods on t he IAccountsManager int er face, and t he MakePayment( ) m et hod on t he ILoansManager int er face, but no ot her m et hods. Grant ing access at t he m et hod lev el is sim ilar t o gr ant ing it at t he int er face or com ponent lev el. For exam ple, t o configure t he GetBalance( ) m et hod, display t hat m et hod’s Propert ies page, select it s Securit y t ab and check t he Cust om er r ole ( see Figur e 7- 5) . The m et hod’s Securit y t ab shows inherit ed roles from t he int erface and com ponent levels. COM+ displays roles inherit ed from t he com ponent level wit h a com ponent icon; it shows roles inher it ed from t he int erface lev el w it h an int er face icon. Figu r e 7 - 5 . Gr a n t in g a cce ss t o a role a t t h e m e t h od le ve l
182
Because of t he inherit ed nat ur e of r oles, you can deduce a sim ple guideline for configuring r oles: put t he m or e power ful roles upst ream and t he m or e rest r ict ed roles downst ream . 7 .3 .2 Role - Ba se d Se cu r it y Be n e fit s For all pract ical purposes, COM+ role- based access cont rol gives you ult im at e flex ibilit y wit h zero coding. I t gives you t his flex ibilit y because access cont r ol at t he m et hod level is usually gr anular enough. Role- based securit y offers a scalable solut ion t hat does not depend on t he num ber of sy st em user s. Wit hout it , you would hav e t o assign access right s for all obj ect s and resources m anually, and in som e cases you would have t o im personat e user s t o find out whet her t hey have the right credent ials. ( I n Sect ion 7.8, you will see how an obj ect can im per sonat e a caller.) Configurable r olebased securit y is an ext ensible solut ion t hat m ak es it easy t o m odify a secur it y policy . Lik e any ot her requirem ent , your applicat ion’s securit y requirem ent s are lik ely t o change and evolv e over t im e, but now you have t he r ight t ool t o handle it product ively. Role- based access cont r ol is not lim it ed t o configurat ions m ade wit h t he Com ponent Ser vices Explor er. You can build m or e gr anular securit y policies program m at ically if you need t o, using r ole- based securit y as a support ing plat form . 7 .3 .3 D e signin g Role - Ba se d Se cu r it y Roles m ap nicely t o t er m inology fr om your applicat ion’s dom ain. Dur ing t he r equir em ent s analysis phase, you should aspire t o discer n user roles and privileges, in addit ion t o discov er ing int er faces and classes. Focus your effort s on discovering differences in t he r oles users play t hat dist inguish t hem from one anot her, rat her t han placing explicit per m issions on each obj ect in t he sy st em . As y ou saw in t he bank exam ple, r oles work very well when
183
you need t o charact erize groups of users based on w hat act ions t hose user s can perfor m . How ever, roles don’t work well in a couple of cases. First , t hey don’t work well when access decisions rest on t he ident it y of a part icular user : for exam ple, if only t he bank t eller Mary Sm iling is allowed t o open an account . Second, t hey don’t work well when access decisions r est on special infor m at ion regarding t he nat ure of a part icular piece of dat a: for exam ple, when bank cust om ers cannot access account s out side t he count ry . Role- based secur it y is a serv ice t hat pr ot ect s access t o m iddle- t ier obj ect s. Middle- t ier obj ect s should be wr it t en t o handle any client and access any dat a. Basing your obj ect behavior on par t icular user ident it ies does not scale. For cing y our obj ect s t o know int im at e det ails about t he dat a does not scale well eit her . Each secur it y m echanism has it s lim it at ions—if your applicat ion requir es you t o im plem ent t his sort of behavior , y ou m ay want t o look at ot her opt ions, such perform ing t he securit y access checks at t he dat abase it self. When designing effect ive roles, t r y t o avoid a v ery int ricat e rolebased policy. A policy wit h m any roles t hat allocat es users t o m ult iple r oles m ay be t oo com plicat ed. Role- based securit y should be a st raight for ward solut ion wit h cr isp dist inct ions bet ween r oles. Av oid defining roles wit h am biguous m em ber ship cr it er ia. The sim pler t he solut ion, t he m or e robust and m aint ainable it will be. Your applicat ion adm inist r at or should be able t o m ap users t o roles inst ant ly . Use m eaningful, self- describing nam es for roles, borrowing as m uch as possible fr om t he applicat ion dom ain's t erm s and v ocabulary. For exam ple, Super User is a bad r ole nam e, wher eas Bank Manager is a good nam e ( ev en t hough your applicat ion would funct ion j ust fine wit h t he form er) . Occasionally, you will be t em pt ed t o m odel a real- life sit uat ion and define num erous roles. May be different branches of t he bank have differ ent policies descr ibing what a t eller can do. Try t o collapse roles as m uch as possible. You can do t his eit her by refact oring your int er faces ( deciding what m et hods will be on what int erface and which com ponent suppor t s which int erface) or by defining new int er faces and com ponent s. Breaking t he syst em int o m or e gr anular COM+ applicat ions, each wit h it s own sm all set of roles, is anot her design solut ion used t o cope wit h num er ous r oles. This solut ion would probably be a bet t er m odeling of t he sy st em in ot her r espect s as well. Av oiding num er ous roles also im proves perform ance. On each call, COM+ m ust scan t he list of roles t o find out w het her t he caller is a m em ber of a role t hat is gr ant ed access. Roles are defined at t he applicat ion lev el, but t hey ar e act ually par t of every com ponent 's design. I f you writ e a st andalone COM+
184
com ponent t hat will be placed in COM+ applicat ion m anaged by som eone else, you need t o have in your docum ent at ion explicit inst ruct ions describing how t o configur e secur it y for t he host ing applicat ion. You need t o docum ent t hat y our com ponent needs it s access cont rol t urned on for t his applicat ion, t he r equired aut hent icat ion lev el, t he r oles t hat should be defined for t his applicat ion, and t he cr it er ia t hat should be used t o allocat e users for your roles. You need t o st ipulat e which m et hods and int erfaces each role should be grant ed access t o and which roles ar e gr ant ed access t o t he ent ir e com ponent . 7 .3 .4 D e ployin g a nd Adm in ist e r in g Role - Ba se d Se cur it y Roles are an int egral part of your design, but allocat ion of users t o roles is part of your applicat ion deploym ent . The applicat ion adm inist rat or should m ake t he final allocat ion of user s t o r oles at t he cust om er sit e. Because you need t o m ak e t he adm inist r at or ’s j ob as easy as possible, your applicat ion should alr eady have predefined r oles, and t he adm inist rat or should only need t o allocat e users t o r oles. When adding user s t o r oles, populat ing t he roles wit h Windows 2000 user gr oups inst ead of individual user s is w ise. Groups also appear on t he sam e list as users, such as in Figure 7- 2, in t he Bank Tellers gr oup. By assigning gr oups t o r oles, t he applicat ion is aut om at ically configured t o handle t he new user corr ect ly when a new user is added t o a dom ain user gr oup. The sam e is t r ue when a user is r em ov ed fr om a Windows user group or rem oved from one group and added t o anot her ( for exam ple, when Mary Sm iling is prom ot ed t o a bank m anager posit ion) . When you assign groups t o r oles, your applicat ion r eact s t ranspar ent ly t o nor m al event s in t he applicat ion dom ain. I f you t arget int ernat ional m arket s, you should localize y our r oles and hav e t hem t ranslat ed int o t he local language. I n m any cases, applicat ion adm inist rat or s will be local hires on t he for eign m ark et , and properly t ranslat ed roles can m ake a world of difference. When prov iding t he best suppor t for your applicat ion adm inist rat or, you should clearly docum ent t he role- based policy you design, whet her or not role m em ber ship is obv ious t o y ou. I n par t icular, use t he descr ipt ion field av ailable for each role, as shown in Figur e 7- 6. The descript ion should be concise. I f you cannot describe who should belong t o t he r ole in t hr ee lines, t he role is pr obably t oo com plex. Figu r e 7 - 6 . Th e D e scr ip t ion fie ld on t h e r ole pr op er t ie s pa g e
185
Building a helper adm inist rat ive ut ilit y t o add user s t o roles program m at ically , using t he COM+ Cat alog’s int er faces and com ponent s, m ay also be wor t hwhile; it sav es t he applicat ion adm inist rat or t he t r ouble of learning how t o use t he Com ponent Serv ices Explorer. The ut ilit y should present t o t he adm inist rat or a fam iliar user int er face, pr efer ably t he sam e user int erface st andar d as t he applicat ion it self. The ut ilit y should display t he users select ion dialog box t o t he adm inist r at or and add t he select ed users t o t he appr opr iat e roles. When you export a COM+ applicat ion, t he Applicat ion Export Wizar d giv es you t he opt ion of export ing t he user ident it ies wit h t he r oles ( see Figure 7- 7) Figu r e 7 - 7 . You sh ou ld u su ally a void e xp ort ing u ser ide n t it ie s w it h r ole s
This opt ion should only be used by t he applicat ion adm inist r at or when m aking cloned inst allat ions at a par t icular sit e, from one m achine t o anot her . Rem em ber t hat r oles are par t of t he design, while allocat ion of users t o roles is part of deploym ent . I n fact , export ing user infor m at ion from one deploy m ent sit e t o anot her m ay const it ut e a secur it y br each. Most cust om ers would not like a list of t heir em ploy ees, t heir usernam es, and t he roles t hey play in 186
t he organizat ion available at lar ge, let alone at som e ot her com pany’s sit e. As a developer , " export user ident it ies w it h roles" is of lit t le use t o you.
7 .4 Se cu r in g a Se r ver Applica t ion Cont rolling access t o your com ponent s v ia role- based securit y is all fine and well, but t her e is m ore t o securit y t han j ust access cont rol. You m ust st ill set t he securit y ident it y for your applicat ion and set t he aut hent icat ion and im per sonat ion levels. Configuring secur it y for a serv er applicat ion is different fr om t hat of a libr ary applicat ion, j ust ify ing each applicat ion t y pe in a separat e sect ion. When designing and configur ing a serv er applicat ion secur it y, you need t o do t he following: • • • •
•
Decide on t he secur it y ident it y under w hich t he server applicat ion ex ecut es. Decide what aut hor izat ion ( access cont r ol) t he server applicat ion requires—how granular access cont rol should be. Decide at what aut hent icat ion level t o aut hent icat e incom ing calls. Decide at what im per sonat ion level you gr ant obj ect s in ot her applicat ions when t his serv er applicat ion is t he client of t hose obj ect s. Configure your server applicat ion secur it y.
The following sect ions discuss t hese act ion it em s in dept h. 7 .4 .1 Con figu r ing t h e Se r ve r Applica t ion I de n t it y When y ou invoke t he Applicat ion I nst all Wizar d and use it t o cr eat e a new server applicat ion, t he Wizar d present s you wit h a dialog box t hat let s you set t he securit y ident it y of t he ser ver applicat ion. Set t ing t he secur it y ident it y det erm ines w hat user account all com ponent s in t hat applicat ion will r un under , which dict at es cr edent ials, priv ileges, and access r ight s ( see Figur e 7- 8) . You m ay eit her run t he applicat ion as t he int eract ive user ( useful during debugging) or as a designat ed user ( for deploym ent ) . Figu r e 7 - 8 . Se le ct in g a n ide n t it y for a n ew se rv e r a p plicat ion
187
You can alw ays set a different ident it y lat er on ( and y ou usually will) by bringing up t he applicat ion proper t ies page and select ing t he I dent it y t ab ( see Figur e 7- 9) . Figu r e 7 - 9 . Se le ct in g a n ide n t it y for a n ex ist in g ser ver a pp lica t ion
When Obj ect A is cr eat ed in t he applicat ion, t he applicat ion securit y ident it y cont rols ev ery t hing Obj ect A is allowed t o access and do. I f Obj ect A t r ies t o access anot her obj ect ( Obj ect B) in anot her applicat ion, and Obj ect B is configured t o use role- based secur it y, COM+ uses t he secur it y ident it y of Obj ect A t o det er m ine whet her t o grant access t o Obj ect B. The secur it y ident it y of Obj ect A has t o belong t o at least one role t hat Obj ect B allows access t o. But t here is m or e t o an obj ect ’s ident it y t han role- based securit y: accessing t he filesyst em , accessing Win32 handles, inst alling new com ponent s, accessing t he COM+ Cat alog, m odifying t he Regist r y,
188
rem ot e calls, and so on, are all lim it ed by t he privileges of t he securit y ident it y. To m ake an educat ed decision on select ing t he right ident it y for your obj ect s, you need t o know t he t erm Windows st at ion. I n Windows, every user, or m or e pr ecisely, every securit y ident it y , get s t o r un in it s ow n st at ion—it has it s own copy of t he clipboard, global at om s t able, deskt op obj ect s, a keyboard, a m ouse, and a display device. Each logged- on user is pr ovided wit h a new Windows st at ion. Obv iously, only t he Windows st at ion associat ed wit h t he current ly int er act ive user can act ually display a user int erface. I f a com ponent is set t o run under a designat ed securit y ident it y and t hat ident it y is differ ent from t hat of t he int er act iv e user , it is placed in it s own Window s st at ion. When y ou configur e y our server applicat ion ident it y t o run under t he account of t he int eract ive user, t he applicat ion shar es t he int er act ive Windows st at ion wit h t hat user . This opt ion has t he clear benefit of being able t o int eract wit h t he user. However , it also has severe lim it at ions: what should COM+ do if no user is logged on and an act iv at ion r equest fr om anot her m achine t ries t o launch t he applicat ion? I n t his case, COM+ r efuses t o launch t he applicat ion. I f t he int er act ive user logs off, COM+ also t er m inat es t he applicat ion. The second opt ion COM+ prov ides for configur ing a serv er applicat ion's ident it y is t o r un under a specific designat ed ident it y . The applicat ion is placed in it s own Windows st at ion. All subsequent inst ant iat ions of new com ponent s from t hat applicat ion shar e t hat dedicat ed windows st at ion and ident it y credent ials. The com ponent in t he applicat ion cannot have a user int er face because t heir Windows st at ion cannot int er act w it h t he user . However, for a m iddle- t ier com ponent , a user int er face is not necessary anyway; all user int eract ion is perform ed at t he present at ion t ier . You can st ill redir ect m essage boxes t o t he int eract ive Windows st at ion, using t he m essage box t ype at t ribut e MB_DEFAULT_DESKTOP_ONLY. This r edirect ion is done by design for debug purposes and is available for m essage box es only.
Ru nning a s Act iva t or The archit ect s of COM+ act ually had, in t heor y, a t hir d opt ion for a serv er applicat ion secur it y ident it y . That t hird opt ion is t o r un under t he ident it y of t he launching user. This opt ion is available under classic DCOM ( in fact , it is t he default for DCOM) . Howev er , it has a few crit ical lim it at ions: if COM+ were t o creat e a new Windows st at ion for ev ery new act ivat ion r equest com ing fr om a differ ent ident it y, t he sy st em would run out of resour ces very quick ly because a Windows st at ion is ex t rem ely expensive t o cr eat e and m aint ain. As a result , t his opt ion does not scale well at all. Anot her lim it at ion is t he pot ent ial for hav ing obj ect s from t he 189
sam e applicat ion running in different processes because every Windows st at ion has it s own init ial pr ocess. This pot ent ial could violat e design decisions—you m ay have want ed all y our obj ect s in one process because t hey m ay need t o share event handles or som e ot her process- wide resource. Given t hese lim it at ions, you can under st and why t he COM+ archit ect s chose not t o include t he opt ion t o launch t he applicat ion under t he ident it y of t he launching user. So, which of t he t wo opt ions should y ou choose? Running as t he int er act ive user has a dist inct advant age during debugging sessions, because you can use a debugger t o t race t he ex ecut ion of your com ponent s. I n addit ion, dur ing a debug session, t he developer is logged on t o his m achine, so COM+ act ivat es t he applicat ion easily . Running as a designat ed user is m ore useful for deploy m ent purposes. I t frees you from needing a user logged on t o t he ser ver m achines when your applicat ion is running. I f y ou configur e m ore t han one applicat ion t o run under t he sam e designat ed user account , you also conser ve sy st em resour ces because all com ponent s from t hose applicat ions share t he sam e Window s st at ion. Running under a specific ident it y has a few m ore adv ant ages: •
•
Because an obj ect can per form operat ions on behalf of arbit rar y users, lim it ing t he obj ect 's capabilit ies is oft en necessary. By assigning t he obj ect a less priv ileged ident it y, you lim it t he pot ent ial harm m alicious caller s can do aft er being gr ant ed access t he obj ect ( t he int er act ive user m ay hav e unlim it ed adm inist rat or power , and t hat could be v ery dangerous indeed) . I nt er net client s calling int o your applicat ion have no ident it y at all and are anonym ous in m ost cases. You can now assign a specific ident it y t o t he obj ect s t hat carr y out a r equest on behalf of I nt ernet client s.
7 .4 .2 En a bling Au t h or iza t ion The propert ies page of each COM+ ser ver applicat ion includes a Securit y t ab. The securit y t ab is where y ou set t he r est of t he securit y proper t ies for your applicat ion. There are four set t ings on t his t ab, each discussed in t he following sect ions. At t he t op of t he t ab ( see Figure 7- 10) , you w ill find t he aut horizat ion checkbox. Figu re 7 - 1 0 . A se rv e r a p plicat ion Se cu r it y t ab
190
The aut hor izat ion checkbox is t he access secur it y m ast er swit ch for t he applicat ion ( The com ponent ’s dev eloper st ill has t o enable t he com ponent - level aut horizat ion on a com ponent by com ponent basis, as discussed previously ; see Figure 7- 3) . When you inst all a new COM+ applicat ion, eit her a libr ar y or a ser ver applicat ion, t he default set t ing for t his swit ch is off. You m ust t urn on aut hor izat ion your self by check ing t he checkbox t o enable r ole- based securit y for your applicat ion. When aut horizat ion is enabled, COM+ v erifies in every call t hat t he calling ident it y is a m em ber of at least one of t he roles defined for t he applicat ion, and denies access if it is not . I f t he caller is a m em ber of at least one role, but t he t ar get com ponent does not grant access t o any of t he roles t he caller is a m em ber of, t he call is denied access downst r eam at t he com ponent level. Applicat ion- level aut hor izat ion is also t he COM+ way of enforcing launch cont rol. The caller cannot launch a new process ( by t r ying t o cr eat e an obj ect ) if it is not a m em ber of at least one role. 7 .4 .3 Se t t ing t h e Se cu r it y Le v e l The Securit y Level proper t ies gr oup ( which consist s of t wo r adio but t ons; see Figure 7- 10) is t he cent er of t he Securit y t ab. This group is t he r ole- based secur it y m ast er swit ch for all t he com ponent s in t his applicat ion. I f you set it t o t he upper posit ion ( " Perform access checks only at t he pr ocess level" ) , all r ole- based securit y configurat ions at lower lev els ( com ponent , int er face, and m et hod) will be disabled and ignor ed ( see, for exam ple, t he bank com ponent secur it y t ab in Figure 7- 11) . When access checks are per form ed at t he process level only, all calls w ill be allowed t hrough
191
regardless of t he set t ings at t he low er levels, as long as t hey passed t he generic applicat ion- level securit y access check. Figu re 7 - 1 1 . Se t t in g t h e se cu r it y acce ss ch e ck t o b e don e a t t h e pr ocess le vel on ly d isa ble s com pon e n t - le vel se cu r it y
One side effect of perform ing t he secur it y checks at t he pr ocess lev el only is t hat you cannot m ake any program m at ic role- based securit y checks inside your com ponent s because t he securit y infor m at ion will not be par t of t he call obj ect . You cannot access int er faces such as ISecurityCallContext. Addit ionally , when new obj ect s ar e act iv at ed, COM+ ignor es t heir secur it y requirem ent s when deciding in which cont ext t o act ivat e t hem . When y ou set t he access securit y t o be perfor m ed at t he process lev el and t he com ponent level, y ou can t ake adv ant age of rolebased securit y , eit her adm inist r at ively or program m at ically . COM+ considers t he obj ect securit y requir em ent s when deciding on it s act ivat ion cont ext . Com ponent s t hat do not want t o use r ole- based securit y can st ill choose t o do so. As y ou can see, disabling com ponent - level secur it y check s globally for an applicat ion is of lit t le use t o y ou. You can alway s disable it on a com ponent - by- com ponent basis. 7 .4 .4 Se t t ing t h e Au t h e n t ica t ion Le v e l Nex t , y ou need t o configur e t he desir ed aut hent icat ion level by select ing values fr om t he " Aut hent icat ion level for calls" com bo box ( see Figure 7- 10) . The aut hent icat ion level cont rols bot h caller ident it y aut hent icat ion and dat a int egrit y aut hent icat ion. The configured aut hent icat ion lev el affect s all calls t o and from t he applicat ion. COM+ let s you set t he aut hent icat ion lev el t o one of six set t ings: None, Connect , Call, Pack et , Pack et I nt egr it y, and Packet Pr iv acy. The first four aut hent icat ion levels deal wit h t he caller’s ident it y only and t he last t wo add dat a int egrit y as well. 192
7 .4 .4 .1 Au t h e n t ica t ion = N on e
When t he aut hent icat ion level is set t o None, y ou inst r uct COM+ not t o aut hent icat e t he caller at all. I f t he caller claim s t o be Joe Cust om er, t hen he is believed t o be so. Clear ly , disabling aut hent icat ion exposes your applicat ion and r enders it com plet ely defenseless t o anyt hing ranging from innocent user m ist ak es t o m alicious t hird- par ty at t acks. Set t ing aut hent icat ion t o None m ay be useful in isolat ed cases when client s calling in ar e anony m ous and no dat a priv acy or int egrit y guarant ee for dat a in t r ansit is requir ed. However , y ou should generally avoid disabling aut hent icat ion com plet ely . 7 .4 .4 .2 Au t h e n t ica t ion = Conn ect
When t he aut hent icat ion level is set t o Connect , COM+ aut hent icat es t he user ident it y only when a client connect s t o an obj ect in t he applicat ion. Connect ing t o t he obj ect m eans cr eat ing t he obj ect or t r ying t o access an obj ect ( given t o t he client fr om anot her client ) for t he fir st t im e. COM+ uses t he challenge/ r esponse prot ocol t o aut hent icat e t he client ’s ident it y . I f t he sam e client t ries t o connect t o anot her obj ect , COM+ aut hent icat es t he client ’s ident it y again. However , COM+ st ay s out of t he way once a connect ion is est ablished. This approach t o aut hent icat ion leav es t he door open for a m alicious t hird par t y t o sniff t he net wor k, wait for COM+ t o aut hent icat e a genuine caller, and t hen m ake subsequent calls in place of t he legit im at e caller, because fut ur e calls are not aut hent icat ed. Connect ion- lev el aut hent icat ion is t he bar e m inim um requir ed for m eaningful role- based secur it y because it v er ifies at least once t hat t he caller is who it say s it is. Connect ion- level aut hent icat ion, however, provides no privacy or int egr it y guarant ee for t he dat a in t ransit . 7 .4 .4 .3 Au t h e n t ica t ion = Ca ll
When t he aut hent icat ion level is set t o Call, COM+ aut hent icat es t he caller’s ident it y using challenge/ response on every m et hod call t o every obj ect in t he applicat ion, not j ust t he first call. This approach is clearly an im pr ovem ent ov er aut hent icat ion done only at connect ion t im e. 7 .4 .4 .4 Au t h e n t ica t ion = Pa ck et
Aut hent icat ing at t he beginning of every call m ay not be secur e enough if t he m et hod invocat ion payload is spread over m ult iple net wor k packet s. The underly ing net work t ranspor t pr ot ocol m ay div ide t he pay load ( param et er s, r et ur ned v alue, sour ce and dest inat ion, and so on) over m ult iple pack et s regularly . A
193
det er m ined m alicious t hird par ty m ay wait for t he first pack et t o be aut hent icat ed, and t hen int ercept t he r est of t he pack et s, change t hem , or send his own. To handle t his possibilit y, you can inst r uct COM+ t o aut hent icat e each packet fr om t he caller, not j ust t he first packet of every call. This level of aut hent icat ion is t he default used for every new COM+ server applicat ion. Packet level aut hent icat ion m ay be t he fir st m eaningful aut hent icat ion set t ing. However, it st ill provides no pr iv acy or int egrit y guarant ee for t he dat a in t r ansit . 7 .4 .4 .5 Au t h e n t ica t ion = Pa ck et I n t eg rit y
The prev ious four aut hent icat ion levels dealt wit h aut hent icat ing t he caller’s ident it y only. Aut hent icat ing ev er y packet from t he caller would prevent a m alicious t hird part y from being t em pt ing t o be t he caller or pr et ending t o change t he pack et flow. However , not hing st ops a m alicious t hird part y fr om m odify ing t he packet s’ cont ent . The m alicious t hir d part y could st ill, for ex am ple, change param et er values inside individual packet s. By set t ing t he aut hent icat ion level t o Pack et I nt egrit y , y ou inst r uct COM+ t o append a hashed checksum t o each packet . The receiv ing side calculat es t he checksum on t he packet j ust receiv ed, and if t he result ing checksum differs from t hat appended t o t he packet , COM+ fails t he call. Packet int egrit y incr eases t he pack et size and net work t r ansport t im e, but it provides a dat a int egrit y guar ant ee. Aut hent icat ing dat a int egr it y is done on t op of pack et - level ident it y aut hent icat ion. 7 .4 .4 .6 Au t h e n t ica t ion = Pa ck et Priva cy
Alt hough t he Pack et I nt egrit y level of aut hent icat ion prot ect s t he dat a int egrit y of each pack et , t he m alicious t hird part y can st ill r ead t he pack et s’ cont ent . I f y ou want t o pr ot ect t he pr ivacy of t he infor m at ion, you can inst ruct COM+ t o not only pr ov ide pack et int egr it y wit h a checksum , but also t o encrypt t he packet ’s cont ent when in t ransit and decrypt it when it is received. Packet Privacy is t he highest aut hent icat ion level possible, providing you wit h aut hent icat ed caller ident it y, dat a int egr it y, and privacy for dat a in t r ansit on every net work packet . You w ill encount er a per form ance hit for t he ext ra com put at ional effor t of encrypt ing and decrypt ing every pack et . However, for m any ent erprise applicat ions, t his level of securit y m ay be required t o prot ect sensit iv e dat a properly according t o organizat ional secur it y policy . 7 .4 .4 .7 D e cid ing on t h e a u t h en t ica t ion le vel
Every aut hent icat ion set t ing offers a clear t radeoff of applicat ion securit y versus per for m ance. You should decide on t he r ight aut hent icat ion lev el based on t he nat ur e and sensit ivit y of t he
194
serv ices y our com ponent s ex pose, potent ial- t hreat s analysis, and t he calling pat t ern fr om your client s ( t he lower t he call frequency and t he longer t he m et hod execut ion t im e is, t he less not iceable t he aut hent icat ion penalt y will be) . The applicat ion aut hent icat ion set t ing affect s all com ponent s in y our applicat ion. I f t he com ponent s in y our applicat ion differ great ly in t heir aut hent icat ion needs, consider put t ing t he m or e sensit ive com ponent s in a separ at e applicat ion and configur ing t hat applicat ion t o hav e a higher level of aut hent icat ion. Don’t m ak e com ponent s pay for an aut hent icat ion level t hey do not r equire. On t he ot her hand, if your t hr eat s analy sis dem ands an aut hent icat ion lev el t hat degrades t he applicat ion per form ance significant ly , or if t r ade- off is im possible because of organizat ional securit y policy, upgrading har dwar e t o im prove applicat ion per form ance is an opt ion. 7 .4 .4 .8 Clie n t a u t h en t icat ion le ve l com pa t ib ilit y
COM+ prefers t o secur e t he server as m uch as possible. I f t he calling client uses an aut hent icat ion lev el lower t han t hat of t he server ( for exam ple, if t he client is configured t o use Connect and t he serv er applicat ion is configur ed t o use Packet ) , t hen COM+ fails t he call. I f, on t he ot her hand, t he ser ver is t he one using t he lower set t ing, COM+ prom ot es t he connect ion t o t he client lev el. 7 .4 .5 Se t t ing t h e I m p e r sona t ion Leve l When an obj ect in Applicat ion A calls anot her obj ect in Applicat ion B, ident it y issues ar e st r aight forw ard: each applicat ion has it s ow n ident it y , used t o decide whet her t o grant access t o obj ect s or t o resources such as files. However , suppose t hat Applicat ion B needs t o access an obj ect in Applicat ion C t o cont inue it s work on behalf of t he original caller in Applicat ion A. The im m ediat e quest ion is, under what ident it y should B access C? Should it access C as B or as A? Suppose t hat t he obj ect in C needs t o call back int o Applicat ion A t o com plet e it s wor k. Should it access Applicat ion A as C, B, or A? One appr oach would let t he ser ver obj ect s im per sonat e t he client . This would be fine in an ideal world, wher e ser vers are never m alicious. However , in an ideal world, you don’t need securit y eit her. Clearly, client applicat ions need t o declare what ident it y t he serv icing obj ect s could use when accessing anot her applicat ion or a secured resour ce. This is w hat im personat ion is all about . The I m personat ion lev el com bo box ( see Figure 7- 10) is at t he bot t om of t he ser ver applicat ion securit y t ab. The im per sonat ion level select ion is used only when t he applicat ion y ou configur e is act ing as a client of an obj ect in anot her applicat ion. The im personat ion lev el is really a m easur e of t rust —how m uch t his applicat ion t rust s anot her applicat ion when it act s on it s behalf. Does t his applicat ion 195
allow ot her obj ect s t o find it s securit y ident it y ? Does it allow t hem t o im personat e it self and perfor m t heir work under t he client ident it y , t r ust ing t he ot her applicat ions’ obj ect s not t o abuse t he t r ust ? Does it allow t he obj ect s t o m ake addit ional calls wit h t he original client secur it y ident it y? These ar e im por t ant quest ions from any client applicat ion per spect iv e. COM+ defines four levels of t r ust , or im per sonat ion levels: Anonym ous, I dent ify, I m per sonat e, and Delegat e. I m personat ion of any level r equires aut hent icat ion t o be at least Connect ( t hat is, any aut hent icat ion level except None) t o propagat e t he client ident it y t o t he server side. 7 .4 .5 .1 I m p er son a t ion = An on ym ou s
Anony m ous is t he least t rust ing im personat ion lev el. The client does not even allow any server obj ect t o learn t he securit y ident it y of t he client . 7 .4 .5 .2 I m p er son a t ion = I d en t ify
When t he client set s t he im per sonat ion level t o I dent ify, t he server can ident ify t he client —t hat is, obt ain t he securit y ident it y of t he calling client . The serv er obj ect is not allowed t o im per sonat e t he client —everyt hing t he obj ect does is st ill done under t he server 's own ident it y. Not e t hat allowing or prevent ing t he obj ect from ident ifying t he caller is not t he sam e as having t he obj ect lear n program m at ically whet her t he caller is a m em ber of a par t icular role. When t he obj ect queries for t he caller 's role m em bership ( you will see how lat er on) , t he quest ion and t he answer ar e in role t erm s ( Bank Manager, Teller) and not in ident it y t erm s ( Joe Cust om er) . 7 .4 .5 .3 I m p er son a t ion = I m pe rson a t e
When t he client applicat ion set s t he im personat ion level t o I m personat e, t he obj ect can im personat e and assum e t he client ident it y 's credent ials. This im personat ion lev el is t he default value COM+ uses for new applicat ions. I m personat ion indicat es a great deal of t rust bet ween t he client and t he serv icing obj ect ; t he serv er can do anyt hing t he client can do, even if t he server applicat ion is configured t o use a less privileged ident it y. The only difference bet ween t he real client and t he obj ect is t hat if t he obj ect is on a separ at e m achine from t he client , it cannot access resour ces or obj ect s on ot her m achines as t he client . This lack of access is a dir ect r esult of t he underly ing aut hent icat ion m echanism —t he challenge/ r esponse pr ot ocol. I f t he obj ect , im per sonat ing t he client ,
196
t r ied t o access anot her m achine while claim ing t o be t he client , it would fail t o aut hent icat e it self as t he client because it does not know t he client ’s password. I f t he obj ect and t he client w ere on t he sam e m achine, t he obj ect im per sonat ing t he client could m ak e one net wor k hop t o anot her m achine, since t he m achine it resides on could st ill aut hent icat e t he client ident it y—but it could go no fur t her. 7 .4 .5 .4 I m p er son a t ion = D eleg at e
The only difference bet ween delegat ion and im personat ion is t hat wit h delegat ion, t he obj ect can freely access any obj ect on any m achine as t he client . I f any of t hese serv er obj ect s use delegat ion, t he client ident it y could be pr opagat ed furt her and furt her down t he call chain. Delegat ion is possible because Windows 2000 can use t he Kerberos aut hent icat ion serv ice, which uses a different aut hent icat ion m et hod t han challenge/ response. Bot h t he client and server user account s m ust be configur ed in t he Act ive Direct ory proper ly t o suppor t delegat ion, [ 1] ( in addit ion t o t he client gr ant ing aut hor it y t o do delegat e- level im personat ion) , due t o t he enorm ous t r ust ( and hence, secur it y r isk) involv ed. Delegat ion uses, by default , anot her secur it y ser vice called cloaking, which pr opagat es t he caller ident it y along t he call chain. Delegat ion is ext r em ely dangerous from t he client per spect ive because t he client has no cont rol over who uses it s ident it y or wher e. When t he im personat ion lev el is set t o I m personat e, t he client t ak es a calculat ed r isk because it knows which obj ect s it w as accessing. I f t hose obj ect s are on a differ ence m achine, t he client ident it y could not have propagat ed across t he net work . [ 1]
For m ore inform at ion, see Windows 2000 Adm inist rat ion in a Nutshell by Mit ch Tulloch ( O’Reilly, 2000) .
7 .5 Se cu r in g a Libr a r y Applica t ion A libr ar y applicat ion is host ed in it s client pr ocess. As such, it has no cont rol over t he host ing applicat ion ident it y and securit y set t ings. I t runs under t he ident it y of t he host ing pr ocess ( t he I dent it y t ab is st ill pr esent in t he applicat ion's propert ies page, but it is grayed out and ignor ed) . Thus, t he library applicat ion has only as m uch privilege as t he host ing client does. This lim it at ion m ay be significant because t he libr ar y could be loaded by m any different client s and m ay not alway s have sufficient cr edent ials t o do it s work . As a rule of t hum b, put y our m eaningful business logic processing com ponent s in a server applicat ion, w her e you can configure exact ly t he applicat ion securit y ident it y . Deploy a library applicat ion in sit uat ions when you ex pect very a int ensive calling
197
pat t ern fr om y our client s and when you can filt er or process t he calls befor e for warding t hem t o t he ser ver applicat ion, where t he real work should t ak e place. Anot her ident it y - relat ed lim it at ion is t hat a librar y applicat ion cannot declar e an im personat ion lev el, so it norm ally uses t he process- wide im personat ion lev el. The libr ary applicat ion can set a desir ed aut hent icat ion and im personat ion lev el program m at ically , as descr ibed in Sect ion 7.8 lat er in t he chapt er . A libr ar y applicat ion has no cont r ol over t he pr ocess- level secur it y set t ings, and t he only way for it t o per for m it s own secur it y access checks is t o em ploy com ponent - level role- based securit y ( rolebased securit y at t he com ponent level is t he sam e as wit h a ser ver applicat ion) . Before y ou dive int o t he det ails of securing a libr ary applicat ion, consider t he following point : because t he library applicat ion is loaded int o t he client process, it has access t o all t he process resources, m em or y, obj ect s, GI T, handles, et c. The client should be very car eful when loading a libr ary applicat ion, as it m ay cont ain m alicious obj ect s. Agreeing t o use a libr ary applicat ion im plies t hat t he client has a lev el of t r ust and fam iliar it y of t he librar y applicat ion. Once y ou set an applicat ion t o be a libr ar y applicat ion, t he applicat ion’s Securit y t ab will be different fr om t hat of a server applicat ion ( see Figur e 7- 12) . Figu r e 7 - 1 2 . A lib ra ry a p plicat ion ’s Se cu rit y t a b
Not iceable by t heir absence are t he aut hent icat ion and im personat ion lev els cont r ols, replaced wit h a single " Enable aut hent icat ion" checkbox. The aut horizat ion checkbox and t he securit y - level radio but t ons offer t he sam e funct ionalit y as wit h a
198
server applicat ion. I f you want t o enable role- based secur it y, t he aut hor izat ion check box m ust be checked and t he securit y level r adio but t on m ust be at t he lower posit ion. This posit ion inst ruct s COM+ t o perform access checks at t he com ponent lev el. The int erest ing it em on t his t ab is t he " Enable aut hent icat ion" checkbox. The client process host ing t his libr ar y applicat ion can hav e an aut hent icat ion level already configured for it . The library applicat ion can t ak e advant age of t he process- wide aut hent icat ion and have COM+ use it t o aut hent icat e calls com ing from out side t he process t o t he librar y applicat ion. However , t he library applicat ion has no cont r ol over how rigorous t hat aut hent icat ion is. The process- level aut hent icat ion m ay even be set t o None. The im m ediat e conclusion is t hat in a library applicat ion, you should avoid perform ing sensit ive work t hat r equires aut hent icat ion. Ther efore, you have at y our disposal t wo m echanism s t o secure your library applicat ion: pr ocess- wide aut hent icat ion and com ponent - level r ole- based access cont r ol, and you can t ur n each on or off independent ly of t he ot her. These m echanism s giv e you four configur at ion opt ions, discussed in t he following sect ions. 7 .5 .1 Bot h Role - Ba se d Se cur it y a nd Globa l Au t h e n t ica t ion Your t ypical secur it y set t ing for a COM+ libr ar y applicat ion has bot h role- based securit y and global aut hent icat ion enabled. All calls fr om out side t he process ar e aut hent icat ed, whet her t hey ar e dest ined for t he libr ary applicat ion or som e ot her COM obj ect in t he process ( see Figure 7- 13) . I n addit ion, COM+ uses com ponent - lev el access securit y and v er ifies t hat t he caller is a m em ber of a role t hat was grant ed access t o t he com ponent . However, calls from wit hin t he host ing process are not aut hent icat ed. I f t he host ing pr ocess claim s t o r un under t he ident it y of Joe Cust om er , and Joe is a m em ber of a role t hat was gr ant ed access t o a com ponent , client s in t he host ing applicat ion can access obj ect s in t he libr ar y applicat ion fr eely. This access opens t he way for a m alicious client pr ocess t o load t he librar y applicat ion and call int o it unaut hent icat ed. This secur it y gap is present in t he ot her t hr ee configur at ion set t ings as well. This lack of securit y is yet anot her reason t o avoid perfor m ing sensit iv e work t hat r equires aut hent icat ion in a libr ary applicat ion. Figu re 7 - 1 3 . En ab lin g p roce ss- leve l a u t h en t ica t ion an d role - b a sed se cu r it y
199
7 .5 .2 Globa l Au t h e n t ica t ion W it hou t Role - Ba se d Se cu r it y When im por t ing an ex ist ing set of legacy COM com ponent s t o a COM+ librar y applicat ion ( per haps t o be int egrat ed in a bigger dev elopm ent , deploym ent , and adm inist r at ion fram ewor k) , t he im port ed legacy com ponent s do not use r ole- based secur it y, and enfor cing it m ay int r oduce side effect s, because t hose com ponent s m ay alr eady have t heir own access cont r ol m echanism s. I t t his case, you can t urn off r ole- based securit y for t he libr ar y applicat ion. As a result , client calls from out side and inside t he process access t he com ponent s direct ly . However, you st ill m ay want t o t ak e adv ant age of t he global aut hent icat ion t hat m ay be in place, t o aut hent icat e caller s fr om out side t he process ( see Figure 7- 14) . Figu re 7 - 1 4 . Disab lin g role - b a sed se cu r it y w h ile re lyin g on global a u t h e n t icat ion
200
Since y ou can t ur n off role- based securit y at t he com ponent lev el as well, I recom m end not disabling r ole- based securit y at t he libr ar y applicat ion level. I n t he fut ur e, you m ay want t o add com ponent s t o t he library applicat ion t hat do require role- based securit y. As a r ule, always enable securit y at t he highest level possible, and disable securit y at t he lowest lev el possible. 7 .5 .3 Role - Ba se d Se cu r it y W it h ou t Globa l Au t h e n t ica t ion Suppose your host ing process uses a st rict aut hent icat ion level, or at least one t hat is st r ict er t han what your libr ary applicat ion needs. Your applicat ion ends up paying a perfor m ance hit for a ser vice it does not r equir e. You can choose t o disable global aut hent icat ion suppor t for your applicat ion and exem pt all calls fr om out side t he host ing process t o your libr ar y applicat ion ( see Figure 7- 15) . However, you should st ill use role- based secur it y t o cont rol access t o y our applicat ion. Of cour se, t her e is a downside t o disabling aut hent icat ion: you cannot t ell if t he caller s are who t hey say t hey are. You can only decide whet her t o grant t he caller access, assum ing t he caller s are indeed w ho t hey say t hey are. Figu r e 7 - 1 5 . D isa b lin g a u t h en t ica t ion for t h e libr ar y a pp lica t ion w h ile u sing r ole- ba se d secu rit y
201
This configur at ion is usually of lit t le use, as t he m ain m ot iv at ion for configuring an applicat ion as a library is t o av oid fr equent crossprocess calls fr om client s. I f t he volum e of calls fr om out side t he process is an issue, t hen j ust configure t he applicat ion as a server applicat ion, and hav e your ow n process- wide aut hent icat ion level. This configur at ion has anot her serious pr oblem : it has t he pot ent ial for a securit y breach. Since calls int o t he library applicat ion ar e not aut hent icat ed, what happens if a com ponent in t he library applicat ion, while ex ecut ing a m et hod on behalf of an out - ofprocess caller, t ries t o access an obj ect in t he host ing pr ocess ( m aybe t o fire an ev ent on) ? I nt raprocess calls are not aut hent icat ed because all obj ect s in t he pr ocess share t he sam e ident it y . Thus, t he out side call can by pass t he process- wide aut hent icat ion. This bypass only st r engthens t he idea t hat when host ing a librar y applicat ion, t he client pr ocess should be on guar d, should load only libr ary applicat ions it knows ar e benign, and should m inim ize t heir int eract ion wit h ot her obj ect s in t he pr ocess. At t he ver y least , t he host ing serv er- applicat ion should use role- based securit y , since crossing applicat ion boundar ies for ces access checks and t he call fr om t he libr ary applicat ion is m ade across an applicat ion boundary. I t w ill not get you aut hent icat ion, but it will giv e you som e access cont rol. 7 .5 .4 N e it he r Role - Ba se d Se cu r it y n or Au t h e n t ica t ion Surpr isingly , disabling bot h role- based secur it y and process- level aut hent icat ion can be useful. I m agine a sit uat ion in w hich com ponent s from your library applicat ion ar e host ed by a brow ser and have t o accept calls from anonym ous, unaut hent icat ed callers. The process- wide aut hent icat ion has t o be disabled t o allow caller s
202
t hat cannot be aut hent icat ed t o go t hr ough; role- based secur it y cannot be used because you cannot add t he anony m ous callers t o your roles. By t ur ning t he securit y knob all t he way dow n, all calls int o your libr ar y applicat ion w ill always be grant ed access ( see Figure 7- 16) . Figu r e 7 - 1 6 . Tu r n ing off p a rt icip a t ion in process- w id e a u t h e n t ica t ion a n d role - ba sed se cu r it y
7 .6 Pr ogr a m m a t ic Role - Ba sed Se cu r it y Som et im es, adm inist rat ive r ole- based securit y it not granular enough for t he t ask at hand. Consider a sit uat ion in which y our applicat ion m aint ains a privat e r esource ( such as a dat abase) t hat does not expose any public int erfaces dir ect ly t o t he client s. You st ill want t o allow only som e caller s of a m et hod t o access t he r esource and deny access t o ot her callers who ar e not m em ber s of a specific role. The second ( and m ore com m on) sit uat ion is when a m et hod is inv oked on y our obj ect and you want t o know whet her t he caller is a m em ber of a part icular r ole so you can bet t er handle t he call. To illust rat e t he second sit uat ion, suppose in t he bank exam ple, one of t he requir em ent s is t hat a cust om er can t ransfer m oney only if t he sum inv olved is less t han $5,000, whereas m anager s and t ellers can t ransfer any am ount . Declar at ive role- based securit y goes down only t o t he m et hod level ( not t he param et er level) and can only assure you t hat t he caller is a m em ber of at least one of t he r oles you have grant ed access t o. To im plem ent t he r equirem ent , you m ust find out t he caller’s r ole program m at ically . For t unat ely, COM+ m akes it easy t o do j ust t hat . Rem em ber t hat every m et hod call is r epr esent ed by a COM+ call
203
obj ect ( discussed in Chapt er 2) . The call obj ect im plem ent s an int er face called ISecurityCallContext, obt ained by calling CoGetCallContext( ). ISecurityCallContext provides a m et hod called IsCallerInRole( ), which let s you verify t he caller’s role m em bership. IsCallerInRole( ), is available on IObjectContext, a legacy fr om MTS as w ell. Exam ple 7- 1 show s how t o im plem ent t he new r equirem ent using t he call obj ect securit y int er face. Ex a m p le 7 - 1 . V e rifyin g t h e ca lle r m e m be rsh ip by ca llin g I Se cu r it yCa llCon t e x t ::I sCa lle r I n Role ( )
STDMETHODIMP CBank::TransferMoney(int nSum,DWORD dwAccountSrc,DWORD dwAccountDest) { HRESULT hres = S_OK; ISecurityCallContext* pSecurityCallContext = NULL; _bstr_t bstrRole = "Customer" ; VARIANT_BOOL bInRole = FALSE; hres = ::CoGetCallContext(IID_ISecurityCallContext, (void**)&pSecurityCallContext); if(pSecurityCallContext == NULL) { //No security call context available, role-based security not in use return E_FAIL; } hres = pSecurityCallContext>IsCallerInRole(bstrRole,&bInRole); pSecurityCallContext->Release( ); if(bInRole)//The caller is a customer { if(nSum > 5000) return E_FAIL; } return DoTransfer(nSum,dwAccountSrc,dwAccountDest);//Helper method }
7 .7 Se cu r it y Boun da r ie s COM+ m ak es a sensible assum pt ion: t wo com ponent s fr om t he sam e applicat ion t r ust each ot her, and int ra- applicat ion securit y is not necessary . As a r esult , secur it y is checked only at applicat ion boundar ies. When t w o applicat ions int eract , a secur it y check ex ist s bet ween t hem . For exam ple, in t he case of a library applicat ion t hat was loaded int o a serv er applicat ion, t here is an applicat ion
204
boundary, and t hus a securit y boundary, bet ween t hem . When a client accesses t he library applicat ion in t he host ing pr ocess, COM+ ver ifies t hat t he client has access t o t he library applicat ion com ponent . When a client fr om t he libr ar y applicat ion calls a com ponent in t he host ing process, COM+ uses t he host ing applicat ion’s r ole- based secur it y. The sam e is t r ue when t w o library applicat ions int er act wit h each ot her while bot h share t he sam e host ing process. You can draw a design conclusion fr om t his behavior: if you have t wo com ponent s and you want secur it y checks done when one calls t he ot her, put t hem each in separat e COM+ applicat ions. As y ou have seen, each COM+ m et hod inv ocat ion has a call cont ext obj ect associat ed wit h it . COM+ will not updat e t he securit y call cont ext when no securit y boundary is crossed. I f one com ponent has done progr am m at ic role- based securit y and is about t o call anot her com ponent in t he sam e applicat ion, repeat ing t he r ole m em bership verificat ion is redundant , as no new securit y cont ex t infor m at ion will be present .
M or e on I Se cur it yCa llCont e x t For m ost pract ical purposes, finding out whet her t he caller is a m em ber of a role is t he only part of COM+ secur it y you will ever deal w it h program m at ically. How ever, ISecurityCallContext prov ides you ot her ext ensive securit y infor m at ion det ails, including: • •
• • •
The t ot al num ber of callers in t he chain of calls leading down t o t his obj ect . The m inim um aut hent icat ion lev el used t o aut hent icat e callers in t he calling chain. Ev en if t he im m ediat e caller int o t his applicat ion was proper ly aut hent icat ed, previous caller s could have been subj ect ed t o less st ringent aut hent icat ion. This m ay or m ay not be an issue in your business logic. I nfor m at ion about whet her a part icular user is a m em ber of a role. The direct caller’s securit y ident it y . The original caller ’s secur it y ident it y.
7 .8 Adva n ced COM + Se cu r it y On t op of incredibly rich, user - friendly adm inist r at iv e support for all your secur it y needs, COM+ pr ovides low- level, advanced
205
program m at ic securit y capabilit ies. These feat ur es cat er t o com plex securit y needs. However, I have found t hat t here is alm ost alway s a good design solut ion t hat let s m e use COM+ configur able set t ings wit hout having t o resort t o adv anced, program m at ic, low- level securit y m anipulat ion. I n fact , y ou can probably lead a product iv e and fulfilling developm ent career using COM+ wit hout using lowlev el securit y m anipulat ion at all. I f what you have read so far fulfills your requirem ent s, feel free t o sk ip t his sect ion and m ov e t o t he conclusion of t his chapt er and it s account of t he ever- present pit falls of COM+ secur it y. I f not , cont inue reading. 7 .8 .1 Se r ve r - Sid e I m p e r son a t ion Set t ing t he allowed im per sonat ion level is a client - side configurat ion, in which t he client declares t he level of t rust it has t oward t he serv er . Configuring t he im personat ion level is not an adv anced securit y m easure; it is a necessar y precaut ion because you cannot know what t he server is up t o and whet her it int ends t o im personat e t he client . How ever, ser ver- side im per sonat ion is adv anced securit y . You should be aware t hat serv er- side im personat ion is not an ext ensible or scalable design approach. Each COM+ applicat ion should be configur ed wit h enough credent ials ( t hat is, a securit y ident it y ) t o perfor m it s work , and should not rely on t he client ’s cr edent ials by im per sonat ing it . I m per sonat ion couples t he server applicat ion t o t he client ’s ident it y and prevent s t he applicat ion from evolv ing independent ly . I n alm ost all cases when im personat ion is a cr it ical par t of t he applicat ion design, t he design is not scalable. Consider, for exam ple, an applicat ion in which t he dat abase per form s it s own aut hent icat ion and aut horizat ion of end users t o secure access t o dat a in t he dat abase. Middle- t ier obj ect s have t o im personat e t he caller t o access t he dat abase, r esult ing in a program m ing m odel t hat is t ight ly coupled t o t he ident it y of t he callers ( bank t ellers can only access t he account s t hey ar e responsible for ) . Adding new user s is not t r ivial, and t her efor e does not scale. A bet t er design decision would be t o have t he dat abase aut hent icat e j ust t he COM+ applicat ions accessing it and t rust t he applicat ions t o aut hent icat e and aut hor ize t he client s secur ely. Allocat ing dat abase connect ions per user is anot her exam ple of when using im per sonat ion is not scalable. The m iddle- t ier obj ect s hav e t o im personat e t he user t o get a connect ion. Consequent ly, t he connect ions cannot be shared ( no connect ion pooling) and t he t ot al num ber of users t he sy st em can handle is dr ast ically r educed. One m ore im personat ion liabilit y is perfor m ance—im per sonat ing t he client can be m uch slower t han m aking t he call direct ly under t he applicat ion ident it y. I f t he client does not have enough credent ials t o access a resour ce, t he call fails downst ream , when t he
206
im personat ing obj ect t r ies t o access t he resource, inst ead of upst ream , when t he client first accesses t he obj ect . I m personat ion m ay also involve int ensive under- t he- hood t raffic and v alidat ions. I f you decide t o use im personat ion, do so j udiciously, and only for t he pur pose of obt aining t he client ’s ident it y t o verify access t o a sensit iv e resour ce t he ser ver applicat ion has independent access t o. Once t he server has ver ified t hat t he client has enough credent ials, t he serv er obj ect should r evert t o it s own ident it y and access t he resource. The call cont ext obj ect support s anot her int erface called IServerSecurity. The server can access IServerSecurity by calling CoGetCallContext( ). Rem em ber t hat t he point er t o IServerSecurity w ill only be v alid for t he dur at ion of t he curr ent call and cannot be cached. To im per sonat e t he calling client , t he ser ver should call IServerSecurity::ImpersonateClient( ). To rever t back t o it s original ident it y, t he server should call IServerSecurity::RevertToSelf( ). Exam ple 7- 2 shows a server obj ect im per sonat ing a client t o verify t hat t he client has access right s t o cr eat e a file. I f it does, t he server rev er t s t o it s original ident it y and creat es t he file under it s own ident it y. Ex a m p le 7 - 2 . Th e se r ve r im p e rson a t in g t he clien t t o ve rify file cr ea t ion acce ss r ig h t s
STDMETHODIMP CMyServer::CreateFile(BSTR bstrFileName) { HRESULT hres = S_OK; IServerSecurity* pServerSecurity = NULL; hres = ::CoGetCallContext(IID_IServerSecurity,(void**)&pServerSe curity); ASSERT(pServerSecurity); hres = pServerSecurity->ImpersonateClient( ); HANDLE hFile = ::CreateFile(_bstr_t(bstrFileName),STANDARD_RIGHTS_ALL,0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); //Do the cleanup first, before the error handling ::CloseHandle(hFile);//Does not change the value of hFile hres = pServerSecurity->RevertToSelf( ); pServerSecurity->Release( );
207
if(hFile == INVALID_HANDLE_VALUE)//failure due to lack of access rights //as well as anything else that can go wrong { return E_FAIL; } //The client has the right access rights to this file, now create it again //under the server’s own identity //m_hFile is a member of this object m_hFile = ::CreateFile(_bstr_t(bstrFileName),STANDARD_RIGHTS_ALL,0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if(m_hFile == INVALID_HANDLE_VALUE)//Something went wrong { return E_FAIL; } return hres; } COM+ provides t w o helper funct ions t o aut om at e coding sequences lik e t he one in Exam ple 7- 2. CoImpersonateClient( ) creat es t he server securit y obj ect , im personat es t he client , and releases t he server securit y obj ect . CoRevertToSelf( ) sim ilarly creat es t he server securit y obj ect , rever t s t o t he server’s original ident it y , and releases t he server secur it y obj ect . Ex am ple 7- 3 shows t he sam e sequence as in Exam ple 7- 2, using t he helper funct ions. Even t hough t he code in Exam ple 7- 3 is m ore concise and readable t han Ex am ple 7- 2, you should be aware of a slight per form ance penalt y t hat using t he im personat ion helper funct ions int r oduces. I n Exam ple 7- 2, t he ser ver secur it y obj ect is only cr eat ed and r eleased once, while it is done t wice in Exam ple 7- 3. Nevert heless, I recom m end using t he helper funct ions because t hat penalt y is t r uly m iniscule and readable code is always essent ial. Ex a m p le 7 - 3 . V e rifyin g file cr ea t ion a cce ss r igh t s w it h CoI m p e rson a t e Clie n t ( ) a n d CoRe ver t ToSe lf( )
STDMETHODIMP CMyObj::CreateFile(BSTR bstrFileName) { HRESULT hres = S_OK;
208
hres = ::CoImpersonateClient(
);
HANDLE hFile = ::CreateFile(_bstr_t(bstrFileName),STANDARD_RIGHTS_ALL,0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); ::CloseHandle(hFile);//Does not change the value of hFile hres = ::CoRevertToSelf( ); if(hFile == INVALID_HANDLE_VALUE)//failure due to lack of access rights as well //as anything else that can go wrong { return E_FAIL; } //The client has the right access rights to this file, now create it again //under server own identity //m_hFile is a member of this object m_hFile = ::CreateFile(_bstr_t(bstrFileName),STANDARD_RIGHTS_ALL,0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if(m_hFile == INVALID_HANDLE_VALUE)//Something went wrong { return E_FAIL; } return hres; }
7 .8 .2 Pr ogr a m m a t ic Clie n t - Side Se cu r it y Every client - side proxy support s an int erface called IClientSecurity, which let s t he client set t he secur it y at t ribut es on t he com m unicat ion channel bet ween it and t he obj ect behind t hat proxy. COM+ calls t he set of secur it y at t ribut es ( such as aut hent icat ion and im per sonat ion levels) a securit y blanket . Using t he IClientSecurity m et hod SetBlanket( ), t he client can set a new aut hent icat ion level, a new im personat ion lev el, or bot h. I t can
209
also set ot her securit y at t ribut es. Howev er , t he proxy m ay be shared am ong a few client s, and not all of t hem m ay be int erest ed in a new secur it y blank et . COM+ allows a client t o clone a personal copy of t he proxy for it s own use, using anot her m et hod of I Client Secur it y called CopyProxy( ) t hat gives t he client a priv at e new pr oxy. You can set a secur it y blank et wit hout cloning your own proxy, but it is r ecom m ended t hat you clone it . Set t ing a securit y blank et m ay be useful in a few sit uat ions: •
•
When t he global applicat ion secur it y level is not gr anular enough. For exam ple, som e m et hods m ay requir e addit ional aut hent icat ion. I n t he bank ex am ple, t he client m ay want t o set an ex plicit high aut hent icat ion level for t he TransferMoney( ) m et hod but use what ever secur it y level t he applicat ion uses for GetBalance( ). When a librar y applicat ion is at t he m er cy of t he host ing process. I f t he library applicat ion is a client of ot her obj ect s, t hough, it can set it s own aut hent icat ion and im per sonat ion lev els using IClientSecurity.
COM+ provides helper funct ions t o aut om at e coping a proxy ( t he CoCopyProxy( ) funct ion) and set t ing a securit y blanket ( t he CoSetProxyBlanket( ) funct ion) . Ex am ple 7- 4 shows a client of t he bank applicat ion, copies t he pr oxy, and set s explicit im personat ion and aut hent icat ion levels for t he TransferMoney( ) m et hod, using t he helper funct ions. Ex a m p le 7 - 4 . Se t t in g e xp licit au t h en t ica t ion a nd im p er son at ion le vels for t h e Tr an sfe r M on e y( ) m et h od
HRESULT hres = S_OK; //pAccountsManager is initialized somewhere else IAccountsManager* pPrivateAccountsManager = NULL;//This client private copy //copying the proxy to get a private copy, so not to interfere with other clients hres = ::CoCopyProxy(pAccountsManager,(IUnknown**)&pPrivateAccou ntsManager); //Setting explicit authentication and impersonation levels hres = ::CoSetProxyBlanket(pPrivateAccountsManager,//The private proxy RPC_C_AUTHN_DEFAULT,//The system default authentication RPC_C_AUTHZ_DEFAULT ,//Default authorization 210
NULL, //Use authentication level "Packet Integrity" RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, //Impersonation level is "Identify" RPC_C_IMP_LEVEL_IDENTIFY, NULL,//Use process identity EOAC_DEFAULT);//Default capabilities hres = pPrivateAccountsManager>TransferMoney(1000,1234,5678); pPrivateAccountsManager->Release( );//Release private copy I would advise t hat y ou always prefer aut om at ic, adm inist rat iv e, declarat ive securit y rat her t han doing securit y wit hin com ponent s, whet her it is on t he server or t he client side. Following t his sim ple rule will m ak e it easier t o: • • •
Writ e and m aint ain com ponent s Design consist ent securit y across an ent ir e applicat ion Modify an applicat ion’s secur it y policy w it hout recoding it
7 .9 COM+ Se cu r it y Pit fa lls Dist ribut ed sy st em s securit y is a vast , int r icat e t opic, and cer t ainly COM+ m ak es it possible for m er e m ort als t o secure sy st em s in an elegant , pr oduct iv e, and ext ensible m anner . All you hav e t o do is underst and a few sim ple securit y concept s, configure your applicat ions proper ly , and let COM+ t ake care of t he rest . How ever, no ser vice is wit hout a flaw , and COM+ securit y is no except ion. Even t hough t he following list of pit falls m ay seem long, you should consider t wo t hings: fir st , consider ing how encom passing COM+ securit y really is, it is a sur prisingly sm all list , as secur it y affect s alm ost everyt hing you do in COM+ . Second, t his list describes only t hings I have encount ered, and it is probably only par t ial. You will undoubt edly encount er ot her var iat ions and pit falls when you do your own developm ent . Howev er , w it h a solid under st anding of t he way COM+ securit y work s, y ou should be able t o isolat e and t r oubleshoot t he problem s yourself. Som e of t he pit falls have alr eady been im plied t hr oughout t his chapt er, but t he following is dedicat ed and explicit pit fall list . 7 .9 .1 M a ch in e - W ide Se cu r it y Se t t in gs
211
At t he r oot of t he Com ponent Serv ices Ex plorer is t he My Com put er it em , which let s you set global configur at ions for y our com put er . I f you have adm inist r at ive privileges on ot her m achines, y ou can add t hem t o t he list of m achines m anaged by t he Com ponent Serv ices Explorer. Each com put er icon has a propert ies page wit h t wo t abs t hat are seem ingly relev ant t o COM+ secur it y: t he Default Secur it y t ab and t he Default Pr opert ies t ab. Though t hese t abs are par t of t he Com ponent Ser vices Explor er, t hey have lit t le or not hing t o do wit h COM+ applicat ions and are t he reincar nat ion of DCOMCNFG.EXE, t he awkw ard DCOM configurat ion ut ilit y . The Default Securit y t ab has no bear ing on COM+ applicat ions. I t is used t o cont r ol default access and launch per m ission for classic COM local ser vers. The Default Propert ies t ab is m ost ly irr elevant for COM+ applicat ions. I t is used t o set default aut hent icat ion and im per sonat ion levels for COM local serv er processes t hat can be accessed rem ot ely . I f t hose pr ocesses were t o int er act w it h COM+ applicat ions as client s t o t he configured obj ect s ( locally or rem ot ely) and did not provide t heir ow n secur it y configurat ions ( adm inist r at iv ely or program m at ically ) , t hen t hese set t ings would be used. I t shor t , neit her t ab is relevant t o COM+ applicat ions. 7 .9 .2 Ca llin g CoI n it ia liz e Se cu r it y( ) I f you used DCOM securit y before, calling CoInitializeSecurity( ) is second nat ur e t o you. I n t he old DCOM day s, CoInitializeSecurity( ) was t he gat eway t o m anageable securit y , and any properly writ t en DCOM ser ver called it t o ensure t hat t he r equir ed securit y lev els would be used. However , a configured com ponent has no point in calling CoInitializeSecurity( ) because any configured com ponent is loaded in a host ing pr ocess. I f t he com ponent is par t of a server applicat ion, COM+ calls CoInitializeSecurity( ) when t he process is creat ed, wit h t he applicat ion global securit y set t ings as par am et ers. I f t he com ponent is par t of a librar y applicat ion, t he host ing process calls CoInitializeSecurity( ) before doing any t hing else wit h COM. Ot herwise, COM would hav e called CoInitializeSecurity( ) for it . CoInitializeSecurity( ) m ay be an issue when im por t ing an exist ing COM local server t o COM+ . I f t he port ed server used CoInitializeSecurity( ), y ou m ust r em ove t he call fr om t he code, look at t he par am et ers for CoInitializeSecurity( ), and configure t he global applicat ion secur it y levels accor dingly. 7 .9 .3 D isa blin g Ch a n ge s t o t he Applica t ion Con figu r a t ion
212
A perm ission propert ies gr oup is locat ed on ev ery COM+ applicat ion’s Adv anced t ab ( see Figur e 7- 17) . By select ing " Disable changes," you can pr ev ent any body from m ak ing changes t o y our applicat ion set t ings ( and any changes at t he com ponent , int erface, and m et hod level) , including t he securit y set t ings and access policy. The problem is t hat t his checkbox is not password prot ect ed, and any one wit h adm inist rat ive priv ileges can m odify y our precious securit y set t ings and int roduce securit y gaps in your applicat ion. Cust om er- side adm inist rat ors ( who are not your product adm inist rat ors) m ay be t em pt ed t o change your securit y set t ings t o accom m odat e som et hing else in t he sy st em , or j ust t o fool around wit h your applicat ion. Be awar e of t his sit uat ion. This checkbox is t here for a r eason, and I wonder why Micr osoft did not t ake an ext ra st ep and m ake it password pr ot ect ed. Figu re 7 - 1 7 . Disab lin g an d en ab lin g ch a nges t o you r a pplica t ion
7 .9 .4 Avoid Se n sit iv e W or k a t t h e Obj e ct Con st r uct or I m agine a sit uat ion in which a client is gr ant ed access ( using rolebased securit y ) t o one com ponent in your applicat ion, Com ponent A, but is not grant ed access t o anot her com ponent , Com ponent B. When t he client t r ies t o creat e Com ponent B, COM+ creat es t he obj ect , but only let s t he client access t he IUnknown m et hods of Com ponent B and denies access t o m et hods on any ot her int erface. As explained in t he Launch Cont r ol definit ion at t he beginning of t his chapt er , t his process int ent ionally avoids a DCOM pit fall. This pit fall allows a client t o creat e a new obj ect in a new process, but forget s
213
t o grant t he client access t o t he obj ect s inside. This pit fall r esult ed in a zom bie process because t he client could not ev en call IUnknown::Release( ) on t he obj ect it j ust creat ed. However, because t he client is allowed t o cr eat e t he obj ect , it im plies t hat t he const ruct or of Com ponent B act ually execut es code on behalf of a client t hat is not allowed t o access t he com ponent . I f you do any sensit ive work at t he obj ect const ruct or, it m ay const it ut e a secur it y br each because t hat wor k should never be done for t hat client . The obvious conclusion is t o avoid doing any sensit iv e wor k in t he obj ect const r uct or, such as er asing or opening sensit iv e files or cr eat ing sensit ive account s in a dat abase. 7 .9 .5 Ch a n gin g Applica t ion Act iva t ion M ode When y ou swit ch bet w een applicat ion act iv at ion m odes ( for exam ple, from a library t o a serv er applicat ion) , COM+ pr esent s you wit h t he enigm at ic m essage box shown in Figur e 7- 18. The m essage box war ns you t hat cert ain proper t ies will be set t o t heir default values. Those set t ings are m ost ly secur it y pr opert ies t hat t he library applicat ion does not have, such as aut hent icat ion and im personat ion set t ings. Aft er changing t he applicat ion act ivat ion m ode, go t hrough t he securit y set t ings and m ake sure t he default values COM+ picked up for you are what your design calls for , and set t hem t o t he corr ect values if y ou need t o. Figu r e 7 - 1 8 . COM + w a rn s you t h a t som e se t t in gs h a ve b e en se t t o t h e ir de fau lt valu e s
7 .9 .6 I sCa lle r I n Role ( ) Re t ur n s TRUE W h e n Se cur it y I s N ot En a ble d
214
Program m at ic r ole- based securit y, as you have seen, is used t o ver ify t he caller’s m em bership in a par t icular role. However , rolebased securit y m ust be enabled properly for ISecurityCallContext::IsCallerInRole( ) t o ret urn accur at e result s. I n t he following cases, IsCallerInRole( ) alw ays r et urns TRUE, regardless of t he act ual caller r ole m em ber ship: •
•
Role- based secur it y is enabled at t he applicat ion level, but not enfor ced at t he com ponent level, because t he " Enforce com ponent level access check s" checkbox ( shown in Figur e 73) is not select ed. Calls t o ISecurityCallContext::IsCallerInRole( ) from wit hin t he com ponent alway s ret ur n TRUE. At t he applicat ion lev el, aut horizat ion is not enforced because t he " Enforce access checks for t his applicat ion" checkbox ( shown in Figur e 7- 10) is not checked. All calls t o ISecurityCallContext::IsCallerInRole( ) will alway s ret ur n TRUE, ev en if com ponent level access check s are enabled.
IsCallerInRole( ) m isbehaves in bot h libr ar y and serv er applicat ions when one of t hese t wo sit uat ions occur s. To ov er com e t his m isbehavior , you should call anot her m et hod of ISecurityCallContext t o v er ify t hat secur it y is enabled befor e checking role m em bership. This m et hod is called IsSecurityEnabled( ), and is available specifically for t hese cases. Exam ple 7- 5 shows t he sam e code as Exam ple 7- 1, ex cept t his t im e IsSecurityEnabled( ) is used fir st . Ex a m p le 7 - 5 . V e rifyin g t h a t secu rit y is e n a bled be fore ch eck in g t h e calle r role m em be r sh ip
STDMETHODIMP CBank::TransferMoney(int nSum,DWORD dwAccountSrc,DWORD dwAccountDest) { HRESULT hres = S_OK; ISecurityCallContext* pSecurityCallContext = NULL; _bstr_t bstrRole = "Customer" ; VARIANT_BOOL bInRole = FALSE; VARIANT_BOOL bSecurityEnabled = FALSE; hres = ::CoGetCallContext(IID_ISecurityCallContext, (void**)&pSecurityCallContext); if(pSecurityCallContext == NULL) { //No security call context available, role-based security not in use
215
return E_FAIL; } hres = pSecurityCallContext>IsSecurityEnabled(&bSecurityEnabled); if(!bSecurityEnabled) { pSecurityCallContext->Release( ); return E_FAIL; } hres = pSecurityCallContext>IsCallerInRole(bstrRole,&bInRole); pSecurityCallContext->Release( ); if(bInRole)//The caller is a customer { if(nSum > 5000) return E_FAIL; } return DoTransfer(nSum,dwAccountSrc,dwAccountDest);//Helper method } 7 .9 .7 D isa blin g Applica t ion - Le ve l Aut hor iz a t ion When y ou disable applicat ion- lev el aut hor izat ion, ev en if a com ponent is set t o use and enforce r ole- based securit y ( as in Figure 7- 3) , all calls t o t hat com ponent will be per m it t ed, regardless of t he caller ’s ident it y and role m em bership. This sit uat ion is v ery dangerous, as t he com ponent , by design, m ay requir e access cont rol and does not have anot her m echanism in place t o im plem ent access cont rol requir em ent s. I n addit ion, unlike t he case of set t ing t he securit y level t o processwide only ( which disables com ponent level role- based securit y and allows all calls) , t he com ponent securit y t ab w ill not be gr ay ed out as in Figure 7- 11. Always leave t he applicat ion- lev el aut hor izat ion enabled. 7 .9 .8 En a bling Applica t ion- Le ve l Au t h or iz a t ion As explained in t he previous pit fall, y ou should alway s enable applicat ion- level aut horizat ion. However, what happens if, in your applicat ion, you hav e a num ber of com ponent s t hat require r olebased securit y and a few ot her com ponent s t hat do not ? The com ponent s t hat do not r equire access cont rol m ay serve a differ ent set of client s alt oget her. Applicat ion- level aut horizat ion is problem at ic because when a call com es int o an applicat ion, COM+ ver ifies t hat t he caller is a m em ber of at least one role defined for t his applicat ion. I f t he caller is not a m em ber, COM+ denies t he
216
caller access, even if t he caller t r ies t o access a com ponent t hat does not r equir e access cont r ol. Ther e are t wo ways ar ound t his pit fall. The first is t o m ov e t he com ponent s t hat do not r equire role- based securit y t o a separat e applicat ion. The second solut ion sim ply defines a new r ole in your applicat ion called Place Holder and adds j ust one user t o it : t he Everyone group ( see Figure 7- 19) . Now all callers ar e m em bers of at least one role, and com ponent s t hat do not requir e role- based securit y can accept calls fr om any user while applicat ion- wide aut hor izat ion is enabled.
Be aware t hat using t he Place Holder role w it h t he Ev ery one user in it act ually m oves t he first line of defense t o t he com ponent lay er inst ead of t he applicat ion layer. This m ov em ent m ay open t he way for a denial of serv ice at t ack by a m alicious client t hat bom bards y our applicat ion w it h r equest s t o creat e new com ponent s. COM+ allows t he at t acker t o creat e t he com ponent s, but not access t hem . The bom bardm ent m ay cause your applicat ion t o r un out of r esour ces. Figu r e 7 - 1 9 . Ad ding a role a s a p la ceh olde r for t h e Eve r yon e u se r
217
7 .1 0 Su m m a r y COM+ securit y offers t he com ponent developer a wide spect rum of securit y support , fr om sim ple and adm inist rat iv e role- based secur it y t o advanced program m at ic securit y. Securit y is all about t r adeoffs: per form ance ver sus r isk m it igat ion, ease of use v er sus flex ibilit y, and ease of adm inist rat ion versus pot ent ial at t acks. Regar dless of wher e you find y ourself in t his spect rum , y ou will learn t o appr eciat e t he elegance and power of COM+ securit y. You can also com bine COM+ secur it y wit h t he high- level COM+ serv ices described in t he nex t chapt er s: COM+ queued com ponent s and COM+ loosely coupled event s.
218
Cha pt e r 8 . COM + Que ue d Com pone nt s COM+ Queued Com ponent s is a ser vice t hat allows a client t o call obj ect m et hods asynchronously. The client is block ed only for a short dur at ion while COM+ pr ocesses t he r equest , and t he obj ect execut es t he call at a lat er point . You can t hink of queued com ponent s as asynchronous COM+ . Under classic COM and DCOM, all m et hod calls on your obj ect are sy nchr onous—t he client is block ed while t he obj ect execut es t he call. Classic COM dev eloper s oft en had t o dev elop a propriet ary m echanism for asynchronously inv ok ing calls on t heir obj ect s. One recur ring m echanism had t he obj ect spin off a wor ker t hread t o process t he client r equest and im m ediat ely r et urn cont rol t o t he client . The obj ect would lat er signal t he client som ehow when t he call com plet ed ( if t he client needed t o know) , and t he client had t o dist inguish bet ween m et hod com plet ions of m ult iple obj ect s. Such solut ions coupled t he client s t o t he obj ect s and were inconsist ent . Differ ent vendor s provided slight ly different solut ions, requir ing differ ent progr am m ing m odels on t he client side at t im es. The first r elease of MTS and Microsoft Message Queue ( MSMQ) in 1998 pr ov ided anot her way t o support asy nchronous obj ect m et hod calls w it h COM. MSMQ is a m essage queuing serv ice t hat allows you t o post m essages from one queue t o anot her, pot ent ially acr oss m achines. Client s and obj ect s could use MSMQ t o facilit at e COM asynchr onous m et hod calls. Wit h MSMQ, t he client post s a m essage t o a designat ed queue t hat cont ains t he m et hod nam e and param et ers. The obj ect ret r ieves t he m essage off t he queue, parses t he m essage, and execut es t he call. The obj ect and client developers agr ee about t he queue locat ion, t he m essage form at , and ot her det ails requir ed for asy nchronous int eract ion in adv ance. However, using MSMQ for asynchr onous calls has som e disadvant ages: • •
• •
The nonst andard int er act ion couples t he obj ect t o it s client s. The client developers st ill hav e t o design and im plem ent a way t o package t he m et hod infor m at ion int o a m essage, and obj ect developers st ill hav e t o design and im plem ent a way t o par se t he m essage. MSMQ is not easy t o inst all and use. Developers have t o lear n how t o writ e code t o use MSMQ int er faces. The client is v ery m uch awar e t hat it uses MSMQ t o post t he call t o t he obj ect . The r esult ing asy nchr onous m et hod inv ocat ion code does not r esem ble t he synchr onous m et hod inv ocat ion on t he sam e COM int erface.
219
This approach is analogous t o t he pre- DCOM days when developers wrot e r aw RPC calls t o inv oke m et hods on r em ot e obj ect s. The idea behind COM+ queued com ponent s is sim ple: let COM+ encapsulat e t he int er act ion wit h MSMQ and pr ov ide a uniform w ay of inv oking asy nchr onous m et hod calls. I n fact , t he m et hod inv ocat ion code it self is t he sam e as a sy nchronous call. The only differ ence is in t he way t he client creat es t he obj ect . You can t hink of MSMQ as t he t r ansport layer bet ween t he client and obj ect , m uch like RPC is t he t r ansport lay er in t he case of rem ot e act iv at ion. A DCOM client does not car e about t he underly ing det ails of RPC prot ocol and m arshaling when invoking a m et hod on a r em ot e m achine. Sim ilar ly , a queued com ponent s client does not need t o care about t he det ails of t he MSMQ prot ocol and t he m et hods- t o- m essage conver sion. Queued com ponent s are an essent ial addit ion t o y our arsenal because im plem ent ing robust asynchr onous execut ion on your ow n is a dem anding t ask ; it requir es you t o spend m uch effort on design, im plem ent at ion, and t est ing. By prov iding y ou wit h queued com ponent s, COM+ let s you focus on t he dom ain pr oblem s at hand, rat her t han on com plicat ed asynchronous plum bing.
8 .1 M a j or Be n efit s of Qu e u e d Com pon en t s Besides sim plifying asy nchronous m et hod invocat ion, queued com ponent s provide y ou wit h ot her m aj or benefit s ( discussed in t he following sect ions) . 8 .1 .1 D iscon n e ct e d W or k When t he client calls a queued com ponent , t he call is convert ed t o a m essage and placed in a queue. MSMQ det ect s t he m essage in t he queue and dispat ches t he m essage t o t he queued com ponent . I f t he client and t he obj ect are on different m achines, t he m essage can be placed in a local queue on t he client m achine, if necessar y. I m agine t hat t he client is disconnect ed from t he net work: suppose a sales person is working on a lapt op at t he airport while wait ing for a flight . The client applicat ion on t he lapt op can st ill m ake calls t o queued com ponent s—t o updat e order num bers, for exam ple. The calls are st or ed locally by MSMQ. The nex t t im e t he client m achine is connect ed t o t he net work, MSMQ is aware t hat t he local queue cont ains m essages, so it dispat ches t hem t o t he rem ot e com ponent . The server host ing t he obj ect s could be disconnect ed as well. MSMQ t r ansfer s queued m essages fr om t he client m achine once t he obj ect m achine is br ought back online. The benefit s of disconnect ed work are t wofold. First , your syst em 's robust ness im pr ov es because net work out age bet ween a client and
220
a queued com ponent is handled easily . Second, allowing disconnect ed work in your applicat ion, by design, has pract ical im port ance: approx im at ely 40 percent of all new com put ers sold are for m obile and por t able use. These devices benefit great ly fr om queued com ponent s, as t hey allow users t o cont inue working while offline or on t he r oad. Target ing t he port able m ar ket is an im port ant considerat ion for m any m odern applicat ions. 8 .1 .2 Re a l Life Busine ss M ode l Many ent er prise- wide applicat ions are dev eloped t o aut om at e exist ing business processes and inform at ion flow. These processes, such as em ail and voicem ail, are oft en m essaging- based by nat ur e, and m odeling t hem wit h queued com ponent s is very appropriat e. 8 .1 .3 Com pon e n t Ava ila bilit y A com ponent m ay not be available because of ser ver ov er load or net wor king problem s. Under classic DCOM, y ou would have t o abor t t he whole t ransact ion or wait for t he com ponent t o becom e accessible. Using queued com ponent s, you can separ at e t he t r ansact ion int o act iv it ies t hat m ust be com plet ed now and t hose t hat can be com plet ed lat er. Your end user s w ill be unawar e of server slowdowns or failures. 8 .1 .4 M SM Q Pa r t icipa t e s in Tr a n sa ct ion s MSMQ is a resour ce m anager, and will t hus aut o- enlist in y our t r ansact ions. When y our applicat ion m akes calls t o queued com ponent s dur ing a t r ansact ion, your applicat ion ( via COM+ ) adds m essages t o an MSMQ queue. Those m essages w ill not persist in t he queue if t he t r ansact ion is abort ed. The t ransact ion coordinat or ( DTC) inst ruct s all r esource m anager s t hat part icipat ed in t he t r ansact ion t o roll back t he changes. MSMQ’s r ollback rej ect s t he m essages t hat were added t o t he queue dur ing t he t r ansact ion. 8 .1 .5 Aut o- Re t r y M e ch a n ism Once a m essage is added t o a queue, COM+ t r ies t o invoke t he call in t hat m essage on t he obj ect . When COM+ ret rieves t he m essage fr om t he queue, it cr eat es a new t ransact ion for t he r et riev al. I f t he obj ect part icipat es in t hat t ransact ion, and t hat t r ansact ion is abor t ed, MSMQ’s r ollback in t his case will ret ur n t he m essage t o t he queue. This, in t urn, causes COM+ t o t r y again t o invoke t he call on t he obj ect . 8 .1 .6 Sca la bilit y
221
A m aj or scalabilit y bot t leneck is t he lengt h of t im e t he client t ies up an inst ance of t he server. I n a dist ribut ed sy st em , you should m inim ize t hat t im e as m uch as possible by r educing t he num ber of net wor k round t rips t o allow y our server t o accept calls fr om ot her client s. When a client m ak es calls on a queued com ponent , COM+ records t he calls t he client m akes and com bines t hem int o a single m essage. Message deliv ery generally requires j ust a single net wor k operat ion, so t he t im e t he serv er inst ance is occupied is r educed. 8 .1 .7 W or k loa d Bu f fe r in g Every sy st em has a peak load of client s asking for serv ices. Sy st em s ar chit ect s have t o design t he syst em t o handle t hat peak load. The quest ion is, what do y ou do if t he wor kload is uneven t hr oughout t he day? Designing your syst em t o handle t he peak load in real t im e m ay require huge invest m ent s in ex pensive har dware, load balancing m achines, longer developm ent t im e, and m ore difficult design goals. Such an approach result s in a sy st em t hat m ay handle t he peak load, but rem ains vast ly underut ilized on average. A m or e r ealist ic alt ernat ive is t o accept client r equest s, buffer t hem , and execut e t hem lat er on. For ex am ple, m ost online web st ores do exact ly t hat —t hey accept your order im m ediat ely and you are free t o surf ot her web sit es. The st ore buffer s your request and can handle t he nex t client . I n t he background, at t he syst em 's leisure, it pr ocesses t he request and sends you an em ail confir m at ion once y our order is processed and shipped. Using queued com ponent s, you can separat e t he pur chasing t ask int o t wo st ages: a shor t - durat ion, fr ont - end, sy nchr onous acknowledgem ent , and an offline, queued t ask—t he order processing it self. 8 .1 .8 W he n Sh ou ld You Use Qu e u e d Com pon e n t s? Clearly , queued com ponent s offer solut ions t o sever al real- life problem s, saving you precious developm ent t im e and increasing overall syst em qualit y. The quest ion is, when should you use queued com ponent s? Dur ing sy st em requir em ent s analysis, t ry t o ident ify business act ivit ies t hat can be separ at ed by t im e. You m ay execut e each act ivit y sy nchr onously, but y ou connect t hem wit h queued com ponent s. For ex am ple, im agine an online st or e. Orders are collect ed from t he cust om er s im m ediat ely and synchr onously . Processing t he order— par t s order s t o var ious vendor s, billing updat es, and so on—can be done lat er . All t asks m ust be done, but t hey don't all have t o be done at once.
222
8 .2 Qu eu e d Com pon e n t s Ar ch it ect u r e One of t he m aj or requir em ent s for t he COM+ queued com ponent s archit ect ure specifies t hat t he com ponent developer should t ak e no special st eps t o m ak e a com ponent asynchronous; t he developer writ es sy nchronous code, and COM+ provides t he m echanism t o allow client s t o call t he m et hod asynchr onously . As a result , t he client cannot cr eat e t he com ponent direct ly, since it would result in t he usual blocking calls. I nst ead, COM+ uses t he archit ect ure shown in Figure 8- 1. For every queued com ponent , COM+ provides a r ecor der obj ect . The recorder obj ect support s t he sam e set of int erfaces as t he queued com ponent . When t he client calls m et hods on t he recorder int erfaces ( St ep 1) , t he r ecor der ( as t he nam e im plies) m er ely records t he calls. When t he client releases t he recorder, t he r ecor der conv ert s t he calls t o an MSMQ m essage and post s t hat m essage t o t he r ecorder queue ( St ep 2) . Every applicat ion t hat cont ains queued com ponent s has a queue associat ed wit h it . MSMQ t r ansfers t he m essage t o t he applicat ion queue from t he recorder queue ( St ep 3) . For each applicat ion, COM+ m aint ains a list ener obj ect t hat det ect s when a m essage was added t o t he applicat ion queue ( St ep 4) . The list ener creat es a player obj ect ( St ep 5) and inst ruct s it t o r et riev e t he m essage fr om t he queue ( St ep 6) . The player creat es t he act ual com ponent and plays t he calls back t o it ( St ep 7) . When t he player is finished playing back calls, it releases t he com ponent . Figu re 8 - 1 . COM + q ue u e d com p on en t s a rch it ect u r e
8 .2 .1 Th e Re cor de r You can t hink of t he recorder as t he com ponent pr oxy . The r ecorder is responsible for for war ding t he call across processes or m achines t o w her e t he obj ect r esides. The recorder lives in t he client pr ocess and suppor t s t he sam e set of queued int er faces as t he com ponent . When client s query t he recorder for a different int er face, t hen t he recorder m ust also provide r ecording abilit y for t he int erface if it is suppor t ed by t he real com ponent . 8 .2 .2 Th e Pla y e r
223
The player in t his ar chit ect ure is analogous t o t he st ub—it t ranslat es t he MSMQ m essage t o m et hod calls and t hen m ak es t hose calls t o t he obj ect . The player is responsible for r em oving t he m essage fr om t he queue and is configur ed t o alway s requir e a t ransact ion. As a result , cr eat ing t he player kicks off a new t ransact ion t hat includes in it s scope t he m essage rem ov al and t he playback of m et hod calls. Every act ion t he queued com ponent t ak es when ex ecut ing t he m et hods, such as dat abase updat es, execut es wit hin t hat t r ansact ion. I f, for exam ple, t he dat abase updat e fails, t he t r ansact ion abort s and every resource m anager t hat t ook part in it has t o roll back. As m ent ioned previously , MSMQ is a resource m anager and it s r ollback put s t he m essage back in t he queue. The list ener det ect s it s pr esence t here and ret ries t he play back sequence ( m ore on t hat lat er) . 8 .2 .3 Th e List e n e r Every COM+ applicat ion has at m ost one list ener associat ed wit h it , serv ing all queued com ponent s in t he applicat ion by list ening t o t he applicat ion queue and creat ing t he player obj ect s. Not e t hat t he queued com ponent s design separ at es t he act of det ect ing a m essage from t he act of playing it back t o t he com ponent . I f t he list ener were responsible for calling m et hods on t he obj ect s, t hen all calls t o queued com ponent s would be asynchronous, but serialized—t hat is, occur ring one at a t im e. That kind of design would have killed per form ance. By hav ing a dedicat ed play er for each com ponent , t he list ener can process asynchronous calls as fast as t hey can be added t o t he queue. The list ener obj ect lives in t he applicat ion process. I f you configure your applicat ion t o support queued com ponent s, COM+ creat es a list ener in t he applicat ion pr ocess when t he applicat ion is launched. I n fact , if t he applicat ion is not r unning, t hen no one will list en t o it s m essage queue, and, as a r esult , no queued com ponent s will ever be inst ant iat ed. COM+ cannot possibly k now when it is a good t im e t o cr eat e t he applicat ion and hav e it st art ser vicing queued calls for you. Only t he applicat ion adm inist r at or has t hat k now ledge ( for exam ple, what hour s of t he day or what load level) . You have a num ber of opt ions available for launching t he applicat ion: • •
•
St art t he applicat ion m anually fr om t he Com ponent Serv ices Explorer. Provide your applicat ion adm inist r at or w it h a sim ple ut ilit y t hat m akes program m at ic calls t o t he COM+ Cat alog ( as explained in Chapt er 6) t o st ar t t he applicat ion. Use t he Windows Task Scheduler t o invoke your ut ilit y at designat ed t im es.
224
•
Act iv at e nonqueued com ponent in t he sam e applicat ion. This act ivat ion causes COM+ t o launch t he applicat ion, and by doing so, it creat es t he list ener .
8 .3 Com pon e n t Ser vice s Ex plor e r Configu r a t ion Befor e you begin configur ing t he Com ponent Ser vices Explor er, m ak e sure you have MSMQ inst alled on y our m achine. The Windows 2000 inst allat ion does not inst all MSMQ by default . To add MSMQ t o your syst em , go t o t he Cont rol Panel and click on Add/ Rem ove Program s. I n t he dialog box, click Add/ Rem ov e Windows Com ponent s, and inst r uct t he wizar d t o inst all Message Queuing Serv ices. This st ep st art s t he MSMQ inst allat ion. Choose t he Work gr oup inst allat ion for a single- m achine set up, or if y ou have a dom ain account on a dom ain ser ver, choose t he dom ain inst allat ion for secur e cross- m achine invocat ions. 8 .3 .1 Applica t ion Con figu r a t ion Every COM+ Server applicat ion can host queued com ponent s. On t he applicat ion pr opert ies page, a Queuing t ab ( see Figure 8- 2) enables and configur es queued com ponent host ing by t hat applicat ion. The t ab cont ains t wo checkboxes, " Queued" and " List en" . Figu r e 8 - 2 . Th e COM + se rve r a pp lica t ion Prope r t ies pa ge ’s Qu e u in g t a b
Checking t he Queued check box causes COM+ t o creat e a public m essage queue, nam ed as t he applicat ion, for t he use of any queued com ponent s in t he applicat ion. I ncom ing m essages for queued com ponent s in t he applicat ion are post ed t o t hat queue. You can act ually see t he queue associat ed wit h your applicat ion by using t he MSMQ Explorer. To bring up t he MSMQ Explor er, go t o t he Cont rol Panel, open t he Adm inist r at ive Tools folder and expand
225
Serv ices and Applicat ion Message Com put er Managem ent Queuing. You will see all t he MSMQ queues inst alled on y our com put er . I f, for exam ple, y our COM+ applicat ion is called MyQC App, once you check t he Queued check box , under t he Public Queues folder y ou should see a new queue called m yqc app ( see Figure 8- 3) . Figu r e 8 - 3 . Usin g t h e M SM Q Ex plor er , you ca n se e t h e qu eu e associa t ed w it h you r a pplica t ion
Checking t he " List en" checkbox on t he Queuing t ab inst r uct s COM+ t o act ivat e a list ener for t he applicat ion when t he applicat ion is launched. Nor m ally , if y ou have queued com ponent s in t he applicat ion, y ou should hav e t he " List en" check box checked. However, COM+ allows you t o t ur n off processing queued calls ( by unchecking t he " List en" checkbox) t o allow nonqueued com ponent s in t he applicat ion t o sever t heir client s adequat ely wit hout t he per for m ance hit of t he queued calls. The per form ance can be sust ained at a lat er point in t im e. A COM+ libr ar y applicat ion cannot cont ain COM+ queued com ponent s because it is host ed at runt im e by a client process, over which COM+ has no cont rol. I n fact , t he client process m ay not even be a COM+ server applicat ion. COM+ cannot cr eat e MSMQ queues as needed for a process or inj ect list ener obj ect s int o it . I f you use queued com ponent s, you m ust put t hem in a server applicat ion. 8 .3 .2 Qu e u e d Com pon e n t I n t e r fa ce Con figu r a t ion The fact t hat a client want s t o m ak e asy nchr onous calls on a com ponent does not m ean t hat t he com ponent developer allows it . You have t o enable queuing for ev er y int erface on which you expect t o r eceive asynchronous calls. You do t hat by displaying t he
226
int er face pr opert ies page and t hen select ing t he Queuing t ab. The t ab has a single check box ( see Figure 8- 4) . When check ed, t hat int er face on your com ponent can accept queued calls. Figu re 8 - 4 . Th e in t e rfa ce Pr op er t ie s pa ge ’s Qu e u in g t a b
8 .4 I n vok in g Que u e d Com pon en t s on t h e Clie n t Side A queued com ponent client cannot creat e t he com ponent using CoCreateInstance( ) ( or CreateObject( )/New for Visual Basic 6.0) because it would r esult wit h t he norm al sy nchronous m ode of int er act ion. The client m ust cr eat e t he com ponent in a way t hat would m ak e COM+ creat e a r ecorder for t he calls inst ead. Consider, for exam ple, t he syst em in Figure 8- 5, which shows t he com ponent layout of an online ret ail shoe st ore. The cust om er int er act s w it h a t op- level St ore com ponent . The int er act ion wit h t he cust om er m ust be fast and synchronous. The cust om er specifies shoe size, shipping m et hod, em ail address, credit card num ber, and so on. The St ore com ponent saves t he or der infor m at ion using t he Order com ponent and processes t he order using t he Shipm ent com ponent . Figu re 8 - 5 . A sim ple on lin e r e t a il st or e syst e m con t ain in g St or e a n d Or de r COM + com pon e n t s a n d a qu e u e d Shipm e n t com p on e n t
However, shipping the or der ( order ing t he shoes fr om t he v endor, updat ing invent ory, int eract ing wit h t he shipping com pany, et c.) can t ake a long t im e t o com plet e. Processing and shipping t he or der should be done offline, using COM+ queued com ponent s. The Shipm ent com ponent exposes t he IShipment int erface, defined as:
227
interface IShipment: IDispatch { [id(1)] HRESULT ShipOrder([in]DWORD dwOrderNumber); } The Shipm ent com ponent prog- I D is MyApp.Shipment. The first st ep in using queued com ponent s configures t he applicat ion cont aining t he Shipm ent com ponent t o host queued com ponent s, and t hen configures t he IShipment int erface on t he Shipm ent com ponent , as shown in t he pr ev ious sect ion. The client of a queued com ponent cr eat es a recorder for t he calls m ade using a m oniker —a st ring t hat shows how t o bind t o an obj ect . I f t he client is writ t en using Visual Basic, t he client uses t he GetObject( ) call. A C+ + client would use t he equiv alent CoGetObject( ). For ex am ple, if t he St or e com ponent were im plem ent ed using Visual Basic, you would writ e t he following code t o cr eat e a recorder for t he queued Shipm ent obj ect and execut e t he call: orderNumber = 123 Dim Shipment As Object Set Shipment = GetObject("queue:/new: MyApp.Shipment") Shipment.ShipOrder(orderNumber) And if it wer e wr it t en in C+ + : IShipment* pShipment = NULL; HRESULT hres = S_OK; DWORD dwOrderNumber = 123; hres = ::CoGetObject(L"queue:/new: MyApp.Shipment", NULL, IID_IShipment,(void**)&pShipment); hres = pShipment->ShipOrder(dwOrderNumber); pShipment->Release( ); Alt ernat ively, t he C+ + client can cr eat e t he queued com ponent using t he com ponent CLSI D inst ead of t he nonunique prog- I D: hres = CoGetObject(L"queue:/new:{8B5C3B80-6D0C-49C7-8F7414E59D4BEF40}",...,); Not hing in t he client 's code differ s fr om int eract ing wit h t he sam e com ponent synchr onously , except cr eat ing t he obj ect . COM+ act ually uses t wo m onik er s t o cr eat e t he queued com ponent . The first is t he new m oniker t hat has exist ed since t he ear ly days of COM. The new m oniker, an alt er nat iv e t o CreateObject( ) and CoCreateIntance( ), is used t o creat e a com ponent . For ex am ple, t he following t wo Visual Basic lines are equivalent : Set Order = CreateObject("MyApp.Shipment") Set Order = GetObject("new: MyApp.Shipment") Eit her line would creat e t he nonqueued com ponent .
228
The queue m onik er is a new addit ion, int r oduced by COM+ t o suppor t queued com ponent s. The com binat ion of queue:/new: t ells COM+ t o cr eat e a recorder inst ead of t he real obj ect . For pract ical purposes, t he synt ax used t o creat e a queued com ponent ( shown previously) is all you will ev er need. However, COM+ provides t he queued com ponent client wit h m any ext ensions t o t he queued m oniker t hat allow y ou t o override it s default behavior. These ext ensions include: •
• • • • •
Post ing t he recorded m et hod calls t o a specified queue, inst ead of t he one associat ed wit h t he queued com ponent applicat ion. You can specify t he queue nam e and locat ion ( t he m achine on which t he queue resides) , as w ell as applicat ionspecific infor m at ion t hat w ill be at t ached t o t he m essage. The m essage securit y aut hent icat ion lev el. The m essage encr ypt ion opt ions. Whet her MSMQ should keep a j our nal of t he m essage. Various send and r eceive t im eout s. The m essage priorit y wit hin t he queue.
Please refer t o t he MSDN Library for m or e inform at ion about t hese and ot her ext ensions. The very fine- grained cont rol a client can hav e ov er t he queued m et hod r ecor ding is anot her r eason why t he convent ional m echanism for cr eat ing com ponent s ( CoCreateInstance or New) cannot be used for queued com ponent s.
8 .5 D esign in g Qu e u e d Com pon en t I n t er fa ce s When a client m akes calls t o a queued com ponent , it int er act s wit h t he recorder pr ov ided by COM+ . No act ual calls t o t he real obj ect occur. So, at t he t im e of t he call, t he client has no way t o receive out put from t he m et hod, nor can it t ell whet her t he call succeeded or failed. Consequent ly , when y ou design an int erface t o be suppor t ed by a COM+ queued com ponent , you m ust avoid any out going param et ers on any int er face m et hod. Specifically, do not use any [out], [in,out], or [retval] I DL at t r ibut es on your m et hod param et er s. When you im port a com ponent int o t he Com ponent Services Explorer, COM+ inspect s t he int er faces support ed by t hat com ponent , and if it det ect s an out put at t r ibut e, COM+ disables t he queuing opt ion for t hat int er face. I f you develop y our COM+ com ponent using Visual Basic 6.0, you do not have dir ect access t o y our com ponent I DL. Nor m ally, t his lack of access would not be a pr oblem . However , Visual Basic, by default , t r eat s m et hod par am et ers as [in,out] param et ers. I f you
229
expect your com ponent t o be accessed as a queued com ponent , you hav e t o ex plicit ly use t he Visual Basic ByVal at t ribut e on y our m et hod param et ers. I n t he next ver sion of Visual Basic, Visual Basic.NET, all param et er s are, by default , passed in by value inst ead of by r eference. See Chapt er 10 for m or e inform at ion. A differ ent kind of a param et er ret ur ned from a m et hod is it s ret ur n value. You should av oid using cust om HRESULT codes t o indicat e par t icular failure cases. The client only receives t he HRESULT from t he recorder, indicat ing t he success or failure of recording t he call. When y our obj ect ex ecut es, it s HRESULT codes are delivered t o t he player , which does not under st and your cust om sem ant ics. COM+ does not r equir e you t o st ick t o t he st andard HRESULT codes, but t here is no point in not doing so. Anot her rest rict ion on queued com ponent int er faces is t hat t he client cannot pass in a point er t o anot her COM obj ect . The reason is obv ious—when t he act ual call t akes place lat er, t her e is no guar ant ee t hat t he obj ect passed in as a param et er is st ill ar ound. I m plem ent ing a queued com ponent im plies m ore t han j ust a few m et hod param et ers rest r ict ions and click ed checkboxes on t he Com ponent Services Explorer. I t r eally m eans a change in y our design pat t er ns and program m ing m odel. The only way t o pass in COM obj ect s as a m et hod par am eter t o a queued obj ect is if t he obj ect y ou pass in support s t he int erface IPersistStream. IPersistStream is a st andar d int erface used for obj ect ser ializat ion, defined in t he early days of OLE2 and COM. Obj ect s t hat support IPersistStream can ser ialize t heir st at e int o a COM+ provided st ream and reconst ruct t heir st at e out of such a st ream . Whenever you pass a COM obj ect as a m et hod param et er , COM+ quer ies it for IPersistStream. I f t he obj ect support s it , COM+ calls IPersistStream::Save( ), passing in a point er t o an IStream obj ect . The input obj ect saves it s st at e t o t he st ream . The recorder st or es t he sav ed st at e infor m at ion in t he m essage sent t o t he queued com ponent applicat ion queue. When t he play er ret rieves t he m essage, it cr eat es a st r eam obj ect from t he m essage and calls IPersistStream::Load( ) t o init ialize t he obj ect t o t he st at e it was in when t he call was m ade. I t t hen invokes t he call on t he com ponent , passing in t he input obj ect as par am et er. When y ou design an int erface used by a queued com ponent , you can use a new I DL ext ension, an int er face at t ribut e called QUEUEABLE, t o denot e it as an int erface used by a queued com ponent . The m t x at t r .h header file defines t his ex t ension.
230
Exam ple 8- 1 shows t he IShipment int erface definit ion again, t his t im e using t he QUEUEABLE at t r ibut e. Ex a m p le 8 - 1 . Usin g t h e I D L QUEUEABLE at t rib ut e t o de n ot e a n in t e r fa ce as qu e u ed - com pon e nt s com pa t ible
#include "mtxattr.h" // For QUEUEABLE [ object, uuid(97184D0F-F7EF-413A-8C6D-C1745018B2E9), dual, pointer_default(unique), QUEUEABLE ] interface IShipment: IDispatch { [id(1)] HRESULT ShipOrder([in]DWORD dwOrderNumber); }; This at t r ibut e aut oconfigures t he int er face as Queued w hen y ou im port a com ponent t hat support s t he int erface int o t he Com ponent Serv ices Explorer. This aut oconfigur at ion saves y ou t he t rouble of doing it yourself. The at t ribut e has no sem ant ic m eaning for t he MI DL com piler; it will not st op y ou from defining [out] param et ers on an int er face m arked QUEUEABLE. Only COM+ exam ines t his at t r ibut e.
ATL 7 Que uing At t r ibut e As explained in Chapt er 4, if you use t he at t ribut ed ATL 7 proj ect under Visual St udio.NET, you can t ak e advant age of precom piler - specific support for COM+ 1.0 serv ices, using special at t ribut es. I f you add a new class t o your ATL 7 proj ect , and you select " ATL COM+ 1.0 Com ponent " from t he Add Class dialog, t he wizard will let you configure queuedcom ponent support for t he int er face. I f y ou select t he " Queueable" checkbox , t he at t r ibut ed int er face will have a cust om at t r ibut e as par t of it s decelerat ion: [ object, uuid(97184D0F-F7EF-413A-8C6D-C1745018B2E9), dual, custom(TLBATTR_QUEUEABLE,0) pointer_default(unique) ] __interface IShipment: IDispatch { [id(1)] HRESULT ShipOrder([in]DWORD dwOrderNumber); };
231
Befor e com piling your code, ATL 7 feeds y our sour ces t o a special pr ecom piler t hat parses t he at t ribut es and generat es convent ional, nonat t r ibut ed ATL sour ce files, including an I DL file. The new sour ces are t hen fed t o t he convent ional C+ + com piler . I n t hat process, t he TLBATTR_QUEUEABLE cust om at t r ibut e is convert ed t o t he I DLQUEUEABLE ext ension.
8 .6 Rece ivin g Ou t pu t fr om a Queu ed Com pon e nt Banishing all out put opt ions for a queued com ponent would be t oo draconian and im pr act ical. Som et im es expect ing out put fr om a queued com ponent is appropriat e. Consider t he online shoe st ore. Aft er t he Shipm ent obj ect ships t he or der , you would lik e it t o not ify anot her obj ect about it . The St ore obj ect would like t o pass in t he Not ificat ion obj ect as a param et er t o t he ShipOrder( ) m et hod. Fr om a design per spect ive, it is a good idea t o decouple t he not ificat ion act ions from t he Shipm ent process it self. You should decouple t he act ion t o ensure t hat t he Shipm ent obj ect k nows only about a generic not ificat ion int erface and t hat it calls a m et hod on it once t he order is shipped. I t is up t o t he St or e obj ect t o decide which not ificat ion obj ect t o pass in as a param et er . You could have m ult iple im plem ent at ions of t he Not ificat ion int erface—for exam ple, one Not ificat ion obj ect sends an em ail t o t he cust om er t o say t hat t he shoes are on t he way and anot her also sends prom ot ional m at er ial. You have alr eady seen t hat COM+ allow s y ou t o pass in int er face point ers for obj ect s t hat suppor t t he IPersistStream int erface. COM+ also let s you pass in as a m et hod par am et er an int er face point er t o anot her queued com ponent . This t echnique is called Response Obj ect because it allows t he client t o r eceive a response fr om t he queued com ponent and be not ified about t he r esult s of t he queued call. The response obj ect does not need t o support t he IPersistStream int erface, as COM+ will not t r y t o t ransfer it by value t o t he queued com ponent . The client can use a r esponse obj ect in t wo way s. Fir st , it can creat e t he response obj ect , creat e t he queued com ponent , and pass in t he response obj ect int er face point er ( which act ually point s t o t he response obj ect recorder) . Aft er t he m et hod call, t he client can release t he response obj ect . Figure 8- 6 shows how a r esponse obj ect in t he online st or e exam ple is used t o send not ificat ion em ail t o t he cust om er once t he order is processed and shipped. I n t he exam ple, t he cust om er subm it s t he pur chase order t o t he St or e obj ect s ( St ep 1) . The St ore obj ect creat es a Not ificat ion obj ect ( St ep 2) and a Shipm ent obj ect ( St ep 3) , in bot h cases cr eat ing recorder s only . The St or e obj ect passes in t he Not ificat ion 232
obj ect as a param et er for t he Shipm ent obj ect . The Shipm ent recorder knows how t o ext ract from t he Not ificat ion recorder it s queue nam e and locat ion, and pack s it in t he m essage ( St ep 4) . When t he m et hod call is play ed back t o t he Shipm ent obj ect ( St ep 5) , based on t he infor m at ion in t he m essage, t he player creat es a not ificat ion recor der ( St ep 6) and passes it as a m et hod param et er t o t he Shipm ent obj ect . The Shipm ent obj ect calls m et hods on t he Not ificat ion r ecor der ( St ep 7) . Once released, t he not ificat ion recorder post s t he queued calls t o t he Not ificat ion queue ( St ep 8) , wher e t hey are ev ent ually played back t o t he Not ificat ion obj ect ( St ep 9) . I n t his exam ple, t he Not ificat ion obj ect t hen not ifies t he cust om er about t he shipm ent by sending him em ail ( St ep 10) . Figu r e 8 - 6 . On lin e st or e e x a m ple : u sing a respon se obj e ct t o se n d n ot ifica t ion e m a il
The second way a client can use a r esponse obj ect is t o pass in st ring m et hod param et er s t hat inst r uct t he queued com ponent how t he response obj ect should be cr eat ed. I n t he exam ple, t he St ore obj ect would creat e only t he Shipm ent r ecorder and pass in as par am et ers where and how t he Shipm ent obj ect should cr eat e t he Response obj ect ( m achine and queue nam e, aut hent icat ion lev el, and so on) . The Shipm ent obj ect would use t hese param et er s as argum ent s for t he m oniker t o creat e t he Not ificat ion obj ect . Passing in a queued com ponent as a param et er is m or e t r ansparent t o bot h t he client and t he queued com ponent and does not cont am inat e t he int erface wit h param et ers, which ex pose execut ion locat ion and inv ocat ion m ode. However, passing in t he response obj ect queue infor m at ion provides ult im at e flexibilit y for t he client cont rolling t he response obj ect . Err or handling is anot her use for response obj ect s. The client has no way of know ing about t he success of t he queued call. I m agine, for exam ple, t hat while processing t he order , t he Shipm ent obj ect was unable t o com plet e it successfully ; per haps t he vendor ran out of shoes in t he request ed color , or t he cust om er supplied an expir ed cr edit card num ber.
233
The Shipm ent obj ect cannot possibly handle all t he er ror cases corr ect ly. However , it can not ify t he response obj ect t hat t he order processing failed. The response obj ect not ifies t he cust om er — per haps r equest ing t he cust om er t o select a different color or supply a new card num ber. Er ror handling is t he subj ect of t he nex t sect ion.
8 .7 Qu eu e d Com pon e n t Er r or H a ndlin g I n classic synchr onous COM, t he client k new im m ediat ely about t he success or failur e of a m et hod call by inspect ing t he r et urned HRESULT. A queued com ponent client int er act s wit h t he recorder only , and t he ret ur ned success code only indicat es t he success of recording t he call. Once recorded, a queued com ponent call can fail because of deliv er y pr oblem s or dom ain- specific err or s. COM+ provides a few opt ions for handling errors on bot h t he client side and t he server side. These opt ions include an except ion- lik e m echanism , aut o- ret r ies, and a few adm inist rat iv e t ools. You can always use a response obj ect t o handle err ors, as well. 8 .7 .1 H a n dlin g Poison Ca lls Once a call is placed successfully in t he applicat ion queue, plent y can st ill go wrong; perhaps t he com ponent was rem ov ed, it s inst allat ion was corr upt ed, or t he com ponent failed while execut ing t he call ( for exam ple, if t he cust om er provided a bogus cr edit card num ber) . I n case of failure, if t he call is sim ply ret ur ned back t o t he queue, COM+ could be t rapped in an endless cycle of r em ov ing t he call fr om t he queue—t r ying t o call t he com ponent , failing, placing it back in t he queue, and so on. COM+ would nev er k now when t o st op ret ry ing—per haps t he call could succeed t he next t im e. This scenario in dist ribut ed m essaging syst em s is called t he poison m essage syndrom e because it can lit er ally k ill your applicat ion. COM+ addr esses t he poison m essage syndrom e by keeping t rack of t he num ber of r et ries and m anaging t he int erval bet ween t hem . Besides cr eat ing t he applicat ion public queue ( where t he calls ar e placed) , COM+ creat es fiv e pr iv at e ret ry queues and one dead queue when you enable queuing for a COM+ applicat ion ( seeFigure 8- 7) . The dead queue is t he final rest ing place for a m essage for which all deliver y att em pt s have failed—it is a suspect ed poison m essage. When a call is post ed t o a queued com ponent , it is placed in t he applicat ion public queue and a player t r ies t o invoke it . I f t he invocat ion fails, t he m essage is put int o Queue 0. COM+ t ries t o process t he m essage again t hr ee t im es from queue 0, wit h a one- m inut e int er val bet ween r et ries. I f t he call st ill fails, t he
234
m essage cont inues t o m ov e up t he r et ry queues, where COM+ ret ries t hree t im es fr om each queue, wit h ever- increasing int er vals bet ween t he ret ries. The higher t he num ber of t he ret ry queue, t he longer t he int er val bet ween ret ries ( Q_1 is 2 m inut es, Q_2 is 4, Q_3 is 8, and Q_4 is 16) . Aft er t he last at t em pt from t he last ret r y queue fails, COM+ put s t he m essage in t he dead queue, from which t here are no m ore ret ries, and t he m essage is deem ed poisonous. Figu r e 8 - 7 . COM + a p p lica t ion p riva t e re t ry qu e u e s a nd d ea d le t t e r qu e u e
The dead queue can accum ulat e an infinit e num ber of m essages. I t is up t o your applicat ion adm inist rat or t o m anage t he dead queue. One sim ple cour se of act ion available t o t he adm inist r at or is t o sim ply pur ge t he queue of all t he m essages it cont ains. Anot her opt ion is t o put t he m essage back in t he applicat ion or ret ry queues, if t he adm inist r at or believes t hat t he call w ill succeed t his t im e. Your applicat ion adm inist r at or can also delet e one or m or e of t he ret ry queues and by doing so cont rol t he num ber and length of t he ret ries; COM+ cont inues t o m ove a m essage t hat cont inuously fails up t he rem aining ret r y queues. I f all r et ry queues ar e delet ed, a m essage t hat fails will be m oved dir ect ly t o t he dead queue. 8 .7 .2 Qu e u e d Com pon e n t Ex ce pt ion Cla sse s Som et im es it m ay not be possible for t he call t o succeed due t o dom ain- specific const r aint s. For ex am ple, a cust om er m ay at t em pt t o w it hdraw m oney fr om an account t hat has insufficient funds, or t he cust om er account m ay close when t he m essage is in t he queue. Or, plain securit y set t ings m ay be a problem —t he user who issued t he queued call sim ply does not have the right credent ials t o car ry out t he call. I f t he sit uat ion is br ought t o y our product adm inist rat or 's at t ent ion ( on t he client and t he ser ver side) he or she m ight be able t o do 235
som et hing about it . COM+ let s you associat e an ex cept ion class wit h your queued com ponent . I n case of repeat ed failur e, COM+ cr eat es t he except ion class obj ect and not ifies it about t he failure. You associat e an ex cept ion class wit h y our queued com ponent on t he Advanced t ab of your com ponent ’s proper t ies page by specify ing t he prog- I D of t he except ion calls ( see Figure 8- 8) . I f a queued call cannot reach your com ponent , COM+ inst ant iat es t he except ion class and let s it handle t he failure. Figu r e 8 - 8 . Sp ecifying a n e x cep t ion class for a qu eu ed com pon en t
A queued com ponent except ion class is a COM+ com ponent t hat im plem ent s all t he queued int erfaces suppor t ed by your com ponent and a special int erface called IPlaybackControl. COM+ uses t he except ion class obj ect if t he call could not be deliv ered t o t he queued com ponent , or if t he call persist ent ly failed. IPlaybackControl has only t wo m et hods and is defined as: interface IPlaybackControl : IUnknown { HRESULT FinalClientRetry( ); HRESULT FinalServerRetry( ); }; The t er m s client and server are defined loosely in t he int er face. I t really refers t o which side of t he queued call t he err or occurr ed on. Bot h t he client and t he server adm inist rat or s can inst all t he except ion class, alt hough each will be m ore int er est ed in what happened on t heir side. 8 .7 .2 .1 Clie n t - side e x cep t ion h an dling
Delivering a m essage t o t he queued com ponent queue is never guar ant eed. I f all at t em pt s t o deliv er t he m essage t o t he queued com ponent queue fail, COM+ places t he call on t he client side in a public queue called t he Xact Dead Let t er queue. The Xact Dead Let t er queue is shar ed by all client s on t he sam e m achine. The dead let t er queue has a list ener associat ed wit h it called t he Dead Let t er Queue Monit or ( DLQM) —a COM+ serv er applicat ion inst alled on ev ery Windows 2000 m achine. You can st art t he DLQM applicat ion m anually or pr ogr am m at ically by calling int o t he COM+ Cat alog. When t he DLQM app is r unning, and it det ect s t he m essage
236
in t he queue, it r et rieves t he t arget com ponent from t he m essage and checks for an ex cept ion class. I f t he com ponent has an except ion class associat ed wit h it , t he DLQM inst ant iat es t he ex cept ion class and quer ies it for IPlaybackControl. Since t his is a client - side failure, t he DLQM calls IPlaybackControl::FinalClientRetry( ) on t he ex cept ion class, let t ing it k now t hat client - side failure of deliv er y is t he r eason it is inv oked. Nex t , t he DLQM plays back all m et hod calls fr om t he m essage t o t he ex cept ion class. Recall t hat t he ex cept ion class is required t o im plem ent t he sam e set of queued int erfaces as t he com ponent it is associat ed wit h. I f t he ex cept ion class r et urns a failure st at us from any one of t he m et hod calls, t he m essage is ret ur ned t o t he Xact Dead Let t er queue. The DLQM delet es t he m essage fr om t he Xact Dead Let t er Queue only if t he except ion class ret ur ns S_OK on all calls. This error- handling schem a allows t he ex cept ion class t o im plem ent an alt ernat ive behavior for m essages t hat cannot be sent t o t he server. For exam ple, t he ex cept ion class could gener at e a com pensat ing t r ansact ion. Anot her course of act ion w ould pass in a queued not ificat ion obj ect as a m et hod par am et er. The except ion class would call t he not ificat ion obj ect , let t ing it k now which calls failed. The not ificat ion obj ect can in t ur n send an em ail t o t he cust om er s asking t hem t o resubm it t he order, or it can t ake som e ot her dom ain- specific err or handling act ion. Because all COM+ know s about t he except ion class is it s I D, you can ev en prov ide deploy m ent - specific except ion classes and have a per- cust om er error handling policy . 8 .7 .2 .2 Se r ve r - side e x cep t ion h a n dling
Successful deliv ery of t he m essage t o t he server side does not m ean t hat t he call will succeed—it could st ill fail for dom ain- specific reasons, including inv alid m et hod param et ers, cor rupt inst allat ion, and m issing com ponent s. As explained befor e, t he m essage m oves up t hrough t he ret ry queues in case of repet it iv e invocat ion failures. When t he final ret ry on t he last r et ry queue fails, COM+ ret rieves t he t ar get com ponent fr om t he m essage and checks for an except ion class. Sim ilar t o it s handling of t he failur e on t he sending client side, if t he com ponent has an except ion class associat ed wit h it , COM+ inst ant iat es t he except ion class, quer ies for IPlaybackControl, and calls IPlaybackControl::FinalServerRetry( ). I t does t his t o let t he except ion class know t hat t he failure t ook place on t he ser ver side and t hat t he m essage is about t o be placed in t he dead queue. COM+ t hen plays back all m et hod calls from t he m essage t o t he except ion class. The except ion class can do what ever it deem s fit t o
237
handle t he err or, from sending an em ail t o t he applicat ion adm inist rat or t o alert ing t he user . I f t he ex cept ion class r et urns S_OK from all m et hod calls, COM+ consider s t he m essage delivered. I f t he ex cept ion class r et urned failur e on any of t he queued calls, COM+ put s t he m essage in t he dead let t er queue. 8 .7 .2 .3 Th e M e ssa ge M over cla ss
Regardless of wher e t he err or t ook place ( sending or receiving side) , your syst em or applicat ion adm inist r at or has t o deal wit h it . Applicat ion adm inist rat or s usually do not develop soft ware for a liv ing and k now not hing about COM+ , queued com ponent s, MSMQ ret ry queues, et c. I t is up t o you, t he ent er pr ise applicat ion dev eloper , t o provide your applicat ion adm inist rat or wit h t ools t o m anage y our product . You should deliver your m ain pr oduct wit h an applicat ion- orient ed adm inist rat ion ut ilit y t o m anage r et ries of asynchronous calls and dead calls ( on t he ser ver and client side) . The applicat ion adm inist rat ion ut ilit y should use, in it s user int er face, t erm inology from t he dom ain at hand ( such as shipm ent det ails) rat her t han queue nam es. I nt ernally , it will probably int er act w it h except ion classes and not ificat ion obj ect s. Your helper ut ilit y w ill probably need t o m ove m essages bet ween ret ry queues, t he dead queue, and t he applicat ion queue. For ex am ple, suppose a queued call dest ined for a cust om er account s m anagem ent com ponent failed because t he specified account num ber was inv alid. The adm inist rat ion ut ilit y m ay pr om pt t he adm inist rat or t o ent er t he cor rect account num ber for t he cust om er and t hen put t he m essage back in t he applicat ion queue, t his t im e wit h t he cor rect account num ber. To enable you t o m ove m essages bet ween queues easily, COM+ pr ovides you wit h t he IMessageMover int er face and a st andard im plem ent at ion of it . The st andar d im plem ent at ion is av ailable for t he C+ + dev eloper by calling CoCreateInstance( ) using CLSID_MessageMover and for t he Visual Basic developer by calling CreateObject( ) using t he prog- I D QC.MessageMover. The int erface IMessageMover is defined as: interface IMessageMover : IDispatch { [id(1),propget] HRESULT SourcePath([out,retval]BSTR* pbstrPath); [id(1),propput] HRESULT SourcePath([in] BSTR bstrPath); [id(2),propget] HRESULT DestPath([out,retval] BSTR* pbstrPath); [id(2),propput] HRESULT DestPath([in]BSTR bstrPath); [id(3),propget] HRESULT CommitBatchSize([out,retval]long* plSize);
238
[id(3),propput] HRESULT CommitBatchSize([in]long lSize); [id(4)] HRESULT MoveMessages([out, retval]long* plMessagesMoved); }; As y ou can see, IMessageMover is a sim ple int er face. You can set t he sour ce and dest inat ion queues and call MoveMessages( ), as shown in Exam ple 8- 2, in Visual Basic 6.0. Ex a m p le 8 - 2 . Usin g t h e I M e ssag e M ove r in t e rf a ce t o m ove m e ssa ge s fr om t h e la st re t ry q u eu e t o t h e a pplica t ion qu eu e
Dim MessageMover As Object Dim MessagesMoved As Long Set MessageMover = CreateObject("QC.MessageMover") ’move all the messages from the last retry queue to the application queue MessageMover.SourcePath = ".\PRIVATE$\MyApp_4" MessageMover.DestPath = ".\PUBLIC$\MyApp" MessagesMoved = MessageMover.MoveMessages IMessageMover does not provide you wit h a way t o m ove fewer t han t he t ot al num ber of m essages on t he queue, but it does save you t he agony of int eract ing direct ly wit h t he MSMQ API s. See t he MSDN Libr ary for m ore infor m at ion about using t he IMessageMover int er face.
8 .8 Qu eu e d Com pon e n t s a nd Tr a n sa ct ion s As m ent ioned before, MSMQ is a resour ce m anager. By default , when COM+ cr eat es t he applicat ion and ret ry queues, t hey are all t r ansact ional queues; t hey aut o- enlist in t he t ransact ion t hat adds or r em oves a m essage t o or from t he queue. The r ecor der and t he list ener are COM+ com ponent s inst alled in t he COM+ Ut ilit ies applicat ion—a library applicat ion t hat is par t of every Windows 2000 inst allat ion. These com ponent s are configured t o requir e a t ransact ion and t ak e part in an exist ing t ransact ion, or spawn a new one if none exist s. ( COM+ will not let y ou change t he Ut ilit ies applicat ion com ponent s set t ings) . Ev ery t im e a client uses queued com ponent s, t hr ee t ransact ions are inv olved—r ecording t he calls, deliver ing t he m essage t o t he applicat ion queue, and replay ing t he calls. You can see t his concept work wit h t he online st ore ( see Figure 89) ; all t he calls m ade by t he St ore obj ect on t he Shipm ent recorder are pack aged int o one m essage and placed in an int er m ediat e recorder queue. These calls were m ade in t he scope of t he 239
t r ansact ion t hat accept ed t he order param et ers from t he cust om er ( Transact ion 1) . Since MSMQ is a resour ce m anager, t he recorder queue rolls back and rej ect s t he m essage if t he or der t ransact ion is abor t ed. MSMQ t hen has t o t ransfer t he m essage t o t he queued com ponent applicat ion queue, pot ent ially across t he net work . MSMQ cr eat es a new t ransact ion for t he t ransfer, and bot h t he source and t he dest inat ion queues part icipat e in it . I f t he t ransfer was unsuccessful, t he t r ansact ion abort s, t he queues roll back, and t he m essage rem ains in t he recorder queue. This act ion av oids a par t ial success sit uat ion, in which t he m essage is rem ov ed fr om t he source queue, but nev er added t o t he dest inat ion queue. This t ransact ion is called Transact ion 2 in Figur e 8- 9. Figu re 8 - 9 . Th e t h r ee t r an sact ion s in volve d w h e n a clie n t u ses qu e u ed com p on en t s
Once t he m essage is safely in t he applicat ion queue, t he list ener st ar t s a new t ransact ion for rem ov ing it and play ing it back t o t he com ponent ( called Transact ion 3 in Figure 8- 9) . I f t he invocat ion fails, t he applicat ion queue rollback m oves it t o t he first ret r y queue, inst ead of adding it back t o t he applicat ion queue, t o det ect a pot ent ial poison m essage. Usually you t ake t he MSMQ t r ansfer t r ansact ion for grant ed and om it it from your design docum ent s. I f y ou use a response obj ect , t he response obj ect playback would be in a t r ansact ion of it s own because it is j ust anot her queued com ponent ( see Figur e 8- 10) . Figu r e 8 - 1 0 . Th e t ra n sa ct ion in volved w h en u sing a re sp on se ob j ect
240
A word of caut ion w hen configur ing t he t r ansact ional set t ing of a queued com ponent : av oid configuring your queued com ponent t o requir e a new t r ansact ion of it s ow n. I f you configure y our com ponent ’s t ransact ion set t ing t o hav e Requires New, t he recorder is in a separ at e t ransact ion fr om t hat of t he client , and MSMQ accept s t he recorded calls and post s t hem t o t he applicat ion queue even if t he original client t r ansact ion fails ( see Figure 8- 11) . Figu re 8 - 1 1 . Avoid con fig u r in g a q u eu e d com p on en t t o r e qu ire a n ew t r a n sa ct ion
A sim ilar inconsist ency m ay exist if y ou configur e t he applicat ion queue as a nont ransact ional queue, as MSMQ rem oves t he m essage fr om t he queue ev en if t he Shipm ent t r ansact ion is abort ed.
241
You should always set t he t ransact ion set t ing of your queued com ponent t o Required—t hat is what w ill be necessary in m ost business sit uat ions.
8 .9 Syn ch r on ous Ver su s Asyn ch r on ou s Com pon e n t s By now y ou have probably com e t o appreciat e t he elegance of using queued com ponent s and t he great ease w it h which y ou can t ur n a sy nchr onous com ponent and it s client code t o asynchronous. However, alt hough it is t echnically possible t o use t he sam e com ponent s bot h synchronously ( using CoCreateInstance( ) t o cr eat e it ) or asynchr onously ( using t he queued m onik er) , t he lik elihood t hat a com ponent will be used in bot h cases is low for t he following reasons: using a queued com ponent int roduces changes in t he sem ant ics of t he t r ansact ions t he com ponent will t ak e part in, and using a queued com ponent im plies a change in t he client program wor kflow. You sim ply cannot use t he sam e sy nchr onous execut ion sequence logic. The rest of t his sect ion elabor at es on t hese t wo reasons. These argum ent s wer e fir st pr esent ed in Roger Session's book COM+ and t he Bat t le for t he Middle Tier ( John Wiley & Sons, 2000) . 8 .9 .1 Ch a n ge s in Tr a n sa ct ion Se m a n t ics Suppose your online st ore does not use queued com ponent s. When t he cust om er places an order, t he St ore obj ect uses t he Order and t he Shipm ent obj ect s synchronously . All t he order and shipm ent dat abase updat es t hat t hese obj ect s per form are under t he um br ella of one t ransact ion. Bot h dat abases are consult ed regarding com m it t ing t he t r ansact ion ( see Figure 8- 12) . Figu r e 8 - 1 2 . Syn ch r on ou s in voca t ion scopes all ope ra t ion s in on e t r a n sa ct ion
However, if t he St or e obj ect uses a queued Shipm ent com ponent , as shown in Figur e 8- 9, t he shipm ent dat abase and com ponent ar e not par t of t he originat ing t ransact ion and are not consult ed regarding it s success. The Shipm ent t r ansact ion is now ent ir ely differ ent from t he Or der t r ansact ion. By t he t im e t he shipm ent t r ansact ion t akes place, t he order t ransact ion has already been 242
com m it t ed. Ev en if t he shipm ent t r ansact ion abort s, t he order t r ansact ion rem ains com m it t ed and it s changes will not r oll back. Of course, t he shipm ent t r ansact ion m ay ret r y and ev ent ually succeed and com m it , and t hat m ay be fine. On t he ot her hand, it m ay always fail, and t hat is probably not so fine. The conclusion is t hat configuring a com ponent t o be asy nchr onous has ser ious im plicat ions r egarding t he sem ant ics of t he t r ansact ions it par t icipat es wit h. 8 .9 .2 Ch a n ge s in W or k f low The ot her m aj or differ ence bet ween working wit h a queued com ponent as opposed t o it s nonqueued version has t o do wit h t he client work flow. Curr ent ly, your St ore obj ect calls t he Or der obj ect sy nchr onously, and only if t he Order obj ect succeeds in processing t he order w ill t he St ore obj ect call t he Shipm ent queued com ponent . Suppose t he St ore obj ect would like t o use a queued ver sion of t he Or der com ponent ( besides a queued Shipm ent com ponent ) . The St ore obj ect records t he calls t o t he Order com ponent , records t he calls t o t he Shipm ent com ponent s, and t hen releases t he r ecor der s. The problem is t hat t he Order and Shipm ent obj ect s m ight be inv oked in r andom or der, depending on t he net work t opology, MSMQ set up, num ber of r et ries, and so on. The result can be disast rous if t hings go wrong—for ex am ple, if t he Shipm ent obj ect discover s t hat no shoes in t he st ore m at ch t he cust om er request , but t he Order obj ect has already billed t he client for it . Again, y ou will find t hat you cannot j ust configure com ponent s as queued and use t hem asynchronously since doing so result s in pot ent ially flawed workflow. Using a queued com ponent inst ead of a sy nchr onous v er sion of t he sam e com ponent requir es you t o change your code and your work flow . I f t he St ore com ponent dev eloper want s t o use bot h queued Order and queued Shipm ent com ponent s, t he St ore obj ect should only call t he queued Order com ponent . To av oid t he pot ent ial inconsist encies m ent ioned earlier , t he call t o t he Shipm ent queued com ponent should be done by t he Or der obj ect only if t he order processing was successful ( see Figure 8- 13) . Figu r e 8 - 1 3 . Qu e u ed Ord e r a n d Sh ip m en t com p on en t s re qu ir e ch a n ging t h e applica t ion w ork f low
243
I n gener al, if y ou have m ore t han one queued com ponent in your work flow , y ou should have each com ponent inv ok e t he next one in t he logical execut ion sequence. Needless t o say, such a program m ing m odel int r oduces t ight coupling bet ween com ponent s ( t hey’ll hav e t o know about each ot her) and changes t o t heir int er faces, since y ou have t o pass in addit ional par am et ers required for t he desir ed invocat ion of queued com ponent s down t he r oad. I n addit ion t o changes in t he w ork flow and int erfaces, y ou st ill face t he problem of having t he or der and shipm ent oper at ions in separ at e t ransact ions. The only way t o have t hem shar e t he sam e t r ansact ion is t o m ak e t hem sy nchronous. The conclusion from t his sim ple ex am ple is t hat using t he asynchronous version of a com ponent inst ead of it s sy nchr onous ver sion int roduces m aj or changes t o t he com ponent int er faces, t he client work flow, and t he support ing t r ansact ion sem ant ics. A queued com ponent should be designed for queuing from t he gr ound up. The handy " Queued" checkbox is m erely configur at ion sugar on t op.
8 .1 0 Qu eu ed Com pon e n t s Secu r it y As y ou saw in Chapt er 7, secur it y is an essent ial part of any dist ribut ed applicat ion, and COM+ pr ovides y ou wit h a rich, userfr iendly secur it y infr ast ruct ur e. When a client m akes a queued call, t he queued com ponent m ay st ill r equire t he sam e lev el of securit y serv ices and prot ect ion as if it were inv ok ed synchronously, and r ely on COM+ t o pr ovide aut hent icat ion and aut horizat ion. However, t he underlying m et hod call inv ocat ion is different , and t he sy nchr onous securit y m echanism sim ply will not do—by t he t im e t he act ual obj ect is inv ok ed, t he client m ay be long gone ( wit h it s securit y ident it y and cr edent ials) . The synchr onous aut hent icat ion t hat uses t he challenge- r esponse m echanism cannot be used. The idea behind queued com ponent securit y is sim ple—have t he recorder capt ur e t he securit y ident it y ( and ot her securit y- relat ed infor m at ion) of t he client as it r ecor ds the m et hod calls. The securit y inform at ion is bundled in t he m essage along wit h t he m et hod calls and sent t o t he queued com ponent applicat ion queue. Befor e t he player m akes t he call on t he com ponent it self, COM+ uses t he capt ur ed securit y infor m at ion t o validat e t hat t he client is allowed t o access t he com ponent . The under ly ing im plem ent at ion of t his idea relies heav ily on MSMQ securit y serv ices t o capt ure t he client securit y det ails in t he m essage and t r ansfer it securely t o t he applicat ion queue. To ensur e aut hent icit y of t he m essage, t he m essages can carry a digit al signat ure from t he client . MSMQ can even encrypt t he m essage for t ransfer . I f, on t he r eceiving side, MSMQ encount ers a m essage wit h insufficient securit y cr edent ials or a m essage t hat
244
was t am pered wit h, t hen MSMQ put s it in t he applicat ion’s dead queue. 8 .1 0 .1 Qu e ue d Ca lls Au t h e n t ica t ion The default call aut hent icat ion level act ually depends on t he queued com ponent applicat ion set t ings. I f t he applicat ion uses r ole- based securit y , t hen dur ing t he call t o CoGetObject( ), COM+ capt ures t he inform at ion required t o aut hent icat e t he call dur ing playback in t he m essage. The queued com ponent client can ex plicit ly specify t he desired aut hent icat ion lev el for t he queued call and t he r equir ed privacy lev el by pr ov iding t he queued m oniker wit h addit ional par am et ers. I f t he client requir es aut hent icat ion, MSMQ digit ally signs t he m essage wit h t he user’s securit y cert ificat e. I f t his is t he case, y our applicat ion adm inist r at or has t o issue an MSMQ securit y cert ificat e for each pot ent ial user by using t he MSMQ adm inist rat ion applet in t he Cont rol Panel. I n Ex am ple 8- 3, t he St ore obj ect ex plicit ly t ur ns on aut hent icat ion and inst r uct s MSMQ t o encrypt t he m essage body. Ex a m p le 8 - 3 . Ex p licit ly set t in g a u t h en t icat ion an d en crypt ion le vels for a qu eu ed ca ll
IShipment* pShipment = NULL; HRESULT hres = S_OK; DWORD dwOrderNumber = 123; hres = ::CoGetObject(L"queue:AuthLevel= MQMSG_AUTH_LEVEL_ALWAYS, PrivLevel= MQMSG_PRIV_LEVEL_BODY /new: MyApp.Shipment", NULL, IID_IShipment,(void**)&pShipment); hres = pShipment->ShipOrder(dwOrderNumber); pShipment->Release( ); Except ionally par anoid client s can also specify t he encry pt ion algorit hm t o use and a crypt ogr aphic hash funct ion ( see t he MSDN Library for det ails on t hese advanced param et er s for t he queue m oniker ) . I nsist ing on high secur it y car ries wit h it t he usual per form ance/ securit y t r adeoff. Decide on your securit y set t ing wisely. For ex am ple, y ou m ay want t o aut hent icat e only t he act ual order shipm ent call, but not less sensit iv e m et hod calls. 8 .1 0 .2 Qu e ue d Com pon e n t s a n d Role - Ba se d Se cu r it y Despit e t he fact t hat under - t he- hood COM+ uses a drast ically differ ent m echanism for queued com ponent s secur it y, t he queued 245
com ponent can, once inst ant iat ed, t ake adv ant age of r ole- based securit y wit h t he sam e ease as if it wer e invoked sy nchr onously. You can configure your com ponent adm inist rat iv ely t o use r olebased securit y on t he com ponent , int er face, and m et hod lev els, and even use program m at ic role- based secur it y calls such as IObjectContext::IsCallerInRole( ). The only requir em ent for using role- based secur it y is t hat t he call be aut hent icat ed. I f t he client explicit ly t urns aut hent icat ion off while role- based secur it y is in use, t he call will fail dur ing play back , since COM+ has no way of aut hent icat ing what role t he client belongs t o. 8 .1 0 .3 Qu e ue d Com pon e n t s Se cu r it y Lim it a t ions A queued com ponent developer has access t o sim ilar secur it y feat ur es and serv ices as a nonqueued com ponent , and from a securit y st andpoint , your code will be t he sam e as if you w ere dev eloping a norm al synchr onous com ponent . Howev er , som e differ ences do ex ist , especially if y ou plan t o use t he m or e advanced or esot eric securit y serv ices. You should be awar e of t he following lim it at ions: •
•
•
The queued com ponent developer is discouraged fr om per form ing low- level securit y m anipulat ion, such as int er act ing direct ly wit h t he Ker beros aut hent icat ion ser vice, because t he Kerberos cookies are not par t of t he MSMQ m essage. Generally, if you want t o do low- lev el securit y calls, you are rest rict ed t o what ev er MSMQ support s. Queued com ponent s do not support im personat ing t he client . This is done ( by design) t o close a pot ent ial securit y hole in which an unt rust wor t hy sour ce has generat ed a m essage whose form at r esem bles t hat of a m essage t o a queued com ponent and placed it in t he applicat ion queue. COM+ requir es t he or iginal client ’s securit y ident it y t o com pare wit h t he m essage sender ident it y t o ver ify t hat t he m essage cam e fr om t he client . By doing so, however , COM+ inhibit s im personat ion. I f you inst all MSMQ using t he Wor kgr oup configurat ion, MSMQ cannot aut hent icat e queued calls. As a result , y ou should t ur n off securit y access check s at t he applicat ion and com ponent lev els.
246
8 .1 1 Qu eu ed Com pon e n t s Pit fa lls Queued com ponent s is a gr eat ser vice, no doubt , but it does have a few quirks and pit falls t hat I would lik e t o point out . 8 .1 1 .1 M SM Q Se t u p MSMQ can be inst alled in t wo configurat ions. The first relies on hav ing a Windows 2000 dom ain ser ver pr esent on t he net w or k. The work st at ion ont o w hich y ou wish t o inst all MSMQ m ust be par t of t hat dom ain. The second inst allat ion opt ion is for a Windows Work gr oup. To call queued com ponent s acr oss t he net wor k securely, queued com ponent s requir e t he presence of a Message Queuing Prim ar y Ent erprise Cont roller ( PEC) on t he net work . I f you inst all MSMQ for Work gr oup, you have t o t urn t he secur it y knob all t he way down ( set t he aut hent icat ion lev el for t he queued com ponent s applicat ion t o None and avoid using access cont r ol checks) . Any cr oss- m achine calls m ust be unaut hent icat ed. This lim it at ion is ser ious. For any Ent erprise- level wort hy applicat ion, y ou need t he MSMQ dom ain server inst allat ion. 8 .1 1 .2 Qu e ue d Com pon e n t Clie n t A client of a queued com ponent can run only on a Windows 2000 m achine. There is no appar ent reason for t his condit ion, as every Microsoft plat for m suppor t s MSMQ. What m akes it even m or e awkward is t he fact t hat m ost port able devices t hat could benefit fr om disconnect ed sessions will not run Windows 2000. [ 1] [ 1]
I can only say that I find t his sit uat ion ver y st range, and I hope t hat Micr osoft w ill am end t his predicam ent soon.
8 .1 1 .3 V isu a l Ba sic Pe r sist a ble Obj e ct s As m ent ioned before, a queued com ponent client can pass in as a m et hod param et er an int erface point er t o a COM obj ect , pr ovided t hat t he obj ect support s t he IPersistStream ( so t hat COM+ can serialize t he obj ect st at e int o a st ream ) . However, if t he obj ect is writ t en in Visual Basic 6, t he obj ect m ust be init ialized befor e m aking t he call on t he recorder int erface by querying it for IPersistStream and calling one of t he IPersistStream m et hods Init( ), InitNew( ), or Load( ). I f your client is wr it t en in Visual Basic as well, t he Visual Basic runt im e handles t he obj ect init ializat ion aut om at ically for you. I f t he client applicat ion is writ t en in C+ + , t he applicat ion m ust init ialize t he com ponent explicit ly . Requir ing t he client t o k now t he language used t o im plem ent t he queued com ponent couples t he client t o t he
247
com ponent , but knowing of a lim it at ion is bet t er t han t rying t o figur e out what went wrong. 8 .1 1 .4 I D ispa t ch Con side r a t ion s When a queued com ponent client m akes a call, it act ually int eract s wit h a recor der . The r ecorder has t o m at ch as m uch as possible t he behavior of t he real com ponent , including it s im plem ent at ion of IUnknown::QueryInterface( ). The recorder bases ev eryt hing it does on t he com ponent - t y pe libr ary. I t is com m on for a com ponent t o suppor t m ult iple int erfaces derived from IDispatch . I f t hat is t he case, what int erface should t he recor der ret ur n t o t he client when it is quer ied for IDispatch( )? The r ecor der uses t he following algorit hm t o prov ide t he right IDispatch( ): • • •
I f t he com ponent default int erface inher it s fr om IDispatch, t he default int er face is r et ur ned. I f no int erface is m ar ked as default , but only one int erface inherit s from IDispatch, t hat int er face is r et ur ned. I f no int erface is m ar ked as default and m ult iple int er faces inherit from IDispatch, t he r ecor der ret ur ns E_NOINTERFACE.
The obvious recom m endat ion is t o alw ay s m ar k one of your com ponent IDispatch- der ived int erfaces as t he default int erface. 8 .1 1 .5 Qu e ue d Com pon e n t Applica t ion St a r t u p When an applicat ion host s queued com ponent s, COM+ m ust act ivat e a list ener for queued calls sent t o it s queue whenever t he applicat ion is launched. I f you pack age queued and nonqueued com ponent s in a single applicat ion, t he applicat ion m ight ser vice client s of nonqueued com ponent s when a queued call arr ives. This sit uat ion m ay be a cause for concer n if t he queued com ponent m ak es a lot of CPU- int ensive calculat ions or requir es ot her expensive r esources. These charact er ist ics m ay be t he r easons you m ade t hat com ponent queued—so t hat y our com ponent will not be in t he way of ot her com ponent s and will do t he expensive processing at t im es when t he syst em load is low. When deciding on com ponent allocat ion t o applicat ions, m ake sure t hat y ou r eally want queued com ponent s t o st ar t w hen a nonqueued com ponent execut es. I f y ou would like t o cont rol t he queued com ponent s' execut ion t im e, package t he queued com ponent s int o a separat e COM+ applicat ion and explicit ly st art it up when you deem it fit .
248
8 .1 2 Su m m a r y Com ponent developers benefit from COM+ queued com ponent s on differ ent levels. Fir st , t hey t ake away t he need t o handcraft asynchronous m et hod inv ocat ion solut ions, allowing developers ( as wit h t he ot her COM+ com ponent services) t o focus on t he business problem at hand. Second, and per haps j ust as im por t ant , queued com ponent s’ abilit y t o t ake seam less advant age of ot her COM+ serv ices, such as t r ansact ions and role- based secur it y, is som et hing t hat would be alm ost im possible t o provide in a cust om solut ion. You can even com bine queued com ponent s wit h COM+ loosely coupled event s, t he subj ect of t he next chapt er.
249
Cha pt e r 9 . COM + Eve nt Se r vice I n a com ponent - or ient ed pr ogr am , an obj ect provides services t o client s by let t ing client s invok e m et hods on t he obj ect ’s int erfaces. But w hat if a client ( or m ore t han one client ) want s t o be not ified about an ev ent t hat occurs on t he obj ect side? Tradit ionally, t he client im plem ent s a callback int erface called a sink int erface. To not ify t he client of an occur ring ev ent , t he obj ect calls a m et hod on t he sink int er face. Each m et hod on a sink int er face corresponds t o a t y pe of event fired by t he obj ect . This m odel raises a few quest ions: How does t he obj ect access t he sink int erfaces? How do client s find out which sink int er faces t he obj ect fir es event s on? How do t he client s unsubscr ibe from event not ificat ion? As y ou will see shor t ly, t he COM+ ev ent s ser vice is an excit ing new serv ice t hat ev olved t o address t he classic problem s of ev ent not ificat ion and recept ion. COM+ event s are also k nown as Loosely Coupled Ev ent s ( LCE) , because t hey provide an effect iv e way of decoupling com ponent s. They put t he logic for publishing and subscribing t o event s out side t he scope of t he com ponent . Besides significant ly im pr ov ing on t he classic COM m odel for handling event s, LCE t akes full advant age of such COM+ serv ices as t r ansact ions, queuing, and secur it y. Managing event publishing and subscript ion can be done bot h declarat ively v ia t he Com ponent Serv ices Explorer and program m at ically using t he COM+ Cat alog. To fully appreciat e t he elegance of t he COM+ event s ser vice, you should first underst and t he drawback s of t he way classic COM handles event s.
9 .1 Cla ssic COM Even t s I n classic COM, w hen a client want s t o r eceive event s fr om an obj ect , t he client has t o pass t he obj ect an int erface point er t o a client im plem ent at ion of t he sink int erface. This oper at ion is called adv ising t he obj ect of t he sink. Adv ising t akes place by eit her using a st andard m echanism ( connect ion point s) or a cust om one very sim ilar in nat ur e. These m echanism s have changed lit t le since t he ear ly day s of OLE 2.0 and COM. I f you use connect ion point s, t he obj ect has t o im plem ent an int er face called IConnectionPointContainer ( see Figure 9- 1) . The client uses t he connect ion point cont ainer int er face t o find out whet her t he obj ect suppor t s fir ing event s on a specified sink int er face I I D. Think of it lik e a k ind of rev er se QueryInterface( ) call: t he client quer ies t he obj ect for it s abilit y t o use an int er face im plem ent ed by t he client . 250
Est ablishing a connect ion point usually follows a pat t er n sim ilar t o t his one: 1. The client queries an exist ing obj ect int erface for IConnectionPointContainer. 2. The client uses IConnectionPointContainer t o find out whet her t he obj ect suppor t s fir ing event s on a specified sink int er face. I f it does, t he obj ect r et urns t o t he client an obj ect side im plem ent at ion of anot her int erface called IConnectionPoint. 3. The client uses IConnectionPoint t o adv ise t he obj ect of t he client - side sink int erface. 4. The obj ect has t o m aint ain a list of sinks t hat hav e subscribed. I t adds t he new sink t o t he list and r et urns t o t he client a cook ie ident ify ing t he connect ion. Not e t hat t he obj ect m anages t he subscr ipt ion list . 5. The obj ect uses t he sink int erface t o not ify t he client ( s) about event s. 6. When t he client want s t o st op r eceiving ev ent s and break t he connect ion, it calls IConnectionPoint::Unadvise( ), passing in t he cookie t hat ident ifies t he connect ion. Figu r e 9 - 1 . Cla ssic COM m a n a ge d eve n t s u sing con n ect ion poin t s
Est ablishing t he connect ion requires expensive round t rips, pot ent ially acr oss t he net work. The client m ust r epeat t his cum ber som e sequence for every sink int er face on which it want s t o receive ev ent s and for ev ery obj ect fr om which it want s t o receive event s. Using connect ion point s, t her e is no way for t he client t o subscribe t o a subset of event s t he obj ect can fire. The client has no way of filt er ing event s t hat ar e fir ed ( Not ify m e about t he event only if...) ; as a r esult , a COM designer oft en opt s for t he use of a cust om m echanism ( inst ead of t he generic connect ion point s) t hat allows subscript ion t o a subset of ev ent s. Needless t o say, t his solut ion int roduces coupling bet ween t he obj ect and it s client s regarding t he specific int eract ion. Connect ion point client s m ust also hav e a w ay t o access a serv er inst ance ( t he obj ect ) t o advise it of t he sink. Usually t he client s 251
know t he serv er CLSI D, get t he obj ect fr om anot her client , or go t hr ough som e init ializat ion pr ot ocol. That , in t urn, also int r oduces coupling bet ween client s and obj ect s and coupling bet ween individual client s. On t he obj ect side, t he obj ect has t o m anage a list of sinks. This code has alm ost not hing t o do wit h t he dom ain pr oblem t he obj ect is designed t o solve. Proper ly m anaging t he list of sinks requires m ar shaling sink point ers t o a w ork er t hr ead m anually t o act ually per form event firing. That ext ra code int r oduces bugs, t est ing t im e, and developm ent over head. To m ake m at t ers worse, t he sam e code for m anaging connect ions is repeat ed in m any servers. Wit h t his m odel, t he obj ect and t he client s have coupled lifet im es— t he serv er usually AddRefs t he sink s and t he client s have t o be running t o r eceive event s. There is no way for a client t o say t o COM " I f any obj ect fires t his part icular event , t hen please creat e an inst ance of m e and let m e handle it ." Ther e is no easy way t o do disconnect ed work—t hat is, t he obj ect fires t he event fr om an offline m achine and t he event is subsequent ly delivered t o client s once t he m achine is br ought online. The reverse is also not possible—having a client running on an offline m achine and receiv ing event s fir ed while t he connect ion was down. Set t ing up connect ions has t o be done program m at ically . Ther e is no adm inist r at iv e way t o set up connect ions. The event s, like any ot her COM call, ar e synchronous. The obj ect is blocked while t he client handles an ev ent . Ot her client s ar e not not ified unt il t he cur r ent client ret ur ns cont rol back t o t he obj ect . Well- behav ed client s av oid lengt hy processing of t he event s ( by per haps delegat ing t o a client - side worker t hread) , but t here is no way of forcing client s t o behave nicely or t o fire t he event s on m ult iple t hreads wit hout writ ing a lot of com plex code. Ther e is no safe way t o m ix t r ansact ions and event s. Suppose an event fires, but t hen t he t r ansact ion t he obj ect t ook part in is subsequent ly abort ed. How can t he obj ect not ify t he client s t o r oll back?
9 .2 COM+ Eve n t M odel The COM+ event m odel is based on a sim ple idea—put t he connect ion set up and t he event fir ing plum bing out side t he scope of t he com ponent s. Under COM+ , an obj ect t hat fir es ev ent s is called a publisher . A client w ho want s t o receiv e event s is called a subscriber. Subscr ibers who w ant t o receiv e event s regist er wit h COM+ and m anage t he subscr ibe/ unsubscribe pr ocess via COM+ , not t he obj ect . Sim ilar ly , publisher s hand over t he event s t o COM+ , not dir ect ly t o t he subscr ibed client s.
252
COM+ delivers an event t o t he client s t hat have subscr ibed. By hav ing t his layer of indirect ion, COM+ decouples y our syst em . Your client s no longer hav e any knowledge about t he ident it y of t he publisher s. The subscr ipt ion m echanism is uniform acr oss all publisher s, and t he publisher s do not m anage list s of connect ions. The r est of t his chapt er explains t he det ails of t he COM+ ev ent s serv ice, it s capabilit ies and lim it at ions, and it s int eract ion wit h ot her COM+ serv ices.
9 .3 Th e Ev e n t Cla ss A publisher obj ect fir es an event at COM+ ( t o be deliv er ed t o t he subscribers) using an ev ent class. The ev ent class is a COM+ provided im plem entat ion of t he sink int erfaces t he publisher can fire t he ev ent s at . The im plem ent at ion is synt hesized by COM+ , based on a t ype library you prov ide. This libr ary cont ains t he int er face definit ions and st ipulat es which CoClass im plem ent s t hem . COM+ uses t he sam e CoClass definit ion for it s im plem ent at ion of t he event classes. To publish an event , t he publisher first CoCreat es t he event class ( t he publisher has t o know t he ev ent class CLSI D) and t hen fires t he event s at it s int erfaces. For ex am ple, suppose an obj ect want s t o fir e event s at t he sink int er face IMySink, using an ev ent class called MyEventClass. IMySink is defined as: interface IMySink : IUnknown { HRESULT OnEvent1( ); HRESULT OnEvent2( ); }; The publisher code look s like: HRESULT hres = S_OK; IMySink* pMySink = NULL; hres =: =:CoCreateInstance(CLSID_MyEventClass,NULL,CLSCTX_ALL,IID _IMySink, (void**)&pMySink); ASSERT(SUCCEEDED(hres)); hres = pMyEvent->OnEvent1( ASSERT(hres == S_OK);
);
pMyEvent->Release( ); Com pare t he sim plicit y on t he publisher side t o classic COM connect ion point s—t he publisher does not hav e t o m anage list s of
253
subscribers. All t he publisher has t o do is creat e an event class and fire t he ev ent on it . Figure 9- 2 illust rat es t he int er act ion bet ween t he publisher, t he event class, COM+ , and t he subscr ibers. The client cr eat es t he event class ( St ep 1) and fires t he event at it ( St ep 2) . When t he publisher is finished wit h t he ev ent class, it can eit her r elease it or cache t he event class int er face point er for t he sake of per form ance, t o be used t he nex t t im e t he publisher want s t o publish ev ent s. Figu re 9 - 2 . Th e COM + e ve n t syst em a t w ork
The COM+ im plem ent at ion of t he event class int er faces goes t hr ough t he list of subscr ibers on t hat event class ( St ep 3) and publishes t he event s t o t hem . COM+ m aint ains a list of subscript ions for every event class. The subscript ions can be int er face point er s t o exist ing obj ect s ( called t ransient subscript ions) or CLSI D for a class ( called persist ent subscr ipt ions) . I n t he case of a persist ent subscr ipt ion, COM+ cr eat es an obj ect of t he t y pe specified by t he CLSI D ( St ep 4) , calls t he appr opr iat e sink m et hod on t he obj ect ( St ep 5) , and releases t he obj ect . I n t he case of a t ransient subscript ion, COM+ sim ply calls t he appropriat e sink m et hod on t he obj ect ( St ep 5) . I t is int er est ing t o not e t hat firing t he ev ent is by default ser ial and sy nchr onous—t hat is, t he subscribers are called by default one aft er t he ot her ( ser ial) , and cont rol ret ur ns t o t he publisher obj ect only aft er all t he subscr ibers are not ified ( synchronous) . 9 .3 .1 Addin g a n Eve n t Cla ss You can add an ev ent class t o t he Com ponent Serv ices Explorer by using t he Com ponent I nst all Wizard. Br ing up t he wizar d for inst alling a new com ponent t o your applicat ion and select I nst all new event class( es) ( see Figur e 9- 3) . Figu r e 9 - 3 . Th e Com p on en t I n st a ll W iz a r d is u sed t o ad d a n e w e ve n t cla ss
254
The r est of t he st eps in t he wizar d ar e t he sam e as when adding a new COM+ com ponent . When y ou point t he wizard at a DLL cont aining a t y pe libr ary wit h sink int er face and ev ent CoClass definit ions ( m or e about t hose in a m inut e) , under- t he- hood COM+ sy nt hesizes it s own im plem ent at ion of t he int erfaces and inst alls t he sy nt hesized com ponent s inst ead of y ours. Aft er inst alling t he ev ent class in t he Com ponent Ser vices Explor er, t he only way t o det ect t hat it is not a user - im plem ent ed COM+ com ponent is t o inspect it s com ponent proper t ies page on t he Advanced t ab. The Adv anced t ab of an event class cont ains t he Loosely Coupled Event ( LCE) group ( see Figure 9- 4) . Figu r e 9 - 4 . Th e LCE gr ou p con figu re s e ven t cla ss- spe cific set t in gs
You can add an ev ent class com ponent t o any COM+ applicat ion, be it a library or a server applicat ion. 9 .3 .2 Su pply in g t h e Ev e n t Cla ss D e f init ion
255
For COM+ t o im plem ent an event class for you, you hav e t o provide COM+ wit h t he sink int er faces definit ions, t he ev ent class CLSI D, and t he int er face each ev ent class support s. You prov ide t his infor m at ion in t he form of a t y pe libr ary . The t ype library has t o be em bedded as a r esource in a DLL. The Com ponent I nst all Wizar d know s how t o read t he t ype library fr om t he DLL and det ect t he CoClass definit ions inside. For every CoClass in t he t ype libr ary, COM+ t ries t o generat e an event class and add it t o y our applicat ion as a com ponent . COM+ sy nt hesizes im plem ent at ion only t o int er faces t hat ar e part of t he event class CoClass definit ion in t he t y pe libr ar y. For ex am ple, t o define t he event class MyEventClass t hat support s t he sink int erface IMySink ( shown ear lier) , y our I DL file should look lik e t his: [ uuid(0A9B9E44-E456-4153-9FC8-5D72234B7C82), version(1.0), helpstring("Event Class 1.0 Type Library") ] library EVENTCLASSLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); importlib("SubApp.tlb");//The subscribers’ TLB [ uuid(5CAF8E95-3FEF-40F1-94C3-3F408240D53B), helpstring("MyEventClass Class") ] coclass MyEventClass { interface IMySink; }; }; To avoid repeat ing t he definit ion of t he sink int erfaces in bot h t he subscriber applicat ion and t he ev ent class t ype library, t he event class I DL file should im por t t he sink int erface ( IMySink) definit ions fr om t he t y pe library of t he subscriber s. This is what t he line importlib("SubApp.tlb");was used for in t he pr ev ious exam ple. The easiest way t o gener at e a t ype library is t o hav e t he Visual St udio ATL creat e one for y ou. The default behav ior in ATL is t o em bed t he t ype library in t he DLL, since t he ATL Applicat ion Wizar d adds a reference t o t he t ype library in t he proj ect RC file. I st r ongly r ecom m end t hat y ou put only event classes in t he event class DLL. Do not put ev ent classes in t he sam e t y pe libr ary wit h regular CoClasses; such a m ix confuses t he I nst all Wizard—t he Wizar d will inst all all com ponent s as event classes. This inst allat ion has pot ent ially cat ast rophic r esult s, since it m ay corrupt an ex ist ing
256
inst allat ion of t he regular com ponent s. However , as you hav e alr eady seen in Chapt er 1, y ou can m ap m ore t han one DLL t o t he sam e COM+ applicat ion—you can put your event class and ot her com ponent s in t he sam e applicat ion. When y ou supply t he ev ent class, COM+ t r ies t o r egist er it . You are responsible for providing proper regist rat ion code in t he DLL for all com ponent s cont ained in t he DLL. Again, t he easiest way is t o use ATL t o gener at e a sk elet on im plem ent at ion of t he event class for you. Sim ply hav e t he ATL Obj ect Wizard inser t new com ponent s int o t he event classes DLL. Since t he im plem ent at ion of t hese event classes is never called, it is a bug if anybody ev er uses t hem . This would usually happen as a result of not inst alling t he event class in t he COM+ Cat alog and only building and regist ering it as a nor m al COM obj ect . I t her efor e suggest t hat y ou pr ov ide default behavior t o t he ATL code- assert on every m et hod call. See Exam ple 9- 1. Ex a m p le 9 - 1 . Sk e le t on im ple m en t at ion of t h e e ve n t cla ss
class CMyEventClass : public CComObjectRootEx, public CComCoClass, public IMySink { public: CMyEventClass( ){}; DECLARE_REGISTRY_RESOURCEID(IDR_MYEVENTCLASS) DECLARE_PROTECT_FINAL_CONSTRUCT( ) BEGIN_COM_MAP(CMyEventClass) COM_INTERFACE_ENTRY(IMySink) END_COM_MAP( ) // IMySink public: STDMETHOD(OnEvent1)( E_NOTIMPL;}; STDMETHOD(OnEvent2)( E_NOTIMPL;}; };
){ATLASSERT(0);return ){ATLASSERT(0);return
9 .3 .3 Eve n t Cla ss I n t e r f a ce D e sign Gu ide lin e s The sink int erface can be a cust om int er face or an aut om at ioncom pliant int erface. However, t he m et hods of a sink int erface can cont ain only input param et er s. [out] or [in,out] param et er s are not allowed. Since COM+ seeks t o decouple t he publisher fr om t he subscribers, t her e is no way for a subscriber t o ret urn infor m at ion back t o t he publisher —t he call on t he subscr iber int erface ret urns t o COM+ , not t o t he publisher .
257
Fr om t he publisher’s per spect iv e, it only fir es an event on one obj ect —t he event class. COM+ uses t y pe libr ar y m arshaling t o m arshal t he call on t he sink int er face from t he ev ent class t o t he subscr ibers. I nt erfaces t hat use t ype libr ar y m arshaling m ust com ply wit h t he follow ing requir em ent s: • •
All t he m et hods m ust ret urn HRESULT. The m et hods do not use cer t ain I DL at t r ibut es such as [size_is] and [length_is]. See t he MSDN docum ent at ion for t he exact specificat ion of t y pelib- com pliant I DL at t r ibut es.
9 .4 Su bscr ipt ion Types As I m ent ioned ear lier in t he chapt er, t here are t wo t ypes of subscribers. The fir st t ype is an exist ing inst ance of a class t hat suppor t s a sink int erface. That inst ance can be added at r unt im e t o t he list of subscriber s of a part icular event class. This t ype of subscript ion is called t r ansient subscript ion because it exist s as long as t he subscriber is running and will not per sist or surv iv e a sy st em reboot or a crash. Not e t hat when a part icular inst ance of a class subscribes t o an event class, only t hat inst ance will receiv e ev ent s published using t hat class. Ot her inst ances will r eceive t he event s only if t hey t r ansient ly subscr ibe t hem selves. Adding a t ransient subscript ion can only be done pr ogr am m at ically using t heCOM+ Cat alog int er faces and obj ect s. There is no adm inist rat ive Com ponent Serv ices Explorer support . On t he ot her hand, since all you giv e t he COM+ Cat alog is a point er t o a sink, even a nonconfigur ed COM com ponent can regist er as a t ransient subscript ion, as long as it suppor t s t he sink int erface. A t ransient subscr ipt ion does not even need t o be on a Windows 2000 m achine, as long as it is r egist ered wit h a COM+ Cat alog on t he Windows 2000 m achine w her e t he event class r esides. The second t ype of subscr ipt ion is used when y ou want COM+ t o inst ant iat e an obj ect of a part icular class when an ev ent is published, let it handle t he event , and r elease it . This t y pe of subscript ion is called per sist ent subscript ion. Ev er y ev ent class has a list of persist ent subscr ibers associat ed wit h it , st ored in t he COM+ Cat alog. Per sist ent subscript ions, as t he nam e im plies, per sist in t he COM+ Cat alog and surv ive a syst em r est ar t . Obj ect s creat ed by a persist ent subscript ion are alw ays r eleased aft er each ev ent deliv ery, ev en if m ore event s are on t he way. As a
258
result , y our subscribing com ponent should be wr it t en t o handle each event independent ly of ot her ev ent s t hat m ay or m ay not be published or delivered. 9 .4 .1 Addin g a Pe r sist e n t Su bscr ipt ion Every com ponent in t he Com ponent Serv ices Explorer has a Subscript ion folder , cont aining t he per sist ent subscript ions t he product adm inist rat or or dev eloper has set up. Ev ery subscr ipt ion represent s an ev ent class ( or a list of ev ent classes) t hat t he com ponent should be inst ant iat ed t o receiv e event s from whenever any publisher uses t hose event classes. To add a persist ent subscr ipt ion, expand t he subscript ion folder , right - click on it and select New from t he pop- up cont ext m enu. This act ion invokes t he New Subscr ipt ion Wizard ( see Figur e 9- 5) . Figu r e 9 - 5 . Th e N ew Su b scr ipt ion W iz a r d
The New Subscr ipt ion Wizard let s y ou subscr ibe t o event s published t o all t he sink int erfaces your class suppor t s, t o a part icular int er face, or even j ust t o a par t icular m et hod. The w izar d display s all t he int er faces your com ponent support s, including nonsink int er faces—COM+ doesn't know whet her t hey ar e sinks or not ; only you k now. You can set up a subscr ipt ion at t he m et hod or int erface level. At t he m et hod lev el, COM+ delivers t he event t o your com ponent only when publisher s use t hat m et hod. I f you want t o subscribe t o anot her m et hod, you hav e t o add a new subscript ion. A subscr ipt ion at t he int erface lev el m eans t hat any event t ar get ing any m et hod on t hat int er face should be deliv er ed t o your com ponent . By pr oviding you wit h t hese t wo opt ions, you have t he abilit y t o subscr ibe t o only a subset of t he event s publisher s can publish or t o all of t hem .
259
Aft er you select t he int erfaces and m et hods, t he wizard displays a list of all inst alled event classes t hat support t he int erfaces y ou select ed in t he prev ious st eps. You can select a part icular event class or all of t hem . The last st ep in t he wizar d let s you nam e t he subscript ion and enable it . You can alw ays enable or disable a subscript ion by highlight ing it in t he Subscript ions folder , display ing it s propert ies page, select ing t he Opt ions t ab, and enabling or disabling t he subscript ion ( see Figure 9- 6) . Figu re 9 - 6 . A p e rsist e n t su bscr ip t ion ’s Op t ion s t ab
9 .4 .2 Addin g a Tr a nsie nt Su bscr ipt ion The only way t o receive event s on an alr eady running obj ect is t o use t ransient subscr ipt ion. Unlike per sist ent subscript ion, t her e is no adm inist r at iv e way t o add a t r ansient subscript ion. You hav e t o program against t he COM+ Cat alog using t he cat alog obj ect s and int er faces discussed in Chapt er 6. I n addit ion, it is y our responsibilit y t o rem ove t he t r ansient subscript ion fr om t he Cat alog when t he subscribing com ponent is released or if you want t o unsubscribe. Like a per sist ent subscriber , t he obj ect has t o im plem ent a sink int er face for receiv ing event s. The t ransient subscriber can choose t o subscribe t o all t he sink s a par t icular event class suppor ts, t o a par t icular int er face, or ev en t o a part icular m et hod on a part icular int er face. To add a t ransient subscr ipt ion, you m ust follow t hese st eps: 1. Cr eat e t he cat alog obj ect ( CLSID_COMAdminCatalog) and get a point er t o ICOMAdminCatalog. 2. Call ICOMAdminCatalog::GetCollection( ) t o ret r iev e a collect ion called TransientSubscription and get back an ICatalogCollection int er face point er . 3. Call ICatalogCollection::Add( ) t o get ICatalogObject.
260
4. Call ICatalogObject::put_Value( ) once for each desir ed proper t y of t he t ransient subscr ipt ion you want t o set . Som e exam ples ar e t he event class y ou want t o subscr ibe t o, subscribing int erfaces, and t he subscript ion nam e. An im port ant pr opert y you need t o set is whet her or not you want t o enable t he subscript ion. 5. Call ICatalogCollection::SaveChanges( ). 6. Release everyt hing. You are requir ed t o perfor m a sim ilar sequence t o r em ove the t r ansient subscript ion. I n fact , as you will see lat er on, m anaging a t ransient subscr ipt ion is not t he only feat ure of COM+ event s t hat r equires program m ing against t he COM+ Cat alog: im plem ent ing, adding, and rem ov ing a publisher filt er and t r ansient subscript ions filt ering ar e also only available progr am m at ically. I n all t hese cases, t he developer is requir ed t o pr ogr am against t he Cat alog int er faces. The Cat alog int er faces hav e t he following lim it at ions: •
•
•
They are not t ype safe: o A BSTR is used for represent ing GUI D, I I D, and CLSI D. o A BSTR is used inst ead of norm al st r ing. o Am orphous Var iant s are used t o represent m any dat a t y pes. The COM+ int erfaces and t he underlying program m ing m odel and obj ect s hierar chy r equire t ons of generic code for it erat ing over t he Cat alog, ev en for sim ple t asks. The r esult ing code is t edious and err or prone.
To alleviat e t he sit uat ion, I developed an easy - t o- use wrapper obj ect ar ound t he COM+ Cat alog. The wrapper obj ect saves y ou t he agony of program m ing direct ly against t he Cat alog, r educing hundr eds of lines of code t o a m er e line or t wo. The wr apper obj ect encapsulat es t he cat alog obj ect s and int erfaces, exposing inst ead sim ple cust om int erfaces ( wit h t ype safet y) t hat per form all t he har d wor k for you ( see Figure 9- 7) . The wrapper obj ect int erfaces prov ide one- st op shopping for easy m anagem ent of t r ansient subscript ions and publisher filt ering, providing you t he sam e funct ionalit y as t he Cat alog int er faces wit h a fr act ion of t he code. Figu re 9 - 7 . Th e Ca t a log w r ap pe r h e lp er obj e ct
261
I n t he r est of t his chapt er , t he use of t he wr apper obj ect will be dem onst r at ed. I t s im plem ent at ion will also be described. The wrapper obj ect source files ar e available on t his book’s web sit e, ht t p: / / or eilly .com / cat alog/ com dot net svs/ . The first t hing you will use t he wrapper obj ect for is regist er ing a t r ansient subscript ion wit h t he COM+ Cat alog. The Cat alog wr apper encapsulat es t he code required t o regist er a t ransient subscr ipt ion by exposing t he ITransientSubscription int erface, defined as: interface ITransientSubscription : IUnknown { HRESULT Add([in,string]LPCWSTR pwzName,[in]CLSID clsidEventClass, [in]REFIID iidInterface,[in]IUnknown *pSink); HRESULT Remove([in,string]LPCWSTR pwzName); HRESULT AddFilter([in,string]LPCWSTR pwzSubName, [in,string]LPCWSTR pwzCriteria); HRESULT RemoveFilter([in,string]LPCWSTR pwzSubName); }; ITransientSubscription pr ov ides y ou wit h every t hing y ou need t o easily m anage a t ransient subscr ipt ion—you can add a subscr ipt ion t o all t he int er faces of a specified ev ent class or t o a part icular int er face on t hat class. Lat er , you w ill use ITransientSubscription t o inst all or rem ove a t ransient subscriber- side filt er . Adding a t ransient subscript ion using t he helper obj ect is a one liner—a vast ly sim plified program m ing m odel t hat com plet ely encapsulat es t he underly ing Cat alog. Aft er init ializing a point er t o a sink ( pMySink) t hat y ou want t o r eceive event s on, creat e t he wrapper obj ect using CLSID_CatalogWrapper and call TransientSubscription::Add( ): //Creating the wrapper object: ITransientSubscription* pTransSubs = NULL;
::CoCreateInstance(CLSID_CatalogWrapper,...,IID_ITransien tSubscription, (void**)&pTransSubs); //Adding a transient subscription: pTransSubs ->Add(L"My Subs",CLSID_MyEventClass,
262
IID_NULL,//All interfaces of the event class pMySink); //When you wish to unsubscribe: pTransSubs ->Remove(L"My Subs"); //Releasing the wrapper object: pTransSubs ->Release( ); When y ou add a subscr ipt ion, you provide t he Cat alog wr apper w it h t he subscript ion nam e—a st r ing ident ify ing t he subscript ion. The nam e is used t o ident ify t he subscript ion w hen y ou want t o r em ove it lat er or when you want t o associat e a filt er wit h it . Transient subscr ipt ions ar e m or e efficient t han persist ent subscript ions because t hey do not requir e you t o pay t he ov er head of creat ing t he obj ect . However, t ransient subscr ipt ions r aise som e lifet im e issues of classic COM t ight ly- coupled event s. Anot her deficiency of t r ansient subscript ions is t hat t he part y adding t hem has t o hav e adm inist r at iv e pr ivileges t o m odify t he Cat alog.
9 .5 D e live r in g Eve n t s Once an ev ent is published, COM+ is responsible for delivering t he event t o t he subscriber s. By default , publisher s have very lit t le t o do wit h t he delivery it self, t o ensur e decoupling of publisher s from subscribers. However , COM+ does provide you way s t o fine- t une t he delivery and obt ain addit ional infor m at ion on t he result of fir ing t he ev ent t o t he subscriber s. 9 .5 .1 Se r ia l Ve r su s Pa r a lle l Pu blish in g Event s by default ar e fir ed serially at subscriber s—COM+ goes t hr ough t he list of subscr ibers and publishes t o t hem one at a t im e. The call t o t he event class does not r et ur n t o t he publisher unt il t he last subscriber is not ified. As a result , t he publisher is blocked during publishing. To m inim ize t he block ing t im e, you can configur e your event class t o use m ult iple t hreads for publishing by checking t he " Fire in parallel" checkbox in t he Advanced t ab of t he event class propert ies page ( see Figure 9- 4) . This set t ing is a m er e r equest t hat COM+ will fir e in par allel, and COM+ is not requir ed t o com ply . COM+ uses t hreads from t he RPC pool of t hreads t o publish t o subscr ibers, so parallel publishing is subj ect ed t o pool lim it at ions. You should consider Fir e in par allel as an opt im izat ion t echnique only ; avoid relying on it in your design. For ex am ple, do not count on having all t he subscr ibers get t he event at t he sam e t im e.
263
9 .5 .2 Er r or H a n dling When an ev ent class succeeds in publishing t o all t he subscribers, it ret ur ns S_OK t o t he publisher. I f t he event is deliv ered t o COM+ but t here are no subscr ibers, t he ret urn code is EVENT_S_NOSUBSCRIBERS. I f t he ev ent is deliver ed, but is unable t o inv oke any of t he subscriber s, t he ret ur n code is EVENT_E_ALL_SUBSCRIBERS_FAILED. I n t he case of par t ial delivery ( an event t hat invok es som e, but not all, subscribers) , t he r et ur n code is EVENT_S_SOME_SUBSCRIBERS_FAILED. To pr om ot e loose coupling bet ween t he publisher and t he subscribers, COM+ does not pr ovide success or failur e infor m at ion about delivery for part icular subscr ibers. The rat ionale is t hat publisher s should not car e about par t icular subscriber s. However, if your publisher does care about success or failur e when delivering ev ent s t o par t icular subscr ibers, you can im plem ent a publisher filt er t o handle t his case, which is discussed in t he nex t sect ion. 9 .5 .3 Pu blish in g Or de r COM+ does not , by default , provide a way t o specify t he order in which an event get s deliv ered t o m ult iple subscriber s. The publisher fires at t he event class, and under - t he- hood COM+ scans t he list of subscribers and publishes t o t hem . The event s are published one at a t im e t o t he subscr ibers, in no det erm ined or necessarily repeat able order. Publisher s can cont rol t he or der in which subscribers receive an event by im plem ent ing a publisher filt er.
9 .6 Eve n t Filt e r in g I f you would like t o alt er t he default publish/ subscr ibe behavior , COM+ provides a m echanism called event filt ering. There are t wo kinds of filt er ing. The fir st , publisher filt ering, let s y ou change t he way event s are published and t herefore affect all t he subscriber s for an ev ent class. The second, subscriber filt ering, affect s only t he subscriber using t hat filt er. Bot h kinds of filt er s usually let you filt er event s wit hout changing t he publisher or t he subscriber code. However , I find t hat event filt er ing is eit her cum bersom e t o use and im plem ent , or lim it ed and incom plet e in what it offers. Those shor t com ings are m it igat ed by t he use of t he COM+ Cat alog w rapper obj ect . 9 .6 .1 Pu blish e r Filt e r ing
264
Publisher filt ering is a power ful m echanism t hat gives t he publisher fine- grained cont rol ov er event delivery. You can use a filt er t o publish t o only cer t ain subscr ibers, cont rol t he order in which subscribers get an ev ent , and find out w hich subscriber s did not get an ev ent or had encount ered an err or processing it . The publisherside filt er int ercept s t he call t he publisher m ak es t o t he ev ent class, applies filt ering logic on t he call, and perform s t he act ual publishing ( see Figure 9- 8) . Figu re 9 - 8 . A p ublish e r filt er
I f you associat e a filt er wit h an ev ent class, all ev ent s published using t hat class go t hrough t he filt er fir st . You ar e responsible for im plem ent ing t he filt er ( you will see how shor t ly) and t o regist er it in t he COM+ Cat alog. The publisher filt er CLSI D is st or ed in t he COM+ Cat alog as a pr opert y of t he event class t hat it filt er s. At any giv en t im e, an ev ent class has at m ost one filt er CLSI D associat ed wit h it . As a r esult , inst alling a new filt er overr ides t he ex ist ing one. When a publisher fir es event s on t he event class, COM+ cr eat es t he publisher obj ect and let s it perfor m t he filt ering. 9 .6 .1 .1 I m p le m e n t in g a pu blish e r filt er
A publisher - side filt er is a COM obj ect t hat im plem ent s an int er face called IMultiInterfacePublisherFilter. The filt er need not necessarily be a COM+ configur ed com ponent . The filt er int erface nam e cont ains t he word Mult i because it filt ers all t he event s fir ed on all t he int erfaces of t he ev ent class. Anot her int erface, called IPublisherFilter, allows you t o associat e a filt er wit h j ust one sink int erface support ed by an event class. I t is st ill m ent ioned in t he docum ent at ion, but has been deprecat ed ( i.e., don’t use it ) . The definit ion for IMultiInterfacePublisherFilter is: interface IMultiInterfacePublisherFilter : IUnknown { HRESULT Initialize([in]IMultiInterfaceEventControl* pMultiInterfaceEventControl); HRESULT PrepareToFire([in]IID* piidSink,[in]BSTR bstrMethodName, [in]IFiringControl* pFiringControl); }
265
Only COM+ calls t he m et hods of IMultiInterfacePublisherFilter as par t of t he event publishing sequence. I f an ev ent class has a publisher filt er obj ect associat ed wit h it , COM+ CoCreat es t he filt er obj ect and calls t he Initialize( ) m et hod when t he publisher CoCreat es t he event class. Each t im e t he publisher fir es an event at t he event class, inst ead of publishing t he ev ent t o t he subscr ibers, COM+ calls t he PrepareToFire( ) m et hod and let s you do t he filt ering. When t he publisher releases t he event class, COM+ r eleases t he filt er obj ect . When t he Initialize( ) m et hod is called, COM+ passes in as a par am et er an int er face point er of t ype IMultiInterfaceEventControl, defined as: interface IMultiInterfaceEventControl : IUnknown { HRESULT GetSubscriptions( [in] IID* piidSink, [in] BSTR bstrMethodName, [in] BSTR bstrCriteria, [in] int* nOptionalErrorIndex, [out, retval] IEventObjectCollection** ppCollection); //Other methods } The only m et hod of IMultiInterfaceEventControl r elevant t o publisher - side filt ering is GetSubscriptions( ), used t o get t he list of subscriber s at t he t im e t he event is published. Since COM+ calls t he Initialize( ) m et hod only once, y ou should cache t he IMultiInterfaceEventControl point er as a m em ber variable of t he filt er obj ect . The act ual filt ering work is perform ed in t he scope of t he PrepareToFire( ) m et hod. The first t hing you need t o do in t he PrepareToFire( ) m et hod is call t he IMultiInterfaceEventControl::GetSubscriptions( ) m et hod, passing an init ial filt er ing cr it er ia in as a param et er . Filt ering crit eria ar e m ere opt im izat ions—a filt er is used t o inspect subscribers, and t he filt er m ay provide COM+ wit h an init ial cr it er ion of which subscr ibers t o ev en consider for publishing. The cr it erion is a BSTR cont aining som e inform at ion about t he subscribers. For exam ple, consider a filt er ing cr it erion of t he form : _bstr_t bstrCriteria = "EventClassID == {F89859D1-656511D1-88C8-0080C7D771BF} AND MethodName = \"OnNewOrder\""; This causes COM+ t o ret riev e only subscr ibers t hat hav e subscr ibed t o t he specified event class and for t he m et hod called OnNewOrder on one of t he event class int er faces.
266
Anot her exam ple of a cr it er ion is ALL, m eaning j ust get all t he subscribers. See t he IMultiInterfaceEventControl docum ent at ion for m ore infor m at ion on t he exact crit eria synt ax . GetSubscriptions( ) ret urns an int er face point er of t ype IEventObjectCollection, which you use t o access t he subscr ibers collect ion. Nex t , y ou call IEventObjectCollection::get_NewEnum( ) t o get an enum er at or of t y pe IEnumEventObject t o it erat e ov er t he subscribers collect ion. While y ou it er at e, you get one subscriber at a t im e in t he form of IEventSubscription. You r et rieve t he IEventSubscription proper t ies ( such as t he subscriber ’s nam e, descript ion, I I D) , apply filt ering logic, and decide if y ou want t o publish t o t hat subscriber. I f you w ant t o fire t he ev ent at t hat subscriber, use t he last param et er passed t o PrepareToFire( ), a point er of t ype IFiringControl, passing in t he Subscr iber int er face: pFiringControl->FireSubscription(pSubscription); At t his point , y ou also get t he exact success code of publishing t o t hat part icular subscriber. You t hen r elease t he curr ent subscriber and cont inue t o it erat e over t he subscr ipt ion collect ion. I f you want t o publish t o t he subscr ibers in a different order t han t he one in which COM+ handed t hem t o y ou, y ou should it erat e over t he ent ire collect ion, copy t he subscr ibers t o your own local list , sort t he list t o your liking, and t hen fir e. 9 .6 .1 .2 Th e CGen er icFilt e r h e lp er cla ss
By now, y ou probably feel discouraged fr om im plem ent ing a publisher - side filt er. The good news is t hat t he filt ering plum bing is generic, so I was able t o im plem ent all of it in an ATL COM obj ect called CGenericFilter . CGenericFilter per for m s t he m essy int er act ion wit h t he COM+ event syst em required of a publisher filt er . All you hav e t o do is provide t he filt ering logic ( which is what a filt er should do) . As part of t he source files av ailable wit h t his book at O’Reilly’s web sit e, you w ill find t he Filt er proj ect —an ATL pr oj ect cont aining t he im plem ent at ion of t he CGenericFilter class. CGenericFilter let s you cont r ol which subscr ibers t o publish t o. I f y ou want a differ ent filt er , such as one t hat cont r ols t he publishing order, y ou can im plem ent t hat filt er y our self, using t he sour ce files as a st art ing point . The CGenericFilter class definit ion is ( wit h som e code om it t ed for clar it y) : class CGenericFilter: public CComObjectRootEx, public CComCoClass,
267
public IMultiInterfacePublisherFilter { public: CGenericFilter( ); void FinalRelease( ); BEGIN_COM_MAP(CGenericFilter) COM_INTERFACE_ENTRY(IMultiInterfacePublisherFilter) END_COM_MAP( ) //IMultiInterfacePublisherFilter STDMETHOD(Initialize)(IMultiInterfaceEventControl* pMultiEventControl); STDMETHOD(PrepareToFire)(IID* piidSink, BSTR bstrMethodName, IFiringControl* pFiringControl); //Helper methods, used for domain logic specific filtering HRESULT ExtractSubscriptionData(IEventSubscription* pSubscription, SubscriptionData* pSubscriptionData)const; BOOL ShouldFire(const SubscriptionData& subscriptionData)const; _bstr_t GetCriteria( )const; IMultiInterfaceEventControl* m_pMultiEventControl; }; The only t hing y ou have t o prov ide is t he applicat ion dom ainspecific filt ering logic, encapsulat ed in t he t w o sim ple helper m et hods: CGenericFilter::ShouldFire( ) and CGenericFilter::GetCriteria( ). The CGenericFilter im plem ent at ion calls GetCriteria( ) once per event t o allow you t o prov ide a filt ering crit eria. The default im plem ent at ion r et urns ALL: _bstr_t CGenericFilter::GetCriteria( )const { _bstr_t bstrCriteria = "ALL";//ALL means all the subscribers, //regardless of event classes return bstrCriteria; } CGenericFilter::ShouldFire( ) is t he m ost int erest ing m et hod her e. CGenericFilter calls t he m et hod once per subscriber for a par t icular ev ent . I t passes in as a param et er a cust om st r uct of t y pe
268
SubscriptionData, which cont ains ev er y av ailable bit of infor m at ion about t he subscriber —including t he nam e, descr ipt ion, and m achine nam e: struct SubscriptionData { _bstr_t bstrSubscriptionID; _bstr_t bstrSubscriptionName; _bstr_t bstrPublisherID; _bstr_t bstrEventClassID; _bstr_t bstrMethodName; _bstr_t bstrOwnerSID; _bstr_t bstrDescription; _bstr_t bstrMachineName; BOOL bPerUser; CLSID clsidSubscriberCLSID; IID iidSink; IID iidInterfaceID; }; ShouldFire( ) exam ines t he subscriber and ret ur ns TRUE if y ou wish t o publish t o t his subscr iber or FALSE ot her wise. An exam ple for im plem ent ing filt ering logic in ShouldFire( ) is t o publish only t o subscr ibers whose descr ipt ion field in t he Com ponent Services Explorer says Paid Ex t r a. See Exam ple 9- 2. Ex a m p le 9 - 2 . Ba se you r im p le m e n t a t ion of Sh ou ld fire ( ) on t h e in form a t ion in Su bscr ip t ion D at a
BOOL CGenericFilter::ShouldFire(const SubscriptionData& subscriptionData)const { if(subscriptionData.bstrDescription == _bstr_t("Paid Extra")) return TRUE; else return FALSE; } Finally, Ex am ple 9- 3 shows t he CGenericFilter im plem ent at ion of PrepareToFire( ), which cont ains all t he int eract ion wit h t he COM+ event sy st em out lined previously ; som e err or - handling code was rem oved for clar it y. Ex a m p le 9 - 3 . CGe n e r icFilt e r im p le m en t a t ion of Pr e pa re ToFir e( )
STDMETHODIMP CGenericFilter::PrepareToFire(IID* piidSink, BSTR bstrMethodName, IFiringControl* pFiringControl) { HRESULT hres = S_OK; DWORD dwCount = 0; 269
IEnumEventObject* pEnum = NULL; IEventSubscription* pSubscription = NULL; IEventObjectCollection* pEventCollection = NULL; _bstr_t bstrCriteria = GetCriteria( the criteria
);//You provide
hres = m_pMultiEventControl>GetSubscriptions(piidSink, bstrMethodName, bstrCriteria,NULL, &pEventCollection); //Iterate over the subscribers, and filter in this example by name hres = pEventCollection->get_NewEnum(&pEnum); pEventCollection->Release( ); while(TRUE) { hres = pEnum>Next(1,(IUnknown**)&pSubscription,&dwCount); if(S_OK != hres) { //Returns S_FALSE when no more items if(S_FALSE == hres) { hres = S_OK; } break; } long bEnabled = FALSE; hres = pSubscription->get_Enabled(&bEnabled); if(FAILED(hres) || bEnabled == FALSE) { pSubscription->Release( ); continue; } SubscriptionData subscriptionData; subscriptionData.iidSink = *piidSink; //A helper method for retrieving all of the subscription //properties and packaging them in the handy SubscriptionData hres = ExtractSubscriptionData(pSubscription,&subscriptionData);
270
if(FAILED(hres)) { pSubscription->Release( ); continue; } //You provide the filtering logic in ShouldFire( ) BOOL bFire = ShouldFire(subscriptionData); if(bFire) { pFiringControl->FireSubscription(pSubscription); } pSubscription->Release( ); } pEnum->Release(
);
return hres; } Again, let m e em phasize t hat all you hav e t o pr ovide is t he filt er ing logic in ShouldFire( ) and GetCriteria( ); let CGenericFilter do t he hard work for y ou. 9 .6 .1 .3 Par am e t e r s- b ased pu blish e r filt e rin g
What begs an answer now ( as I am sur e you have already wondered) is why is PrepareToFire( ) called " Pr epare" if t he event is fired t her e? Why not j ust call it Fire( )? I t is called Prepare t o suppor t filt er ing based on t he ev ent param et er s as well. I n PrepareToFire( ), COM+ only t ells y ou what event is fired. What if you need t o ex am ine t he act ual event param et er s t o m ake a sound decision on whet her or not y ou want t o publish? I n t hat case, t he publisher filt er can im plem ent t he sam e sink int erfaces as t he event class it is filt er ing. Aft er calling PrepareToFire( ), COM+ quer ies t he filt er obj ect for t he sink int er face. I f t he filt er suppor t s t he event int erface, COM+ only fires t o t he filt er . The filt er should cache t he inform at ion fr om PrepareToFire( ) and per form t he fine- t uned param et er s- based filt er ing. I n it s im plem ent at ion of t he sink m et hod, it uses IFireControl t o fir e t he event t o t he client . 9 .6 .1 .4 Cu st om su bscr ip t ion pr ope rt ie s
Publisher- side filt ers usually base t heir filt er ing logic on t he st andar d subscript ion proper t ies—t he subscr ipt ion nam e, descript ion, and so on. These proper t ies ar e pre- defined and ar e available for every subscript ion. COM+ also allows y ou t o define new cust om pr opert ies for subscr ipt ions and assign v alues t o t hese proper t ies, t o be used by t he publisher filt er. Usually , you can t ake adv ant age of cust om pr opert ies if y ou develop bot h t he subscr ibing
271
com ponent and t he publisher filt er. You can define cust om subscript ion pr opert ies adm inist rat ively only for persist ent subscribers. To define a new cust om proper ty , display t he subscr ipt ion proper t ies page, and select t he Publisher Pr opert ies t ab ( the nam e is m isleading) . You can click t he Add but t on t o define a new proper t y and specify it s v alue ( see Figur e 9- 9) . Figu r e 9 - 9 . De fin ing n ew cu st om p rop er t ies a n d assign ing va lue s on t h e Pu blish e r Prope r t ies t a b
Transient subscr ibers have t o program against t he com ponent COM+ Cat alog. Get hold of t he t ransient subscr ipt ion collect ion, find your t r ansient subscript ion cat alog obj ect , and nav igat e from it t o t he PublisherProperties collect ion. You can t hen add or rem ove cust om proper t ies in t he collect ion. As explained befor e, when t he publisher filt er it er at es ov er t he subscript ion collect ion, it get s one subscr iber at a t im e in t he form of an IEventSubscription int erface point er. The filt er can call IEventSubscription::GetPublisherProperty( ), specify t he cust om proper t y nam e, and r et rieve it s value. For ex am ple, here is how you ret r ieve a cust om subscriber proper t y called Company Name: _bstr_t bstrPropertyName = "Company Name"; _variant_t varPropertyValue; hres = pSubscription>GetPublisherProperty(bstrPropertyName,&varPropertyValue) ; I f t he subscriber does not hav e t his pr opert y defined, GetPublisherProperty( ) ret ur ns S_FALSE. You can even define m et hod param et er nam es as cust om pr opert ies and specify a v alue or r ange in t he proper t y dat a. I f t he filt er is doing par am et ers272
based filt er ing, it can be wr it t en t o par se t he cust om pr opert y v alue and t o publish t o t hat subscr iber only when t he param et er value is in t hat range. 9 .6 .1 .5 I n st allin g a pu blish e r filt e r
Ther e are t wo ways for associat ing a publisher filt er wit h an event class. I n t he absence of any nam es for t hese t wo way s fr om t he COM+ t eam at Micr osoft , I call t he fir st st at ic associat ion and t he second dynam ic associat ion. St at ic associat ion requir es you t o pr ogram against t he COM+ Cat alog and st or e t he filt er CLSI D as a proper t y of t he event class. The filt er will st ay t here unt il y ou r em ove it or ov erride it wit h anot her CLSI D. St at ic associat ion affect s all publishers t hat use t hat event class, in addit ion t o all inst ances of t he ev ent class. Dynam ic associat ion t akes place at runt im e. The publisher will not only creat e an event class, but also direct ly creat es a filt er obj ect and associat es it only wit h t he inst ance of t he ev ent class it current ly has. Dynam ic associat ion affect s only t he publishers t hat use t hat par t icular inst ance of t he ev ent class. Dynam ic associat ion does not persist beyond t he lifet im e of t he event class obj ect . Once you r elease t he event class, t he associat ion is gone. Dy nam ic associat ion allows a publisher t o bind a part icular inst ance of an event class wit h a part icular inst ance of a filt er ; it ov errides any st at ic filt er cur rent ly inst alled. The m ain disadvant age of dynam ic associat ion is t hat y ou cannot dynam ically associat e a filt er wit h an inst ance of a queued event class ( discussed lat er on) , since y ou are int eract ing wit h t he recorder for t he ev ent class, not t he event class it self. 9 .6 .1 .6 St a t ic a ssocia t ion of a pu blish e r filt e r w it h a n e ve n t cla ss
To st at ically associat e a publisher filt er CLSI D wit h t he event class you want it t o filt er , y ou have t o follow t hese st eps: 1. Cr eat e t he cat alog obj ect . 2. Get t he Applications collect ion. 3. For each applicat ion in t he collect ion, get t he Components collect ion. 4. I t erat e t hrough t he Components collect ion looking for t he event class. I f t he class is not found, get t he nex t Application collect ion and scan it s Components collect ion. 5. Once y ou find t he ev ent class, set t he MultiInterfacePublisherFilterCLSID event class propert y t o t he CLSI D of t he filt er. 6. Save changes on t he Components collect ion and release every t hing.
273
Again, t he Cat alog wrapper helper obj ect is useful, as it sav es you t he int er act ion wit h t he COM+ Cat alog. The helper obj ect im plem ent s an int er face called IFilterInstaller, defined as: interface IFilterInstaller : IUnknown { HRESULT Install([in]CLSID clsidEventClass,[in]CLSID clsidFilter); HRESULT Remove ([in]CLSID clsidEventClass); }; IFilterInstaller m ak es adding a filt er a br eeze—j ust specify t he CLSI D of t he ev ent class and t he CLSI D of t he filt er , and it will do t he rest for you: HRESULT hres = S_OK; hres = ::CoCreateInstance(CLSID_CatalogWrapper,NULL,CLSCTX_ALL, IID_IFilterInstaller,(void**)&pFilterInstaller); hres = pFilterInstaller>Install(CLSID_MyEventClass,CLSID_MyFilter); pFilterInstaller->Release( ); Not e t hat y ou do not need t o specify t he applicat ion nam e as a par am et er; j ust specify t he ev ent class and t he filt er CLSI D. Use IFilterInstaller::Remove( ) t o r em ove any filt er associat ed wit h a specified event class. 9 .6 .1 .7 D yn a m ic a ssocia t ion of a pu blish e r filt er w it h a n e ve n t cla ss
To associat e a publisher filt er obj ect wit h an ev ent class inst ance dynam ically , follow t hese st eps: 1. Cr eat e t he ev ent class and get t he sink int er face. 2. Query t he event class for IMultiInterfaceEventControl int er face. 3. Cr eat e t he filt er obj ect . 4. Call IMultiInterfaceEventControl::SetMultiInterfacePublis herFilter( ) and pass in t he filt er obj ect . 5. Release IMultiInterfaceEventControl. 6. Publish ev ent s t o t he ev ent class obj ect . The event s will go t hr ough t he filt er you hav e j ust set up. 7. Release t he event class and t he filt er when y ou are done publishing. Exam ple 9- 4 shows som e sam ple code t hat uses t his t echnique. Ex a m p le 9 - 4 . I n st a llin g a publish e r - side filt e r dyn a m ica lly
274
HRESULT hres = S_OK; IMySink* pMySink = NULL; IMultiInterfacePublisherFilter* pFilter = NULL; IMultiInterfaceEventControl* pEventControl = NULL; //Create the filter hres = ::CoCreateInstance(CLSID_MyFilter,NULL,CLSCTX_ALL, IID_IMultiInterfacePublisherFilter,(void**)&pFilter); //Create the event class hres = ::CoCreateInstance(CLSID_MyEventClass,NULL,CLSCTX_ALL, IID_IMySink,(void**)&pMySink); //Query the event class for IMultiInterfaceEventControl hres = pMySink >QueryInterface(IID_IMultiInterfaceEventControl, (void**)pEventControl); //Setting the filter hres = pEventControl>SetMultiInterfacePublisherFilter(pFilter); pEventControl->Release( ); //Firing the event hres = pMySink->OnEvent1(
);//The event is now filtered
pMySink->Release( ); pFilter->Release( ); Unfort unat ely, COM+ has a bug r egarding cor rect handling of dynam ically associat ing a publisher filt er wit h an event class. COM+ does not call t he filt er m et hod IMultiInterfacePublisherFilter::Initialize( ), and as a result , y ou can’t do m uch filt ering. I hope t his sit uat ion will be fixed in a fut ur e release of COM+ . This defect , plus dynam ic associat ion’s inabilit y t o work wit h queued event classes, renders it effect iv ely useless. Avoid dynam ic associat ion of a publisher filt er ; use st at ic associat ion inst ead. 9 .6 .2 Su bscr ibe r - Side Filt e r in g Not all subscriber s hav e m eaningful oper at ions t o do as a r esponse t o every published event . Your subscriber m ay want t o t ake act ion only if your favor it e st ock is t rading, or perhaps only if it is t rading above a cert ain m ar k. One possible course of act ion is t o accept t he event , exam ine t he par am et ers and decide whet her t o process t he event or discard it . However, t his act ion is inefficient if t he subscriber is not int er est ed in t he event for t he following reasons:
275
• • • •
I t forces a cont ext swit ch t o allow t he subscr iber t o exam ine t he ev ent . I t adds r edundant net work round t rips. Writ ing ex t r a ex am inat ion code m ay int roduce defect s and requir e addit ional t est ing. Event exam inat ion and pr ocessing policies change over t im e and bet ween cust om er s. You will chase your t ail t r ying t o sat isfy ev erybody.
What you should really do is t o put t he filt er ing logic out side t he scope of t he subscriber. You should have an adm inist r at iv e, configurable, post - com pilat ion, deploym ent - specific filt ering abilit y . This is exact ly what subscriber- side filt ering is all about ( see Figure 9- 10) . Subscr ibers t hat do not w ant t o be not ified of every event published t o t hem , but want t o be not ified only if an event m eet s cert ain crit eria, can specify filt er ing cr it er ia. Figu r e 9 - 1 0 . Spe cifyin g filt er ing cr it er ia for a pe rsist e n t su bscr ib er
A subscr iber- side filt er is a st r ing cont aining t he filt ering crit eria. For exam ple, suppose you subscribe t o an event not ifying y ou of a new user in your port folio m anagem ent syst em , and t he m et hod signat ure is: HRESULT OnNewUser([in]BSTR bstrName,[in]BSTR bstrStatus); You can specify such filt er ing cr it er ia as: bstrName = "Bill Gates" AND bstrStatus = "Rich" The event will only be deliver ed t o your obj ect if t he usernam e is Bill Gat es and his cur rent st at us is Rich. The filt er cr it er ia st ring recognizes relat ional operat or s for checking equalit y ( ==,!=) , nest ed parent heses, and logical k ey words AND, OR, and NOT. COM+ evaluat es t he expression and allows t he call t hr ough only if t he cr it eria are evaluat ed t o be t rue.
I f y ou have w rong par am et ers or spelling m ist ak es, or if t he param et er nam es were changed, t he subscriber will nev er be not ified. Because subscriber- side filt er ing occurs only aft er t he ev ent has been fired, if a publisher filt er is used, t hen t he event has t o pass
276
t he publisher filt er fir st . The obv ious conclusion is t hat publisherside filt ering t ak es precedence over subscriber - side filt ering. 9 .6 .2 .1 Pe r sist e n t su b scribe r- sid e filt e rin g
Only per sist ent subscriber s can specify a subscr iber filt er adm inist rat ively. They can do so by display ing t he per sist ent subscript ion pr opert ies page, select ing t he Opt ions t ab, and specify ing t he Filt er crit eria ( see Figur e 9- 11) . Figu re 9 - 1 1 . Su bscr ibe r - side filt e ring
9 .6 .2 .2 Tr a n sien t subscribe r- sid e filt e rin g
Transient subscr ibers have t o program against t he Cat alog t o set a t r ansient subscript ion filt er crit eria, following sim ilar st eps t o t hose per form ed when regist er ing a t r ansient subscript ion: 1. 2. 3. 4.
Get hold of t he Cat alog int erface. Get t he t ransient subscr ipt ion collect ion obj ect . Find your t ransient subscr ipt ion. Set a subscript ion propert y called FilterCriteria t o t he st ring v alue of your filt er. 5. Save changes and release ev ery t hing.
The Cat alog w rapper ’s int erface ITransientSubscription, discussed ear lier, allow s y ou t o add ( or rem ov e) a subscriber- side filt er t o a t r ansient subscript ion wit h t he AddFilter( ) and RemoveFilter( ) m et hods. The m et hods accept t he subscr ipt ion nam e and a filt er ing st ring. Exam ple 9- 5 dem onst r at es t he sam e ex am ple from t he persist ent subscriber filt er, but for a t ransient subscr iber for t he sam e event . Ex a m p le 9 - 5 . Addin g a t r an sie n t su bscr ip t ion filt e ring crit e r ia u sing t h e w r a pp er obj e ct
277
//Adding a transient subscription filter: LPCWSTR pwzCriteria = L"bstrUser = \"Bill Gates\" AND bstrStatus = \"Rich\"" //"MySubs" is the transient subscription name hres = pTransSubs->AddFilter(L"MySubs",pwzCriteria);
//Or removing the filter: pTransSubs ->RemoveFilter(L"MySubs"); The m ain disadvant age of a t r ansient subscriber filt er com par ed t o a per sist ent subscriber filt er is t hat y ou hardcode a filt er , w hich is som et im es deploy m ent - or cust om er- specific. Persist ent subscr ibers can alway s change t he filt er ing cr it er ia using t he Com ponent Serv ices Explorer dur ing deploy m ent .
9 .7 D ist r ibu t e d COM+ Eve n t s As long as t he publisher, t he ev ent class, and t he subscriber s are all inst alled on t he sam e m achine, you can have pret t y m uch any t opology of int er act ion ( see Figure 9- 12) . On t he sam e m achine, publisher s can publish t o any event class, event classes can deliver event s t o any subscr iber, and subscriber s can subscribe t o as m any event classes as t hey like. Figu re 9 - 1 2 . You ca n h a ve an y pu blish e r an d sub scribe r t opology on t h e sa m e m a ch in e
Unfort unat ely, t he COM+ event ser vice has a serious lim it at ion—t he event class and all it s subscr ibers have t o be on t he sam e m achine. This m eans t hat a deploym ent , such as t he one shown in Figure 913, is not possible. Figu r e 9 - 1 3 . Th e e ve n t class a n d t h e sub scribe r m u st re sid e on t h e sa m e m a ch in e
278
The r est of t his sect ion present s y ou wit h a few wor karound solut ions for t his pr oblem t hat allow you t o dist r ibut e your event s across t he net wor k. All t he solut ions adhere t o t he lim it at ion t hat t he ev ent class and t he subscriber s have t o reside on t he sam e m achine, and t hey solve t he problem by designing around it . Like m ost t hings in life, each solut ion has pros and cons. I t will be ult im at ely up t o you, t he sy st em designer, t o select t he m ost appr opr iat e solut ion for y our dom ain problem at hand. 9 .7 .1 Solu t ion 1 : One M a ch in e f or All t h e Subscr ibe r s a n d Ev e nt Cla sse s This solut ion is t he sim plest t o im plem ent . You inst all all ev ent classes on one m achine, along wit h all subscr ibers. You inst all t he event classes in a COM+ ser ver applicat ion and gener at e a proxy inst allat ion ( see Chapt er 1) for t he event classes’ applicat ion. ( Rem em ber , t he event class applicat ion has t o be a server applicat ion for y ou t o export it as a pr oxy applicat ion.) You t hen deploy t he event class pr oxy applicat ion on all t he m achines t hat host publisher s, m aking sur e t he prox y applicat ions point t o t he event classes/ subscribers m achine ( see Figure 9- 14) . . Figu re 9 - 1 4 . Th is solu t ion re quir e s h a ving a ll sub scribe rs an d e ve n t cla sse s on t h e sa m e m a ch in e
When a publisher on a rem ot e Machine A want s t o fir e an event of t y pe E1 , it cr eat es a proxy for t hat ev ent class and calls t he event m et hod on it . The event call will be m arshaled t o t he place wher e t he ev ent class resides—on t he subscr ibers m achine—and get 279
published t o all t he subscriber s t hat subscr ibed t o it . I t is also v er y easy for subscr ibers t o subscribe t o m ore t han one ev ent class, since all t he event classes ar e inst alled locally on t he subscribers m achine This solut ion has t he following disadv ant ages: 1. By locat ing t he event classes away from t he publishers, you int roduce ext ra expensiv e r ound t r ips acr oss t he net work. 2. The single m achine host ing all t he event classes and t he subscribers becom es a hot spot for per form ance. The m achine CPU and operat ing syst em hav e t o handle all t he t r affic. There is no load balancing in your product , and load balancing is a m aj or reason for dist ribut ing your com ponent s in t he fir st place. 3. The subscr ibers m achine solut ion is a single point of failure in your syst em . 4. The subscr ibers ar e not necessar ily ideally deployed. I f t he subscribers do not hav e t o r eside where t he event classes ar e, you m ay have put t hem som ewhere else—m aybe on t he sam e m achine where t he dat abase is if t hey have t o access it fr equent ly . Per form ance m ay suffer. 9 .7 .2 Solu t ion 2 : M a ch in e - Spe cific Eve n t Cla sse s This solut ion allows you t o dist ribut e your subscr ibers anywhere, according t o what ever design preference y ou have. This dist ribut ion m ak es it possible for you t o publish from one m achine t o subscribers t hat reside on m ult iple ot her m achines ( see Figur e 915) . However, t his part icular solut ion is m or e com plex t o m anage and deploy t han t he previous solut ion. Figu re 9 - 1 5 . A h u b m a ch in e h as m a ch in e - sp e cific e ve n t cla ss p rox ie s u se d t o dist ribu t e e ven t s
280
The idea behind t his solut ion is t o cr eat e a COM+ event s hub on one designat ed m achine. The hub m achine is responsible for dist ribut ing t he event s t o where t he subscriber s really reside. This solut ion uses t wo kinds of ev ent classes. The fir st is an ev ent class t hat resides only on t he hub m achine, called Eh . You inst all proxies t o Eh on all t he publishers’ m achines. Publisher s will only publish using Eh . The second k ind of event class is a m achine- specific ev ent class. Every m achine t hat host s subscribers has it s own dedicat ed ev ent class t y pe, inst alled only on t hat m achine. I n Figure 9- 15, t hese t y pes ar e Ea , Eb , and Ec, corr esponding t o t he t hree m achines in t he figur e. You need t o inst all a pr oxy t o every m achine- specific event classes on t he hub m achine. All event classes in t his solut ion suppor t t he exact sam e set of sink int er faces. When a publisher on Machine A w ant s t o publish an event t o subscribers on Machines A, B, and C, t he publisher on Machine A cr eat es an inst ance of t he Eh event class ( which only act ually cr eat es a proxy) and fires t o it . The Eh proxy forwar ds t he call t o wher e Eh really ex ecut es—on t he hub m achine. On t he hub m achine t here is a hub subscriber ( Sh ) t hat subscribes t o t he Eh ev ent . The way Sh handles t he event is t o creat e all t he m achine- specific event classes ( Ea, Eb , and Ec) and fir e t hat par t icular ev ent t o t hem . Because t here are only pr oxy inst allat ions of t he m achine- specific event classes on t he hub m achine, t he event is dist ribut ed t o m ult iple m achines, where local subscr ibers—t he r eal subscriber s— handle t he event . The m ain advant age of using t his solut ion is t hat it giv es you com plet e freedom in locat ing your subscriber s. However, t he flex ibilit y com es wit h a heft y price: •
•
•
•
When y ou publish, y ou encount er m any expensive round t rips across t he net wor k. Even if all t he subscr ibers are on t he publisher m achine, t he publisher st ill has t o go t hrough t he hub m achine. You have t o duplicat e t his solut ion for ev er y kind of event class you hav e, and you t herefore end up wit h separat e set s of m achine- specific and hub event classes. The added com plex it y of t his solut ion m eans t hat y ou probably have a deploy m ent , adm inist r at ion, and m aint enance night m are on y our hands. The hub m achine is pot ent ially a single point of failure in your sy st em .
9 .7 .3 Solu t ion 3 : COM + Rou t in g This last solut ion for dist r ibut ing ev ent s t o subscriber s on m ult iple rem ot e m achines t ak es advant age of a feat ur e pr ovided for y ou by
281
COM+ . However, it is a part ial solut ion because it only w ork s wit h per sist ent subscriber s. I f y our applicat ion uses t ransient subscr ibers ( as it m ost likely will) , y ou have t o use one of t he t wo solut ions discussed previously . The idea here is sim ilar t o t he hub m achine solut ion, and t o dist inguish bet ween t hem , I call t his one t he rout ing solut ion. COM+ provides a field called Server nam e on t he Opt ions t ab for every per sist ent subscript ion ( see Figur e 9- 16) . Figu re 9 - 1 6 . I n st r u ct in g COM + t o cr ea t e t h e su b scr ibe r ob j e ct on t h e m a ch in e sp ecifie d in t h e Se rve r n a m e fie ld
Whenever an event is published t o a persist ent subscriber , before CoCr eat ing t he subscr iber obj ect , COM+ first checks t he v alue of t he Serv er nam e proper t y. I f it is not an em pt y st ring, COM+ CoCr eat es t he subscriber on t he specified m achine, fir es t he event t o t he sink int erface, and r eleases t he subscriber . Rout ing event s t o m ult iple m achines t ak es adv ant age of t his feat ur e. I nst ead of using m achine- specific event classes like in Solut ion 2, t he rout ing solut ion uses m achine- specific persist ent subscript ions. For ex am ple, suppose you have a publisher on Machine A and a subscribing com ponent called My Subscriber t hat y ou want t o deploy on Machines B and C. The publisher publishes using an event class called E. On Machines B and C you add subscr ipt ions t o t he event class, t o t he locally inst alled copies of MySubscriber. You t hen inst all t he MySubscr iber com ponent on anot her designat ed r out ing m achine, t oget her wit h t he event class E, and inst all on Machine A only t he pr oxy t o E ( see Figure 9- 17) . Figu re 9 - 1 7 . Th e rou t ing solu t ion u se s m a ch in e - sp e cific su bscr ipt ion s a n d a r ou t ing m ach ine
282
To t he inst allat ion of My Subscriber on t he r out er m achine ( called SR in Figur e 9- 17) add m achine- specific subscript ions: for every deploy m ent of MySubscriber on anot her m achine, add a subscript ion and redir ect t he inv ocat ion t o t hat m achine, using t he Server nam e field. See Figure 9- 18. Figu r e 9 - 1 8 . Th e r ou t e r m ach in e h as a m a ch in e - spe cific sub script ion u se d t o r ou t e t h e e ven t t o cor re sp on din g m a ch in e s
Now , when t he publisher on Machine A CoCreat es a proxy t o t he event class and fires an ev ent at it , t he call goes t o t he rout er m achine. COM+ inspect s t he subscript ions on t he rout er m achine for t he event class, det ect s t he Server nam e in t he subscr ipt ions, cr eat es t he subscriber s on t he rem ot e m achines, and publishes t o t hem . I already point ed out t he m ain dr awback of t his solut ion ( per sist ent subscribers only ) , but it has a few ot her s: •
Set t ing up and configuring t he sy st em is nont r ivial effort . You hav e t o eit her wr it e som e inst allat ion scr ipt s t o help you aut om at e it or m anually configure it at every cust om er deploy m ent . Every cust om er sit e has it s own m achine nam es; you will not be able t o specify t he m achine nam es in y our applicat ion MSI file, expor t ed for r elease.
283
• • •
You have t o go t hrough t he r out er m achine, so y ou end up pay ing for an ext ra net wor k hop. The r out er m achine can be a per form ance bot t leneck . The r out er m achine is pot ent ially a single point of failur e.
9 .8 Asyn ch r onou s Eve n t s So far, in t his discussion of t he COM+ event m odel, it was always assum ed t hat publishing t he ev ent is a synchronous oper at ion— during publishing, t he publisher is blocked and t hat blocking t im e is propor t ional t o t he num ber of subscriber s and t heir indiv idual processing t im es. A t rue loosely coupled event m echanism decouples t he publisher from t he subscriber even fur t her. I t allow s t he publishers t o publish asy nchr onously and perm it s t he subscribers t o handle t he event asy nchr onously as well. COM+ provides t his capabilit y by using COM+ queued com ponent s ( see Chapt er 8) . As y ou will see, bot h t he event class and t he subscribers can be queued com ponent s, t o enable asynchronous publishing and subscribing. 9 .8 .1 Asyn ch r on ous Pu blish in g COM+ has a built - in serv ice for asy nchronous execut ion: queued com ponent s. COM+ event s and queued com ponent s go t oget her ver y well, giving you t he benefit s of a loosely coupled syst em and t he flex ibilit y of asynchronous ex ecut ion. Every event class support s a set of sink int er faces. As w it h any ot her COM+ com ponent , you can configure any one of t he sink int er faces as Queued. A publisher creat es a queued event class using t he queue m oniker. When a publisher fires an ev ent t o a queued event class int er face, COM+ per for m s it s usual handling of a queued com ponent ( recording t he call, placing it in a queue, and so on) . The COM+ queued com ponent list ener pulls t he m essages ( t he event s) from t he event class queue and plays t hem back t o t he event class. The publisher is blocked only for t he relat iv ely shor t period t im e it t akes COM+ t o recor d t he call. Cont rast t his wit h t he Fire in Parallel at t r ibut e, which ret urns cont r ol t o t he publisher only aft er all subscriber s have been not ified. A publisher t hat is int erest ed in cr eat ing a queued ev ent class cr eat es it using t he queue m onik er , like any ot her queued com ponent . Because of t he inher it ed lim it at ion of queued com ponent s—t hat a queued com ponent cannot reside in a library applicat ion—an event class t hat uses a queued com ponent cannot be in a library applicat ion. One int erest ing side effect of using queued com ponent s is t hat if you publish ev ent s on t wo queued event classes, event s m ay not
284
replay in t he order in which t hey wer e or iginally fired. This sit uat ion can be a sour ce of t rouble if t he t w o publishing sessions ar e relat ed in som e m anner . I f having one event t ak e place before anot her is im port ant , you need t o m ak e t he calls on t he sam e specific queued event class. 9 .8 .2 Asyn ch r on ous Subscr ibe r s COM+ can use queued com ponent s t o invok e calls on a com ponent t hat also uses per sist ent subscript ions. Because COM+ is t he one t hat cr eat es t he subscr iber, you have t o let COM+ know t hat it should creat e t he com ponent using t he queue m onik er, r at her t han CoCreateInstance( ). You do t hat by checking t he Queued proper t y of t he persist ent subscr ipt ion ( see Figure 9- 6) . When COM+ publishes t o a queued subscriber , it post s a m essage t o t he subscriber ’s m essage queue. The list ener of t he COM+ applicat ion t hat host s t he subscriber w ill det ect t he m essages in t he queue, creat e a play er, and play back t he event s t o t he subscr iber. Ther e are t wo m ain advant ages of using queued subscr ibers: •
•
The publisher code r em ains t he sam e for queued and norm al subscribers, and it allows for lengt hy processing of t he event on t he subscr iber side, inst ead of hav ing t o spin off a worker t hr ead, as if you wer e using classic COM. Hav ing bot h t he publisher and t he subscr iber using queued com ponent s allows bot h t o work offline at t he sam e t im e and be com plet ely disconnect ed.
Ther e are also t wo m ain disadv ant ages: •
•
The publisher is st ill blocked while look ing t hr ough t he subscribers list and, for each subscr iber, while cr eat ing a recorder, post ing m essages t o queues, and per form ing ot her queued com ponent m anagem ent act ivit ies. I f som ebody adds a nonqueued subscr ipt ion t o your syst em , t hen publishing is not fully asynchr onous. The publisher is blocked while t he nonqueued subscr iber pr ocesses t he ev ent .
9 .9 COM+ Eve n t s a nd Tr a n sa ct ion s COM+ t r ansact ions flow downward fr om t he t ransact ion r oot , as you hav e seen in Chapt er 4. New obj ect s creat ed during t he t ransact ion t ake part in t heir cr eat or ’s t ransact ion or ar e placed in a t ransact ion of t heir own, according t o t heir t r ansact ion configur at ion.
285
I f t he publisher t ak es par t in a t ransact ion, it is r ecom m ended t hat t he subscriber s par t icipat e in t he publisher’s t r ansact ion. But how would t he t r ansact ion be propagat ed by t he publisher t o t he subscriber if t he publisher does not creat e t he subscr iber direct ly ? To pr opagat e t he publisher’s t ransact ion t o t he subscr iber, you should configure t he ev ent class t o support or r equir e t ransact ions. Like any ot her COM+ com ponent , t he event class has a Transact ion t ab t hat applies t o t he COM+ sy nt hesized im plem ent at ion. Adding t he ev ent class t o your t ransact ion will not affect t he t r ansact ion vot ing result ; in any COM+ cont ext t he consist ency bit is set by default t o TRUE and t he COM+ - provided im plem ent at ion of t he ev ent class does not change t hat bit . You also need t o configure t he ( persist ent ) subscr iber com ponent t o suppor t t ransact ions. Now t he subscriber t akes part in t he publisher ’s t ransact ion and it can abor t t he publisher’s t ransact ion or v ot e t o com m it it . Ther e is one m ore t hing you should keep in m ind when m ix ing COM+ event s and t ransact ions: Do not configur e t he event class t o requir e a new t r ansact ion. This causes t he subscr iber t o t ake par t in a separat e t r ansact ion, t he one init iat ed by t he ev ent class ( see Figure 9- 19) . Figu r e 9 - 1 9 . Con figu ring t h e e ve n t cla ss t o r e qu ire n ew t ra n sa ct ion r esu lt s in a se pa r at e t ra n sa ct ion for t h e persist en t su b scrib e r
I f t he publisher’s t r ansact ion is abort ed, t he subscriber’s t r ansact ion can st ill com m it successfully, which m ay involve changes t o t he dat abase and ot her per sist ent changes t o your syst em st at e. Nobody t ells t he subscr iber t o roll back t hose changes, despit e t he fact t hat t he event t hat t rigger ed t he changes is fired from a t r ansact ion t hat abort s. I n addit ion, when t he publisher t r ies again, t he ev ent m ay be fired once m ore, leaving t he subscriber in an inconsist ent st at e. 9 .9 .1 Pe r sist e n t Su bscr ibe r s a n d Tr a nsa ct ion s Sim ilar ly, av oid configuring any persist ent subscriber ’s t ransact ion set t ing t o Requires New and do not m ix nont ransact ional subscribers wit h t r ansact ional ones; such pract ices m ay int roduce unwelcom e side effect s when t he publisher ’s t r ansact ion is abort ed ( see Figure 9- 20) .
286
Figu r e 9 - 2 0 . Avoid con figu ring sub scribe rs t o r eq uire n ew t r a n sa ct ion s or t o m ix n ont ra n sact ion a l su bscr ib er s w it h t ra n sact ion a l on e s
9 .9 .2 Tr a n sie n t Su bscr ibe r s a nd Tr a n sa ct ions Transient subscr ibers ar e already inst ant iat ed when t he ev ent is fired and m ay be part of t heir creat or’s t r ansact ion. I can only recom m end being m indful when com bining t ransient subscr ipt ions wit h a t ransact ional publisher because you m ay end up wit h t he sam e inconsist encies m ent ioned in t he previous sect ion. I n part icular , t ransient subscr ibers should not abor t t heir client / creat or’s t ransact ion as a r esponse t o a publisher’s event , an event t hat m ay have been fired from wit hin anot her t ransact ion. The problem s t hat arise when y ou com bine t r ansient subscriber s and publisher t r ansact ions ar e t ypical of passing obj ect references across t ransact ion boundar ie. The obj ect does not k now whet her it is allowed t o abort t he t r ansact ion or not ( as discussed in Chapt er 4) .
9 .1 0 COM+ Eve n t s a n d Se cu r it y The fact t hat t he publisher does not call m et hods on t he subscr ibers dir ect ly is an im port ant soft war e engineer ing capabilit y. Nev er t heless, y ou should never decouple y our com ponent s at t he expense of securit y . COM+ m ust st ill allow t he sy st em adm inist rat ors t o configure t he access right s t o subscribers. COM+ event s t ake advant age of t he rich securit y infr ast ruct ure offer ed by COM+ , and COM+ also pr ovides y ou wit h event sy st em - specific securit y set t ings. 9 .1 0 .1 Th e Ev e nt Cla ss a n d Role - Ba se d Se cur it y Like ot her configur ed com ponent s, an ev ent class can use rolebased securit y . The m ost com m on use of use r ole- based securit y for event classes is t o cont r ol which publisher is allowed t o fir e event s.
287
However, since roles in COM+ are per applicat ion, be sur e t o add roles and user s for each product t o t he event class applicat ion if y ou int end t o share event classes bet w een a few applicat ions and product s. You can use role- based secur it y in anot her way: t o im plem ent a publisher - side filt er t hat calls ISecurityCallContext::IsCallerInRole( ) ( discussed in Chapt er 7) and cont rols t he or der of publishing based on t he publisher ’s role. 9 .1 0 .2 Su bscr ibe r s a n d Role - Ba se d Se cu r it y The subscr iber can use role- based secur it y t o cont rol access t o it s serv ices. Unlik e an event class usage of r ole- based securit y ( which affect s t he publishing side and t herefore all t he subscriber s) , w hen a subscriber uses role- based secur it y, only t hat subscriber is affect ed by t he access checks. I f all your subscr ibers have unifor m securit y requirem ent s, put t ing t he secur it y check on t he event class is t he right decision because it im pr oves per form ance ( t he publisher does not publish at all if it is not allow ed t o) . Howev er , if t he securit y requirem ent s of your subscriber s vary ( if som e require t ight er securit y t han ot her s) , put t ing t he securit y access check s on t he sensit iv e subscribers m ay provide you wit h t he bet t er solut ion. 9 .1 0 .3 I n - Pr oce ss Su bscr ibe r s Fr om a securit y point of v iew , an int er est ing sit uat ion arises when t he event class and t he subscriber com ponent are bot h librar y applicat ions. As a result , when t he publisher CoCreat es t he event class and publishes t o it , t he subscr iber is loaded int o t he publisher process. Unlik e a convent ional library applicat ion t hat is int ended t o share t he address space of it s client ( and m ay v ery well be developed by t he sam e t eam ) , t he publisher/ subscriber relat ionship is m uch less t r ust ing and coupled. Most soft ware v endors would feel uneasy let t ing an unk nown ent it y int o t heir pr ocess. The subscriber m ay be of dubious qualit y ( and m ay t ak e t he publisher down wit h it when it cr ashes) or even m alicious ( I will leav e it t o your im aginat ion what you can do if som ebody let s you int o t heir pr ocess) . To pr ot ect t he publisher, t he syst em adm inist rat or can enforce all subscribers t o be creat ed in t heir own pr ocess. On t he Advanced t ab of t he Event Class pr opert ies page, if " Allow in- process subscribers" is not check ed, t he subscr iber obj ect will be creat ed in a separat e process, even if it is configured t o run as a library applicat ion ( see Figure 9- 4) .
288
9 .1 0 .4 Pe r - Use r Su bscr ipt ions COM+ allows you t o deliver an event t o a part icular subscr iber only if a specific user is logged on t o t he publisher’s m achine. When t he user logs off, t he subscr ipt ion is disabled. Per - user subscript ion requir es t he publisher and subscriber t o be on t he sam e com put er, since logon and logoff are only det ect ed locally in Windows. To act iv at e per- user Subscr ipt ion you m ust set t he PerUser flag on t he subscript ion r ecor d t o TRUE and specify a usernam e. You can do t hat by pr ogr am m ing against t he COM+ Cat alog. Per- user subscript ion is an esot eric securit y m echanism , and I recom m end using role- based securit y inst ead t o achieve sim ilar capabilit ies wit h a fr act ion of t he code and r est rict ions.
9 .1 1 COM+ Eve n t s Lim it a t ion COM+ Event s is an out st anding ser vice t hat saves you a lot of work —it provides an ext ensible, feat ur e- r ich service t hat allows you t o focus on adding value t o y our product , not on ev ent connect ivit y plum bing. However, t he event syst em has a few lim it at ions, and t his chapt er would not be com plet e wit hout point ing t hem out . Knowing about t hem allows y ou t o m ake t he best use of COM+ event s: •
•
•
• •
As y ou have seen, COM+ event s do not provide you wit h absolut e locat ion t ransparency . You have t o j um p t hrough hoops t o dist r ibut e your event s acr oss t he ent er pr ise. Good support for a ver y large num ber of subscriber s ( m ore t han a few hundr ed) is lack ing. To publish an ev ent , COM+ m aint ains a link ed list of subscribers, and it scans it on every event —i.e., publishing over head is linear t o a num ber of subscribers. There is no way t o perfor m a br oadcast . All part ies involved ( publisher , event class, and subscr ibers) hav e t o run on a Window s 2000 m achine. This is usually not a problem at t he m iddle t ier, but it does rule out m ost of t he por t able dev ices, such as lapt ops, PDAs, and cell phones. COM+ has difficult y handling a ver y large am ount of dat a as par am et ers for event s. Av oid lar ge st rings and huge arr ays. COM+ event s cannot handle a high rat e of ev ent publishing because it t akes t im e t o publish an ev ent . I f ev ent s ar e published fast er t han COM+ can handle t hem , you get m em ory bloat ing and COM+ will occasionally fail. On a st ress t est I conduct ed on COM+ event s, I had t hree publishers, each on it s own m achine, cr eat ing an event class proxy and firing ev er y 300 m illiseconds at one subscriber on a fourt h m achine. COM+ failed aft er a day and a half.
289
290
9 .1 2 Su m m a r y COM+ loosely coupled event s dem onst rat e all of t he cor e COM+ com ponent services principles discussed in t his book: t he service has ev olved t o im prove an exist ing solut ion; it offers a spect r um of feat ur es, fr om sim ple, t o adm inist rat iv e Com ponent Serv ices Explorer suppor t , t o advanced pr ogr am m at ic feat ur es; and it int er act s w it h alm ost all of t he ot her COM+ services, such as t r ansact ions, securit y , and queued com ponent s. Alt hough t his chapt er has discussed t he m ain point s of t he ser vice, num er ous ot her possibilit ies ex ist , including pooled persist ent subscr ibers. The im port ant lesson is t hat once you under st and how each individual serv ice work s, y ou can st ar t com bining t he serv ices in powerful and sy nerget ic ways. COM+ loosely coupled event s are t he last COM+ com ponent service described in t his book. You will now learn about .NET and see how it ut ilizes COM+ com ponent serv ices.
291
Cha pt e r 1 0 . .N ET Se r vice d Com pone nt s .NET is t he new plat form fr om Micr osoft used t o build com ponent based applicat ions, from st andalone deskt op applicat ions t o webbased applicat ions and serv ices. The plat form will be av ailable on fort hcom ing Micr osoft operat ing syst em s and support ed by t he next release of Visual St udio, called Visual St udio.NET. I n addit ion t o providing a m odern obj ect - or ient ed fram ew ork for building dist ribut ed applicat ions, .NET also provides sev er al specialized applicat ion fram eworks. These fram ework s include Windows For m s for r ich Windows client s, ADO.NET for dat a access, and ASP.NET for dynam ic web applicat ions. Anot her im port ant fr am ework is Web Serv ices, which is used t o expose and consum e rem ot e obj ect s using t he em erging SOAP and ot her XML- based prot ocols. .NET is Microsoft ’s next - gener at ion com ponent t echnology. I t is designed fr om t he ground up t o sim plify com ponent developm ent and deploym ent , as well as t o support int er operabilit y bet ween program m ing languages. Despit e it s innovat ions and m odern design, .NET is essent ially a com ponent t echnology. Like COM, .NET pr ovides you wit h t he m eans t o r apidly build binar y com ponent s, and Micr osoft int ends for .NET t o event ually succeed COM. Like COM, .NET does not provide it s own com ponent ser vices. I nst ead, .NET r elies on COM+ t o provide it wit h inst ance m anagem ent , t r ansact ions, act ivit y- based sy nchr onizat ion, gr anular role- based securit y , disconnect ed asynchronous queued com ponent s, and loosely coupled event s. The .NET nam espace t hat cont ains t he t ypes necessary t o use COM+ serv ices was nam ed Syst em .Ent er pr iseServices t o reflect t he piv ot al role it plays in building .NET ent er prise applicat ions. A .NET com ponent t hat uses COM+ serv ices is called a ser viced com ponent t o dist inguish it from t he st andard m anaged com ponent s in .NET. I f you ar e not fam iliar wit h .NET, y ou should first read Appendix C or pick up a copy of .NET Fr am ework Essent ials by Thuan Thai and Hoang Lam ( O’Reilly , 2001) . I f you ar e already fam iliar wit h t he basic .NET concept s, such as t he runt im e, assem blies, garbage collect ion, and C# ( pr onounced " C sharp" ) , cont inue r eading. This chapt er shows y ou how t o cr eat e .NET serviced com ponent s t hat can t ake advant age of t he COM+ com ponent services t hat y ou have learned t o apply t hr oughout t his book .
292
1 0 .1 D ev elopin g Ser vice d Com pon en t s A .NET com ponent t hat t ak es adv ant age of COM+ services needs t o der ive from t he .NET base class ServicedComponent. ServicedComponent is defined in t he System.EnterpriseServices nam espace. Ex am ple 10- 1 dem onst rat es how t o wr it e a .NET serv iced com ponent t hat im plem ent s t he IMessage int er face and displays a m essage box wit h " Hello" in it when t he int er face’s ShowMessage( ) m et hod is called. Ex a m p le 1 0 - 1 . A sim ple .N ET se r vice d com p on e n t
namespace MyNamespace { using System.EnterpriseServices; using System.Windows.Forms;//for the MessageBox class
public interface IMessage { void ShowMessage( ); } /// /// Plain vanilla .NET serviced component /// public class MyComponent:ServicedComponent,IMessage { public MyComponent( ) {}//constructor public void ShowMessage( ) { MessageBox.Show("Hello!","MyComponent"); } } } A serv iced com ponent is not allowed t o have param et erized const r uct ors. I f you require such param et ers, you can eit her design ar ound t hem by int r oducing a Create( ) m et hod t hat accept s param et ers, or use a const r uct or st ring. Ther e are t wo ways t o configur e a ser viced com ponent t o use COM+ serv ices. The first is COM- like: you deriv e from ServicedComponent, add t he com ponent t o a COM+ applicat ion, and configur e it t her e. The second way is t o apply special at t r ibut es t o t he com ponent , configuring it at t he sour ce- code level. When t he com ponent is added t o a COM+ applicat ion, it is configured according t o t he v alues of t hose at t r ibut es. At t r ibut es are discussed 293
in gr eat er det ail t hr oughout t his chapt er as you learn about configuring .NET com ponent s t o t ake adv ant age of t he various COM+ serv ices. .NET allows you t o apply at t ribut es t o your serviced com ponent s wit h great flex ibilit y . I f you do not apply your own at t r ibut es, a serv iced com ponent is configur ed using default COM+ set t ings when it is added t o a COM+ applicat ion. You can apply as m any at t ribut es as you lik e. A few COM+ ser vices can only be configured via t he Com ponent Services Explorer. These services ar e m ost ly deploy m ent - specific configur at ions, such as per sist ent subscript ions t o COM+ Event s and allocat ion of users t o roles. I n general, alm ost every t hing y ou can do wit h t he Com ponent Serv ices Explorer can be done wit h at t ribut es. I recom m end t hat you put as m any designlev el at t r ibut es as possible ( such as t r ansact ion support or sy nchr onizat ion) in t he code and use t he Com ponent Serv ices Explorer t o configure deploym ent - specific det ails.
1 0 .2 .N ET Asse m blies a n d COM+ Applica t ion s When y ou wish t o t ake advant age of COM+ com ponent ser vices, you m ust m ap t he assem bly cont aining y our serv iced com ponent s t o a COM+ applicat ion. That COM+ applicat ion t hen cont ains your serv iced com ponent s, j ust like any ot her com ponent —COM+ does not care whet her t he com ponent it provides serv ices t o is a m anaged .NET serviced com ponent or a classic COM, unm anaged, configured com ponent . A COM+ applicat ion can cont ain com ponent s fr om m ult iple assem blies, and an assem bly can cont ribut e com ponent s t o m or e t han one applicat ion, as shown in Figure 10- 1. Com pare Figure 10- 1 t o Figur e 1- 8. Ther e is an addit ional level of indirect ion in .NET because an assem bly can cont ain m ult iple m odules. Figu r e 1 0 - 1 . COM + a p plica t ion s a nd a sse m b lie s
However, set t ing up an assem bly t o cont ribut e com ponent s t o m ore t han one COM+ applicat ion is not st raight for war d and is suscept ible t o fut ur e r egist r at ions of t he assem bly. As a rule, avoid m apping an assem bly t o m ore t han one COM+ applicat ion.
294
1 0 .3 Re gist er in g Asse m blie s To add t he serviced com ponent s in your assem bly t o a COM+ applicat ion, you need t o regist er t hat assem bly wit h COM+ . You can per form t hat r egist r at ion in t hree way s: • • •
Manually , using a com m and line ut ilit y called RegSvcs.ex e. Dynam ically, by having t he client pr ogr am r egist er your assem bly aut om at ically . Program m at ically, by wr it ing code t hat does t he regist rat ion for you using a ut ilit y class pr ov ided by .NET.
Regardless of t he t echnique y ou use, t he regist rat ion pr ocess adds your ser viced com ponent s t o a COM+ applicat ion and configur es t hem accor ding t o t he default COM+ set t ings or accor ding to t heir at t r ibut es ( if present in t he code) . I f t he assem bly cont ains incom pat ible at t r ibut es, t he incom pat ibilit y is det ect ed during regist r at ion and t he regist rat ion is abor t ed. Fut ur e versions of t he .NET com piler s m ay det ect incom pat ibilit ies during com pilat ion t im e.
Signing Asse m bly a nd Asse m bly Loca t ion To add an assem bly t o a COM+ applicat ion, t he assem bly m ust be signed ( have a st rong nam e) so t he assem bly resolver can m ap a client act ivat ion request t o t he corr esponding assem bly . Alt hough in t heory you need not inst all t he assem bly in t he global assem bly cache ( GAC) , in pract ice you should inst all it because t he assem bly DLL m ust be in a known locat ion—eit her t he syst em direct ory ( for server applicat ions t hat r un in DllHost ) or t he host ing client process dir ect ory ( if t he client is not a COM+ server applicat ion) . The ot her known locat ion t hat t he assem bly resolver uses is t he GAC. To m aint ain flex ibilit y ( t o change fr om server t o library applicat ion) and consist ency, m ak e sure y ou alway s inst all your serviced com ponent assem bly in t he GAC. 1 0 .3 .1 Spe cif ying Applica t ion N a m e You can provide .NET wit h an assem bly at t ribut e, specifying t he nam e of t he COM+ applicat ion y ou would like your com ponent s t o be part of, by using t he ApplicationName assem bly at t r ibut e: [assembly: ApplicationName("MyApp")]
295
I f you do not prov ide an applicat ion nam e, .NET uses t he assem bly nam e. The ApplicationName at t ribut e ( and t he r est of t he serv iced com ponent s at t r ibut es) is defined in t he System.EnterpriseServices nam espace. You m ust add t his nam espace t o y our proj ect r eferences and reference t hat nam espace in your assem bly inform at ion file: using System.EnterpriseServices; 1 0 .3 .2 Un d e r st a ndin g Se r vice d Com pon e n t Ve r sions Befor e exploring t he t hree regist rat ion opt ions, you need t o underst and t he r elat ionship bet w een an assem bly ’s version and COM+ com ponent s. Every m anaged client of y our assem bly is built against t he par t icular version of t he assem bly t hat cont ains y our com ponent s, whet her t hey are serv iced or regular m anaged com ponent s. .NET zealously enforces version com pat ibilit y bet ween t he client ’s assem bly and any ot her assem bly it uses. The assem bly’s ver sion is t he product of it s ver sion num ber ( m aj or and m inor num bers, such as 3.11) and t he build and r evision num bers. The ver sion num ber is provided by t he developer as an assem bly at t r ibut e, and t he build or r evision num bers can be gener at ed by t he com piler—or t he dev eloper can pr ov ide t hem him self. The sem ant ics of t he ver sion and build or revision num bers t ell .NET whet her t w o par t icular assem bly versions are com pat ible wit h each ot her , and which of t he t wo assem blies is t he lat est . Assem blies are com pat ible if t he version num ber is t he sam e. The default is t hat differ ent build and revision num bers do not indicat e incom pat ibilit y , but a difference in eit her m aj or or m inor num ber indicat es incom pat ibilit y . A client 's m anifest cont ains t he version of each assem bly it uses. At runt im e, .NET loads for t he client t he lat est com pat ible assem blies t o use, and lat est is defined using t he build and r ev ision num bers. All t his is fine while everyt hing is under t ight cont rol of t he .NET runt im e. But how w ould .NET guarant ee com pat ibilit y bet ween t he assem bly's v er sion and t he configurat ion of t he ser viced com ponent s in t he COM+ Cat alog? The answer is via t he COM+ com ponent 's I D. The first t im e a serviced com ponent is added t o a COM+ applicat ion, t he regist rat ion pr ocess gener at es a CLSI D for it , based on a hash of t he class definit ion and it s assem bly 's version and st rong nam e. Subsequent r egist r at ion of t he sam e assem bly wit h an incom pat ible version is considered a new r egist r at ion for t hat serv iced com ponent , and t he com ponent is given a new CLSI D. This way, t he ser viced com ponent 's CLSI D serv es as it s configurat ion set t ings version num ber . Exist ing m anaged client s do not int er fere wit h one anot her because each get s t o use t he
296
assem bly v ersion it was com piled wit h. Each m anaged client also uses a part icular set of configur at ion par am et ers for t he serv iced com ponent s, capt ured wit h a differ ent CLSI D. When a m anaged client cr eat es a serviced com ponent , t he .NET runt im e creat es for it a com ponent fr om an assem bly wit h a com pat ible ver sion and applies t he COM+ configurat ion of t he m at ching CLSI D. 1 0 .3 .3 M a n u a l Re gist r a t ion To regist er your com ponent m anually, use t he RegSvcs.ex e com m and- line ut ilit y. ( I n t he fut ur e, Visual St udio.NET will pr obably allow you t o inv ok e RegSv cs fr om t he v isual env ir onm ent it self.) RegSvcs accept s as a par am et er t he nam e of t he file cont aining your assem bly ’s m et adat a. I n a single DLL assem bly, t hat file is sim ply t he assem bly file. I f you do not specify as an assem bly at t r ibut e t he nam e of t he COM+ applicat ion t hat should host y our com ponent s, RegSvcs m ust be t old t hat nam e ex plicit ly as a com m and- line param et er, using t he /appname: swit ch. For ex am ple, if y our single DLL assem bly resides in MyAssem bly .dll and y ou wish t o add t he serv iced com ponent s in t hat assem bly t o t he MyApp COM+ applicat ion, you w ould use RegSv cs in t his m anner: RegSvcs.exe /appname:MyApp MyAssembly.dll The com m and- line applicat ion nam e is ignored if t he assem bly cont ains an applicat ion nam e. I n any case, y ou m ust creat e t hat COM+ applicat ion in t he Com ponent Services Explorer beforehand; ot herwise, t he pr evious com m and line will fail. You can inst ruct RegSv cs t o cr eat e t he applicat ion for y ou using t he /c sw it ch: RegSvcs.exe /c MyApp MyAssembly.dll Or if t he nam e is specified in t he assem bly: RegSvcs.exe /c MyAssembly.dll When using t he /c swit ch, RegSv cs creat es a COM+ applicat ion, nam es it accordingly, and adds t he serv iced com ponent s t o it . I f t he Cat alog alr eady cont ains an applicat ion wit h t hat nam e, t he regist r at ion fails. You can also ask RegSvcs t o t r y t o find a COM+ applicat ion wit h t hat nam e and, if none is found, creat e one. This is done using t he /fc sw it ch: RegSvcs.exe /fc MyApp MyAssembly.dll Or if t he nam e is specified in t he assem bly: RegSvcs.exe /fc MyAssembly.dll I f you don’t specify a COM+ applicat ion nam e, eit her in t he assem bly or as a com m and- line param et er , RegSvcs uses t he assem bly nam e for t he applicat ion nam e. I f your assem bly is called My Assem bly, RegSvcs adds t he com ponent s t o t he MyAssem bly
297
COM+ applicat ion. This behavior is t he sam e for all t he com m andline swit ches. By default , RegSvcs does not ov erride t he exist ing COM+ applicat ion ( and it s com ponent s) set t ings. I f t hat assem bly version is alr eady regist ered wit h t hat COM+ applicat ion, t hen RegSvcs does not hing. I f t hat v er sion is not regist ered yet , it adds t he new ver sion and assigns new CLSI Ds. Reconfiguring an exist ing version is done ex plicit ly using t he /reconfig sw it ch: RegSvcs.exe /reconfig /fc MyApp MyAssembly.dll The /reconfig swit ch causes RegSvcs t o reapply any applicat ion, com ponent , int er face, and m et hod at t r ibut es found in t he assem bly t o t he exist ing version and use t he COM+ default set t ings for t he rest , t hus r ev er sing any changes you m ade using t he Com ponent Serv ices Explorer. When RegSvcs adds a serv iced com ponent t o t he COM+ Cat alog, it m ust give it a class- I D ( CLSI D) and a prog- I D. RegSvcs cr eat es a GUI D for ev ery com ponent ( based on t he assem bly’s v er sion and t he class definit ion) and nam es it .. For ex am ple, when you add t he serv iced com ponent in Exam ple 101 t o t he COM+ Cat alog, RegSvcs nam es it MyNamespace.MyComponent. You can also specify t he CLSI D and t he prog- I D of your serviced com ponent s using at t r ibut es. I n addit ion t o adding t he serv iced com ponent s in t he assem bly t o a COM+ applicat ion, RegSvcs creat es a t ype library . This libr ary cont ains int erface and CoClass definit ions t o be used by nonm anaged client s ( COM client s) . The default t y pe libr ary filenam e is < Assembly name> .t lb—t he nam e of t he assem bly wit h a .t lb ext ension. 1 0 .3 .4 D yn a m ic Re gist r a t ion When a m anaged client cr eat es a serv iced com ponent , t he .NET runt im e resolv es which assem bly v ersion t o use for t hat client . Nex t , t he r unt im e verifies t hat t he requir ed v er sion is regist ered wit h COM+ . I f it is not regist er ed, t he r unt im e inst alls it aut om at ically. This process is called dynam ic regist rat ion. As w it h RegSvcs, if t he assem bly cont ains an applicat ion nam e, t hen t hat nam e is used; if it does not , t hen t he assem bly 's nam e is used for t he COM+ applicat ion's nam e. Not e t hat only .NET client s can r ely on having dy nam ic r egist r at ion done when t hey inst ant iat e a .NET ser viced com ponent . For COM client s, you m ust use t he RegSvcs ut ilit y . Anot her lim it at ion of dynam ic r egist r at ion is t hat serviced com ponent s in t he assem bly are configured accor ding t o t he at t ribut es in t he assem bly and t he COM+ default s. I f you r equir e configuring som e services ( such as event s subscript ions) using t he Com ponent Serv ices Ex plorer for your applicat ion t o funct ion proper ly , y ou m ust use RegSvcs t o
298
regist er y our com ponent s and provide t he addit ional configurat ion using t he Com ponent Ser vices Explorer. Only t hen can client s use your ser viced com ponent s. As a result , dynam ic r egist rat ion is only useful for ser viced com ponent s t hat cont ain all t he ser vice configurat ions t hey need in t heir code t hrough t he use of at t r ibut es. Finally, dy nam ic regist rat ion requir es t hat t he user inv ok ing t he call t hat t riggers dy nam ic regist r at ion be a m em ber of t he Windows 2000 Adm inist rat or gr oup. I t has t his requir em ent because dynam ic regist r at ion m ak es changes t o t he COM+ Cat alog; if t he user inv oking it is not a m em ber of t he Windows 2000 Adm inist rat or group, dynam ic r egist r at ion w ill fail. I n gener al, y ou should use RegSv cs and t he Com ponent Serv ices Explorer r at her t han r ely ing on dy nam ic regist r at ion. I f you want t o rely on dynam ic r egist r at ion of your serviced com ponent s, you should incr em ent t he ver sion num ber of y our assem bly every t im e you m ake a change t o one of t he com ponent s’ at t ribut es, t o ensur e t hat y ou t rigger dy nam ic regist rat ion. 1 0 .3 .5 Pr ogr a m m a t ic Re gist r a t ion Bot h RegSvcs and dynam ic r egist r at ion use a .NET class called RegistrationHelper t o per form t he r egist r at ion. RegistrationHelper im plem ent s t he IRegistrationHelper int er face, whose m et hods are used t o regist er and unr egist er assem blies. For exam ple, t he InstallAssembly( ) m et hod regist ers t he specified assem bly in t he specified COM+ applicat ion ( or t he applicat ion specified in t he assem bly) . This m et hod is defined as: public void InstallAssembly(string assembly, ref string application, ref string tlb, InstallationFlags installFlags ); The inst allat ion flags cor respond t o t he v ar ious RegSvcs swit ches. See t he MSDN Libr ary for addit ional inform at ion on RegistrationHelper. You can use RegistrationHelper yourself as par t of y our inst allat ion program ; for m or e inform at ion, see Sect ion 10.14 lat er in t his chapt er . 1 0 .3 .6 Th e Applica t ion I D At t r ibu t e Every COM+ applicat ion has a GUI D ident ify ing it called t he applicat ion I D. You can provide an assem bly at t ribut e specifying t he applicat ion I D in addit ion t o t he applicat ion nam e: [assembly: ApplicationID("8BE192FA-57D0-49a0-86086829A314EEBE")] Unlik e t he applicat ion nam e, t he applicat ion I D is guar ant eed t o be unique, and you can use it alongside t he applicat ion nam e. Once an 299
applicat ion I D is specified, all searches for t he applicat ion dur ing regist r at ion ar e done using t he applicat ion I D only, and t he applicat ion nam e is only useful as a hum an- readable form of t he applicat ion ident it y. Using applicat ion I D com es in handy when deploy ing t he assem bly in foreign m ark et s—you can pr ovide a com m and- line localized applicat ion nam e for every m arket while using t he sam e applicat ion I D for your adm inist r at ion needs int er nally. The ApplicationID at t ribut e is defined in t he System.EnterpriseServices nam espace. 1 0 .3 .7 Th e Gu id At t r ibu t e I nst ead of hav ing t he regist rat ion pr ocess gener at e a CLSI D for your ser viced com ponent , you can specify one for it using t he Guid at t r ibut e: using System.Runtime.InteropServices; [Guid("260C9CC7-3B15-4155-BF9A-12CB4174A36E")] public class MyComponent :ServicedComponent,IMyInterface {...} The Guid at t ribut e is defined in t he System.Runtime.InteropServices nam espace. When y ou specify a class I D, subsequent regist rat ions of t he assem bly don't gener at e a new CLSI D for t he com ponent , regardless of t he ver sion of t he assem bly being regist er ed. Regist rat ions alway s reconfigur e t he sam e com ponent in t he COM+ Cat alog. Specify ing a class I D is useful during developm ent , when you have m ult iple cy cles of code- t est - fix. Wit hout it , ev ery inv ocat ion by t he t est client t r iggers a dynam ic regist rat ion—y ou ver y quickly clut t er t he COM+ applicat ion wit h dozens of com ponent s, when y ou act ually only use t he lat est one. 1 0 .3 .8 Th e Pr ogI d At t r ibu t e I nst ead of hav ing t he regist rat ion pr ocess gener at e a nam e for y our serv iced com ponent ( nam espace plus com ponent nam e) , you can specify one for it using t he ProgID at t ribut e: using System.Runtime.InteropServices; [ProgId("My Serviced Component")] public class MyComponent :ServicedComponent,IMyInterface {...} The ProgId at t r ibut e is defined in t he System.Runtime.InteropServices nam espace.
300
1 0 .4 Configu r ing Se r viced Com pon e n t s You can use various .NET at t r ibut es t o configur e your serv iced com ponent s t o t ake adv ant age of COM+ com ponent services. The rest of t his chapt er dem onst rat es t his serv ice by service, according t o t he order in which t he COM+ serv ices are present ed in t his book.
1 0 .5 Applica t ion Act iva t ion Type To specify t he COM+ applicat ion’s act iv at ion t y pe, you can use t he ApplicationActivation assem bly at t r ibut es. You can r equest t hat t he applicat ion be a library or a serv er applicat ion: [assembly: ApplicationActivation(ActivationOption.Server)] or: [assembly: ApplicationActivation(ActivationOption.Library)] I f you do not prov ide t he ApplicationActivation at t ribut e, t hen .NET uses a library act ivat ion t ype by default . Not e t hat t his use differ s fr om t he COM+ default of creat ing a new applicat ion as a server applicat ion. The next release of Windows 2000, Windows XP ( see Appendix B) , allows a COM+ applicat ion t o be act ivat ed as a syst em service, so I expect t hat ApplicationActivation will be ext ended t o include t he value of ActivationOption.Service. Befor e I describe ot her serviced com ponent s at t ribut es, you need t o underst and what at t r ibut es are. Ev er y .NET at t ribut e is act ually a class, and t he at t r ibut e class has a const r uct or ( m aybe ev en a few overloaded const r uct ors) and, usually , a few propert ies y ou can set . The sy nt ax for declar ing an at t ribut e is different from t hat of any ot her class. I n C# , you specify t he at t ribut e t y pe bet ween squar e bracket s [...]. You specify const r uct or param et ers and t he v alues of t he propert ies you wish t o set bet ween par ent heses (...). I n t he case of t he ApplicationActivation at t ribut e, t her e ar e no proper t ies and t he const ruct or m ust accept an enum param et er of t y pe ActivationOption, defined as: enum ActivationOption{Server,Library} Ther e is no default const ruct or for t he ApplicationActivation at t r ibut e. The ApplicationActivation at t r ibut e is defined in t he System.EnterpriseServices nam espace. Your m ust add t his nam espace t o y our proj ect r eferences and reference t hat nam espace in your assem bly inform at ion file:
301
using System.EnterpriseServices; The r est of t his chapt er assum es t hat y ou have added t hese references and will not m ent ion t hem again. A client assem bly t hat cr eat es a serv iced com ponent or uses any of it s base class ServicedComponent m et hods m ust add a reference t o System.EnterpriseServices t o it s proj ect . Ot her client s, which only use t he int erfaces prov ided by your ser viced com ponent s, need not add t he r efer ence.
1 0 .6 Th e D escr ipt ion At t r ibu t e The Description at t r ibut e allows you t o add t ex t t o t he descript ion field on t he General Pr opert ies t ab of an applicat ion, com ponent , int er face, or m et hod. Exam ple 10- 2 shows how t o apply t he Description at t ribut e at t he assem bly, class, int erface, and m et hod levels. Aft er r egist r at ion, t he assem bly- level descript ion st ring becom es t he cont ent of t he host ing COM+ applicat ion’s descript ion field; t he class descript ion st ring becom es t he cont ent of t he COM+ com ponent descript ion field. The int er face and m et hod descript ions are m apped t o t he cor responding int erface and m et hod in t he Com ponent Serv ices Ex plorer. Ex a m p le 1 0 - 2 . Ap plyin g t h e D e script ion a t t r ib u t e a t t h e asse m bly, cla ss, in t e rfa ce , a n d m et h od le ve ls
[assembly: Description("My Serviced Components Application")] [Description("IMyInterface description")] public interface IMyInterface { [Description("MyMethod description")] void MyMethod( ); } [Description("My Serviced Component description")] public class MyComponent :ServicedComponent,IMyInterface { public void MyMethod( ){} }
302
1 0 .7 Acce ssin g t h e COM + Con t e x t To access t he COM+ cont ext obj ect ’s int er faces and proper t ies, .NET prov ides you w it h t he helper class ContextUtil. All cont ex t obj ect int erfaces ( including t he legacy MTS int erfaces) ar e im plem ent ed as public st at ic m et hods and public st at ic proper t ies of t he ContextUtil class. Because t he m et hods and proper t ies are st at ic, you do not hav e t o inst ant iat e a ContextUtil obj ect —you should j ust call t he m et hods. For exam ple, if y ou want t o t r ace t he current COM+ cont ex t I D ( it s GUI D) t o t he Out put window, use t he ContextId st at ic proper t y of ContextUtil: using System.Diagnostics;//For the Trace class Guid contextID = ContextUtil.ContextId; String traceMessage = "Context ID is " + contextID.ToString( ); Trace.WriteLine(traceMessage); ContextUtil has also pr opert ies used for JI TA deact iv at ion, t r ansact ion vot ing, obt aining t he t ransact ions and act ivit y I Ds, and obt aining t he curr ent t r ansact ion obj ect . You will see exam ples for how t o use t hese ContextUtil pr opert ies lat er in t his chapt er.
1 0 .8 COM+ Cont ex t At t r ibu t es You can decorat e ( apply at t ribut es t o) your class wit h t wo cont ex t relat ed at t ribut es. The at t r ibut e MustRunInClientContext infor m s COM+ t hat t he class m ust be act iv at ed in it s creat or's cont ext : [MustRunInClientContext(true)] public class MyComponent :ServicedComponent {...} When y ou r egist er t he class abov e wit h COM+ , t he " Must be act ivat ed in caller's cont ext " checkbox on t he com ponent 's Act iv at ion t ab is select ed in t he Com ponent Ser vices Explor er. I f you do not use t his at t ribut e, t he r egist r at ion process uses t he default COM+ set t ing when regist er ing t he com ponent wit h COM+ —not enforcing sam e- cont ext act iv at ion. As a r esult , using MustRunInClientContext wit h a false par am et er passed t o t he const ruct or is t he sam e as using t he COM+ default : [MustRunInClientContext(false)] Using at t r ibut es w it h t he COM+ default values ( such as const ruct ing t he MustRunInClientContext at t ribut e wit h false) is useful w hen you com bine it wit h t he /reconfig swit ch of RegSv cs. For exam ple, you can undo any unknown changes m ade t o your com ponent configurat ion using t he Com ponent Ser vices Explor er and rest ore t he com ponent configur at ion t o a known st at e.
303
The MustRunInClientContext at t ribut e class has an ov er loaded default const ruct or . I f you use MustRunInClientContext w it h no par am et ers, t he default const ruct or uses true for t he at t r ibut e value. As a result , t he following t wo st at em ent s are equiv alent : [MustRunInClientContext] [MustRunInClientContext(true)] The second COM+ cont ext - relat ed at t ribut e is t he EventTrackingEnabled at t r ibut e. I t inform s COM+ t hat t he com ponent support s event s and st at ist ics collect ion during it s execut ion: [EventTrackingEnabled(true)] public class MyComponent2:ServicedComponent {...} The st at ist ics ar e display ed in t he Com ponent Services Explorer . When y ou r egist er t his class wit h COM+ , t he " Com ponent support s event s and st at ist ics" check box on t he com ponent ’s Act ivat ion t ab is checked in t he Com ponent Ser vices Explor er. I f y ou do not use t his at t r ibut e, t he r egist r at ion process does not use t he default COM+ set t ing of support ing ev ent s when r egist ering t he com ponent wit h COM+ . The .NET designers m ade t his decision consciously t o m inim ize cr eat ion of new COM+ cont ext s for new .NET com ponent s; a com ponent t hat support s st at ist ics is usually placed in it own cont ext . The EventTrackingEnabled at t ribut e class also has an ov er loaded default const ruct or . I f you const r uct it w it h no par am et ers, t he default const ruct or uses true for t he at t ribut e value. As a r esult , t he following t wo st at em ent s ar e equivalent : [EventTrackingEnabled] [EventTrackingEnabled(true)]
1 0 .9 COM+ Obj e ct Poolin g The ObjectPooling at t r ibut e is used t o configur e every aspect of your com ponent ’s obj ect pooling. The ObjectPooling at t r ibut e enables or disables obj ect pooling and set s t he m inim um or m ax im um pool size and obj ect creat ion t im eout . For exam ple, t o enable obj ect pooling of your com ponent ’s obj ect s wit h a m inim um pool size of 3, a m axim um pool size of 10, and a creat ion t im eout of 20 m illiseconds, you would wr it e: [ObjectPooling(MinPoolSize = 3,MaxPoolSize = 10,CreationTimeout = 20)] public class MyComponent :ServicedComponent {...} The MinPoolSize, MaxPoolSize, and CreationTimeout pr opert ies are public pr opert ies of t he ObjectPooling at t r ibut e class. I f you do not specify values for t hese propert ies ( all or j ust a subset ) when
304
your com ponent is r egist ered, t he default COM+ values are used for t hese pr opert ies ( a m inim um pool size of 0, a m axim um pool size of 1,048,576, and a creat ion t im eout of 60 seconds) . The ObjectPooling at t r ibut e has a Boolean proper t y called t he Enabled pr opert y. I f y ou do not specify a v alue for it ( true or false) , t he at t r ibut e’s const ruct or set s it t o true. I n fact , t he at t r ibut e’s const ruct or has a few overloaded versions—a default const ruct or t hat set s t he Enabled proper t y t o true and a const ruct or t hat accept s a Boolean par am et er. All const r uct ors set t he pool param et ers t o t he default COM+ v alue. As a result , t he following t hree st at em ent s ar e equiv alent : [ObjectPooling] [ObjectPooling(true)] [ObjectPooling(Enabled = true)] I f your pooled com ponent is host ed in a librar y applicat ion, t hen each host ing Applicat ion Dom ain will have it s own pool. As a result , you m ay have m ult iple pools in a single physical process, if t hat process host s m ult iple Applicat ion Dom ains. Under COM, t he pooled obj ect ret ur ns t o t he pool when t he client releases it s r eference t o it . Managed obj ect s do not have r eference count ing—.NET uses gar bage collect ion inst ead. A m anaged pooled obj ect ret urns t o t he pool only when it is garbage collect ed. The problem wit h t his behav ior is t hat a subst ant ial delay bet ween t he t im e t he obj ect is no longer needed by it s client and t he t im e t he obj ect ret urns t o t he pool can occur. This delay m ay have serious adverse effect s on your applicat ion scalabilit y and t hroughput . An obj ect is pooled because it was expensiv e t o creat e. I f t he obj ect spends a subst ant ial por t ion of it s t im e wait ing for t he gar bage collect or, your applicat ion benefit s lit t le fr om obj ect pooling. Ther e are t wo ways t o address t his problem . The fir st solut ion uses COM+ JI TA ( discussed nex t ) . When you use JI TA, t he pooled obj ect ret ur ns t o t he pool aft er every m et hod call fr om t he client . The second solut ion requir es client par t icipat ion. Serv icedCom ponent has a public st at ic m et hod called DisposeObject( ), defined as: public static void DisposeObject(ServicedComponent sc); When t he client calls DisposeObject( ), passing in an inst ance of a pooled serv iced com ponent , t he obj ect r et urns t o t he pool im m ediat ely . DisposeObject( ) has t he effect of not ifying COM+ t hat t he obj ect has been r eleased. Besides ret ur ning t he obj ect t o t he pool, DisposeObject( ) disposes of t he cont ext obj ect host ing t he pooled obj ect and of t he pr oxy t he client used. For ex am ple, if t he com ponent definit ion is: public interface IMyInterface {
305
void MyMethod(
);
} [ObjectPooling] public class MyComponent : ServicedComponent,IMyInterface { public void MyMethod( ){} } When t he client is done using t he obj ect , t o expedit e ret ur ning t he obj ect t o t he pool, t he client should call DisposeObject( ): IMyInterface obj; Obj = (IMyInterface) new MyComponent( ); obj.MyMethod( ); ServicedComponent sc = obj as ServicedComponent; If(sc != null) ServicedComponent.DisposeObject(sc); However, calling DisposeObject( ) dir ect ly is ugly . First , t he client has t o know t hat it is dealing wit h an obj ect der ived from ServicedComponent, which couples t he client t o t he t y pe used and render s m any benefit s of int er face- based program m ing useless. Even wor se, t he client only has t o call DisposeObject( ) if t his obj ect is pooled, which couples t he client t o t he serv iced com ponent ’s configur at ion. What if you use obj ect pooling in only one cust om er sit e, but not in ot her s? This sit uat ion is a ser ious breach of encapsulat ion—t he cor e pr inciple of obj ect - or ient ed program m ing. The solut ion is t o hav e ServicedComponent im plem ent a special int er face ( defined in t he System nam espace) called IDisposable, defined as: public interface IDisposable { void Dispose( ); } ServicedComponent im plem ent at ion of Dispose( ) ret urns t he pooled obj ect t o t he pool. Hav ing t he Dispose( ) m et hod on a separ at e int erface allows t he client t o query for t he presence of IDisposable and always call it , regardless of t he obj ect 's act ual t ype: IMyInterface obj; obj = (IMyInterface) new MyComponent( ); obj.MyMethod( ); //Client wants to expedite whatever needs expediting: IDisposable disposable = obj as IDisposable; if(disposable != null) disposable.Dispose( ); The IDisposable t echnique is useful not only w it h serv iced com ponent s, but also in num erous ot her places in .NET. Whenever
306
your com ponent r equires det er m inist ic disposal of t he r esources and m em ory it holds, IDisposable prov ides a t ype- safe, com ponent orient ed w ay of having t he client dispose of t he obj ect wit hout being t oo coupled t o it s t ype.
1 0 .1 0 COM + Just - in - Tim e Act iva t ion .NET m anaged com ponent s can use COM+ JI TA t o efficient ly handle rich client s ( such as .NET Windows For m s client s) , as discussed in Chapt er 3. To enable JI TA support for y our com ponent , use t he JustInTimeActivation at t r ibut e: [JustInTimeActivation(true)] public class MyComponent :ServicedComponent {..} When y ou r egist er t his com ponent wit h COM+ , t he JI TA checkbox in t he Act iv at ion t ab on t he Com ponent Services Explorer is select ed. I f you do not use t he JustInTimeActivation at t r ibut e, JI TA suppor t is disabled when y ou r egist er your com ponent wit h COM+ ( unlike t he COM+ default of enabling JI TA) . The JustInTimeActivation class default const ruct or enables JI TA suppor t , so t he following t wo st at em ent s ar e equiv alent : [JustInTimeActivation] [JustInTimeActivation (true)] Enabling JI TA suppor t is j ust one t hing y ou need t o do t o use JI TA. You st ill hav e t o let COM+ k now when t o deact ivat e your obj ect . You can deact ivat e t he obj ect by set t ing t he done bit in t he cont ex t obj ect , using t he DeactivateOnReturn proper t y of t he ContextUtil class. As discussed at lengt h in Chapt er 3, a JI TA obj ect should ret rieve it s st at e at t he beginning of every m et hod call and sav e it at t he end. Ex am ple 10- 3 show s a serv iced com ponent using JI TA. Ex a m p le 1 0 - 3 . A ser v ice d com pon e n t u sin g JI TA
public interface IMyInterface { void MyMethod(long objectIdentifier); } [JustInTimeActivation(true)] public class MyComponent :ServicedComponent,IMyInterface { public void MyMethod(long objectIdentifier) { GetState(objectIdentifier); DoWork( ); SaveState(objectIdentifier);
307
//inform COM+ to deactivate the object upon method return ContextUtil.DeactivateOnReturn = true; } //other methods protected void GetState(long objectIdentifier){...} protected void DoWork( ){...} protected void SaveState(long objectIdentifier){...} } You can also use t he Com ponent Serv ices Ex plorer t o configure t he m et hod t o use aut o- deact ivat ion. I n t hat case, t he obj ect is deact iv at ed aut om at ically upon m et hod ret urn, unless you set t he value of t he DeactivateOnReturn proper t y t o false. 1 0 .1 0 .1 Usin g I Ob j e ct Con t r ol I f your ser viced com ponent uses obj ect pooling or JI TA ( or bot h) , it m ay also need t o know when it is placed in a COM+ cont ext t o do cont ext - specific init ializat ion and cleanup. Like a COM+ configur ed com ponent , t he ser viced com ponent can use IObjectControl for t hat purpose. The .NET base class ServicedComponent already im plem ent s IObjectControl, and it s im plem ent at ion is virt ual—so you can overr ide t he im plem ent at ion in y our serv iced com ponent , as shown in Exam ple 10- 4. Ex a m p le 1 0 - 4 . A ser v ice d com pon e n t over r iding t h e Se r viced Com pon e n t im ple m en t at ion of I Ob j ect Con t r ol
public class MyComponent :ServicedComponent { public override void Activate( ) { //Do context specific initialization here } public override void Deactivate( ) { //Do context specific cleanup here } public override bool CanBePooled( ) { return true; } //other methods } I f you encount er an er ror dur ing Activate( ) and t hr ow an except ion, t hen t he obj ect 's act ivat ion fails and t he client is given an oppor t unit y t o cat ch t he ex cept ion. 1 0 .1 0 .2 I Obj e ct Con t r ol, JI TA, a n d D e t e r m in ist ic Fin a liza t ion
308
To m aint ain JI TA sem ant ics, when t he obj ect deact iv at es it self, .NET calls DisposeObject( ) on it ex plicit ly, t hus dest r oying it . Your obj ect can do specific cleanup in t he Finalize( ) m et hod ( t he dest ruct or in C# ) , and Finalize( ) will be called as soon as t he obj ect deact ivat es it self, wit hout wait ing for garbage collect ion. I f t he obj ect is a pooled obj ect ( as well as a JI TA obj ect ) , t hen it is ret ur ned t o t he pool aft er deact iv at ion, wit hout wait ing for t he garbage collect ion. You can also overr ide t he ServicedComponent im plem ent at ion of IObjectControl.Deactivate( ) and perform y our cleanup t her e. I n any case, y ou end up wit h a det erm inist ic way t o dispose of cr it ical resources w it hout ex plicit client part icipat ions. This sit uat ion m ak es shar ing y our obj ect am ong client s m uch easier because now t he client s do not hav e t o coordinat e who is responsible for calling Dispose( ). COM+ JI TA gives m anaged com ponent s det erm inist ic finalizat ion, a serv ice t hat not hing else in .NET can provide out of t he box .
1 0 .1 1 COM + Con st r u ct or St r in g Any COM+ configur ed com ponent t hat im plem ent s t he IObjectConstruct int er face has access dur ing const r uct ion t o a const ruct ion st ring ( discussed in Chapt er 3) , configured in t he Com ponent Services Explorer. Serviced com ponent s are no differ ent . The base class, ServicedComponent, already im plem ent s t he IObjectConstruct int erface as a virt ual m et hod ( it has only one m et hod) . Your der ived serviced com ponent can ov err ide t he Construct( ) m et hod, as shown in t his code sam ple: public class MyComponent :ServicedComponent { public override void Construct(string constructString) { //use the string. For example: MessageBox.Show(constructString); } } I f t he check box " Enable obj ect const ruct ion" on t he com ponent Act iv at ion t ab is select ed, t hen t he Construct( ) m et hod is called aft er t he com ponent ’s const ruct or , providing it wit h t he configured const ruct ion st ring. You can also enable const r uct ion st ring support and provide a default const ruct ion st ring using t he ConstructionEnabled at t r ibut e:
309
[ConstructionEnabled(Enabled = true,Default = "My String")] public class MyComponent :ServicedComponent { public override void Construct(string constructString) {...} } The ConstructionEnabled at t ribut e has t wo public pr opert ies. Enabled enables const ruct ion st r ing suppor t for your serviced com ponent in t he Com ponent Services Explorer ( once t he com ponent is regist er ed) and Default pr ov ides an init ial st ring value. When your com ponent is regist er ed wit h COM+ , t he regist r at ion process assigns t he default st r ing t o t he const r uct or st ring field on t he com ponent Act iv at ion t ab. The default st ring has no fur t her use aft er r egist r at ion. New inst ances of your com ponent receive as a const r uct or st ring t he curr ent value of t he const ruct or st ring field. For exam ple, if t he default st r ing is St r ing A, when t he serv iced com ponent is regist er ed, t he value of t he const r uct or st ring field is set t o St r ing A. I f y ou set it t o a different v alue, such as St r ing B, new inst ances of t he com ponent get St ring B as t heir const ruct ion st ring. They receive t he curr ent value, not t he default value. The ConstructionEnabled at t ribut e has t wo ov er loaded const ruct ors. One const ruct or accept s a Boolean value for t he Enabled pr opert y; the default const ruct or set s t he value of t he Enabled pr opert y t o true. You can also set t he value of t he Enabled pr opert y explicit ly . As a result , t he following t hr ee st at em ent s ar e equivalent : [ConstructionEnabled] [ConstructionEnabled(true)] [ConstructionEnabled(Enabled = true)]
1 0 .1 2 COM + Tr a n sa ct ions You can configure your serviced com ponent t o use t he five av ailable COM+ t r ansact ion support opt ions by using t he Transaction at t r ibut e. The Transaction at t ribut e’s const r uct or accept s an enum par am et er of t ype TransactionOption, defined as: public enum TransactionOption { Disabled, NotSupported, Supported, Required, RequiresNew }
310
For ex am ple, t o configure your serviced com ponent t o r equire a t r ansact ion, use t he TransactionOption.Required v alue: [Transaction(TransactionOption.Required)] public class MyComponent :ServicedComponent {...} The five enum v alues of TransactionOption m ap t o t he five COM+ t r ansact ion support opt ions discussed in Chapt er 4. When y ou use t he Transaction at t ribut e t o m ar k your serviced com ponent t o use t ransact ions, y ou im plicit ly set it t o use JI TA and requir e act iv it y- based sy nchr onizat ion as well. The Transaction at t r ibut e has an overloaded default const ruct or, which set s t he t ransact ion suppor t t o TransactionOption.Required. As a result , t he following t w o st at em ent s ar e equiv alent : [Transaction] [Transaction(TransactionOption.Required)] 1 0 .1 2 .1 Vot in g on t he Tr a n sa ct ion Not sur prisingly, you use t he ContextUtil class t o vot e on t he t r ansact ion’s out com e. ContextUtil has a st at ic propert y of t he enum t y pe TransactionVote called MyTransactionVote. TransactionVote is defined as: public enum TransactionVote {Abort,Commit} Exam ple 10- 5 shows a t ransact ional ser viced com ponent vot ing on it s t ransact ion out com e using ContextUtil. Not e t hat t he com ponent st ill has t o do all t he r ight t hings t hat a well- designed t r ansact ional com ponent has t o do ( see Chapt er 4) ; it needs t o ret rieve it s st at e from a resource m anager at t he beginning of t he call and sav e it at t he end. I t m ust also deact iv at e it self at t he end of t he m et hod t o pur ge it s st at e and m ake t he vot e t ake effect . Ex a m p le 1 0 - 5 . A t r a n sa ct ion a l se rvice d com p on en t vot in g on it s t r an sa ct ion ou t com e u sing t h e Con t e x t Ut il M yTr a n sa ct ion V ot e prope rt y
public interface IMyInterface { void MyMethod(long objectIdentifier); } [Transaction] public class MyComponent :ServicedComponent,IMyInterface { public void MyMethod(long objectIdentifier) { try { GetState(objectIdentifier);
311
DoWork( ); SaveState(objectIdentifier); ContextUtil.MyTransactionVote = TransactionVote.Commit; } catch { ContextUtil.MyTransactionVote = TransactionVote.Abort; } //Let COM+ deactivate the object once the method returns finally { ContextUtil.DeactivateOnReturn = true; } } //helper methods protected void GetState(long objectIdentifier){...} protected void DoWork( ){...} protected void SaveState(long objectIdentifier){...} } Com pare Ex am ple 10- 5 t o Ex am ple 4- 3. A COM+ configured com ponent uses t he r et urned HRESULT from t he DoWork( ) helper m et hod t o decide on t he t r ansact ion’s out com e. A serv iced com ponent , lik e any ot her m anaged com ponent , does not use HRESULT r et urn codes for error handling; it uses except ions inst ead. I n Ex am ple 10- 5 t he com ponent cat ches any except ion t hat was t hr own in t he try block by t he DoWork( ) m et hod and vot es t o abor t in t he catch block. Alt ernat ively, if y ou do not want t o wr it e ex cept ion- handling code, you can use t he program m ing m odel shown in Ex am ple 10- 6. Set t he cont ex t obj ect ’s consist ency bit t o false ( vote t o abort ) as t he first t hing t he m et hod does. Then set it back t o true as t he last t hing t he m et hod does ( v ot e t o com m it ) . Any ex cept ion t hrown in bet ween causes t he m et hod except ion t o end wit hout v ot ing t o com m it . Ex a m p le 1 0 - 6 . V ot in g on t h e t r a n sa ct ion w it h ou t ex ce pt ion h a nd lin g
public interface IMyInterface { void MyMethod(long objectIdentifier); } [Transaction] public class MyComponent :ServicedComponent,IMyInterface { public void MyMethod(long objectIdentifier)
312
{ //Let COM+ deactivate the object once the method returns and abort the //transaction. You can use ContextUtil.SetAbort( ) as well ContextUtil.DeactivateOnReturn = true; ContextUtil.MyTransactionVote = TransactionVote.Abort; GetState(objectIdentifier); DoWork( ); SaveState(objectIdentifier); ContextUtil.MyTransactionVote = TransactionVote.Commit; } //helper methods protected void GetState(long objectIdentifier){...} protected void DoWork( ){...} protected void SaveState(long objectIdentifier){...} } Exam ple 10- 6 has anot her advant age over Ex am ple 10- 5: having t he ex cept ion propagat ed up t he call chain once t he t ransact ion is abor t ed. By pr opagat ing it , caller s up t he chain know t hat t hey can also abor t t heir work and avoid wast ing m or e t im e on a doom ed t r ansact ion. 1 0 .1 2 .2 Th e Au t oCom ple t e At t r ibu t e Your serv iced com ponent s can t ak e advant age of COM+ m et hod aut o- deact iv at ion using t he AutoComplete m et hod at t ribut e. Dur ing t he regist rat ion pr ocess, t he m et hod is configur ed t o use COM+ aut o- deact iv at ion when AutoComplete is used on a m et hod, and t he checkbox " Aut om at ically deact ivat e t his obj ect when t he m et hod ret ur ns" on t he m et hod’s General t ab is select ed. Serv iced com ponent s t hat use t he AutoComplete at t r ibut e do not need t o vot e explicit ly on t heir t ransact ion out com e. Ex am ple 10- 7 shows a t r ansact ional serv iced com ponent using t he AutoComplete m et hod at t r ibut e. Ex a m p le 1 0 - 7 . Usin g t h e Au t oCom p le t e m e t h od a t t ribu t e
public interface IMyInterface { void MyMethod(long objectIdentifier); } [Transaction] public class MyComponent : ServicedComponent,IMyInterface
313
{ [AutoComplete(true)] public void MyMethod(long objectIdentifier) { GetState(objectIdentifier); DoWork( ); SaveState(objectIdentifier); } //helper methods protected void GetState(long objectIdentifier){...} protected void DoWork( ){...} protected void SaveState(long objectIdentifier){...} } When y ou configur e t he m et hod t o use aut o- deact iv at ion, t he obj ect ’s int ercept or set s t he done and consist ency bit s of t he cont ext obj ect t o true if t he m et hod did not t hrow an ex cept ion and t he consist ency bit t o false if it did. As a r esult , t he t ransact ion is com m it t ed if no ex cept ion is t hrown and abor t ed ot herwise. Nont r ansact ional JI TA obj ect s can also use t he AutoComplete at t r ibut e t o deact iv at e t hem selv es aut om at ically on m et hod ret ur n. The AutoComplete at t ribut e has an ov erloaded default const r uct or t hat uses true for t he at t r ibut e const ruct ion. Consequent ly , t he following t wo st at em ent s ar e equiv alent : [AutoComplete] [AutoComplete(true)] The AutoComplete at t ribut e can be applied on a m et hod as par t of an int erface definit ion: public interface IMyInterface { //Avoid this: [AutoComplete] void MyMethod(long objectIdentifier); } However, you should avoid using t he at t r ibut e t his w ay. An int er face and it s m et hods declarat ions serve as a cont ract bet w een a client and an obj ect ; using aut o com plet ion of m et hods is pur ely an im plem ent at ion decision. For exam ple, one im plem ent at ion of t he int er face on one com ponent m ay chose t o use aut ocom plet e and anot her im plem ent at ion on anot her com ponent m ay choose not t o. 1 0 .1 2 .3 Th e Tr a n sa ct ion Con t e x t Obj e ct A nont ransact ional m anaged client creat ing a few t ransact ional obj ect s faces a problem discussed in Chapt er 4 ( see Sect ion 4.9) . Essent ially , if t he client want s t o scope all it s int er act ions w it h t he obj ect s it creat es under one t r ansact ion, it m ust use a m iddlem an t o cr eat e t he obj ect s for it . Ot herwise, each obj ect creat ed will be in
314
it s own separat e t r ansact ion. COM+ provides a ready- m ade m iddlem an called TransactionContext. Managed client s can use TransactionContext as well. To use t he TransactionContext obj ect , add t o t he proj ect r eferences t he COM+ services t ype librar y. The TransactionContext class is in t he COMSVCSLib nam espace. The TransactionContext class is especially useful in sit uat ions in which t he class is a m anaged .NET com ponent t hat deriv es from a class ot her t han ServicedComponent. Rem em ber t hat a .NET com ponent can only der ive from one concret e class and since t he class already derives fr om a concret e class ot her t han ServicedComponent, it cannot use t he Transaction at t ribut e. Nev er t heless, t he TransactionContext class giv es t his client an abilit y t o init iat e and m anage a t ransact ion. Exam ple 10- 8 dem onst r at es usage of t he TransactionContext class, using t he sam e use- case as Exam ple 4- 6. Ex a m p le 1 0 - 8 . A n on t ra n sa ct ion a l m a n a ge d clien t u sin g t h e Tr an sa ct ion Con t ex t h e lp e r class t o cr e at e ot h er t ra n sact ion a l ob j ect s
using COMSVCSLib; IMyInterface obj1,obj2,obj3; ITransactionContext transContext; transContext = (ITransactionContext) new TransactionContext( ); obj1 = (IMyInterface)transContext.CreateInstance("MyNamespace.My Component"); obj2 = (IMyInterface)transContext.CreateInstance("MyNamespace.My Component"); obj3 = (IMyInterface)transContext.CreateInstance("MyNamespace.My Component"); try { obj1.MyMethod( ); obj2.MyMethod( ); obj3.MyMethod( ); transContext.Commit(
);
} catch//Any error - abort the transaction { transContext.Abort( ); }
315
Not e t hat t he client in Exam ple 10- 8 decides whet her t o abort or com m it t he t ransact ion depending on whet her an except ion is t hr own by t he int ernal obj ect s. 1 0 .1 2 .4 COM + Tr a n sa ct ions a n d N on se r vice d Com pon e nt s Though t his chapt er focuses on ser viced com ponent s, it is w or t h not ing t hat COM+ t r ansact ions are used by ot her par t s of t he .NET fr am ework besides serv iced com ponent s—in part icular , ASP.NET and Web Ser vices. 1 0 .1 2 .4 .1 W e b se rvice s a n d t ra n sact ion s
Web services ar e t he m ost ex cit ing piece of t echnology in t he ent ire .NET fr am ework . Web services allow a m iddle- t ier com ponent in one web sit e t o invoke m et hods on anot her m iddle- t ier com ponent at anot her web sit e, wit h t he sam e ease as if t hat com ponent were in it s own assem bly . The under lying t echnology facilit at ing web serv ices ser ializes t he calls int o t ext form at and t ranspor t s t he call fr om t he client t o t he web service provider using HTTP. Because web service calls ar e t ext based, t hey can be m ade across fir ewalls. Web services t y pically use a prot ocol called Sim ple Obj ect Access Prot ocol ( SOAP) t o represent t he call, alt hough ot her t ex t - based prot ocols such as HTTP- POST and HTTP- GET can also be used. .NET successfully hides t he requir ed det ails from t he client and t he server dev eloper ; a web ser vice dev eloper only needs t o use t he WebMethod at t ribut e on t he public m et hods exposed as web serv ices. Ex am ple 10- 9 shows t he MyWebService web serv ice t hat provides t he MyMessage w eb ser vice—it ret urns t he st ring " Hello" t o t he caller . Ex a m p le 1 0 - 9 . A t r ivia l w eb se rvice t h at r et u rn s t h e st ring " H ello"
using System.Web.Services; public class MyWebService : WebService { public MyWebService( ){} [WebMethod] public string MyMessage( ) { return "Hello"; } } The web service class can opt ionally deriv e from t he WebService base class, defined in t he System.Web.Services nam espace ( see Exam ple 10- 9) . The WebService base class prov ides you w it h easy access t o com m on ASP.NET obj ect s, such as t hose r epr esent ing
316
applicat ion and session st at es. Your web service probably accesses resource m anager s and t ransact ional com ponent s. The pr oblem wit h adding t ransact ion support t o a web service t hat der ived fr om WebService is t hat it is not derived from ServicedComponent, and .NET does not allow m ult iple inher it ance of im plem ent at ion. To ov er com e t his hurdle, t he WebMethod at t r ibut e has a public proper t y called TransactionOption, of t he enum t ype Enterprise.Services.TransactionOption discussed prev iously. The default const r uct or of t he WebMethod at t ribut e set s t his proper t y t o TransactionOption.Disabled, so t he following t wo st at em ent s ar e equiv alent : [WebMethod] [WebMethod(TransactionOption = TransactionOption.Disabled)] I f your web service requir es a t ransact ion, it can only be t he r oot of a t r ansact ion, due t o t he st at eless nat ure of t he HTTP prot ocol. Even if you configure your web m et hod t o only requir e a t r ansact ion and it is called from wit hin t he cont ext of an exist ing t ransact ion, a new t ransact ion is cr eat ed for it . Sim ilar ly , t he value of TransactionOption.Supported does not cause a web service t o j oin an ex ist ing t ransact ion ( if called fr om wit hin one) . Consequent ly , t he following st at em ent s ar e equiv alent —all four am ount t o no t r ansact ion support for t he web service: [WebMethod] [WebMethod(TransactionOption = TransactionOption.Disabled)] [WebMethod(TransactionOption = TransactionOption.NotSupported)] [WebMethod(TransactionOption = TransactionOption.Supported)] Moreover, t he following st at em ent s ar e also equivalent —cr eat ing a new t ransact ion for t he web service: [WebMethod(TransactionOption = TransactionOption.Required)] [WebMethod(TransactionOption = TransactionOption.RequiresNew)] The v ar ious v alues of TransactionOption ar e confusing. To avoid m ak ing t hem t he sour ce of er rors and m isunderst andings, use TransactionOption.RequiresNew when you want t ransact ion suppor t for your web m et hod; use TransactionOption.Disabled when y ou want t o explicit ly dem onst rat e t o a reader of your code t hat t he w eb service does not t ake par t in a t ransact ion. The quest ion is, why did Micr osoft provide four over lapping t r ansact ion m odes for web serv ices? I believ e t hat it is not t he result of carelessness, but r at her a conscious design decision. Microsoft is probably laying down t he foundat ion in .NET for a point in t he fut ur e when it will be possible t o propagat e t r ansact ions acr oss web sit es.
317
Finally, you do not need t o explicit ly vot e on a t ransact ion from wit hin a web ser vice. I f an except ion occurs wit hin a web serv ice m et hod, t he t r ansact ion is aut om at ically abor t ed. Conversely, if no except ions occur, t he t ransact ion is com m it t ed aut om at ically ( as if you used t he AutoComplete at t ribut e) . Of cour se, t he web serv ice can st ill use ContextUtil t o vot e explicit ly t o abort inst ead of t hr owing an except ion, or when no ex cept ion occur red and y ou st ill want t o abort . 1 0 .1 2 .4 .2 ASP.N ET a n d t ra n sact ion s
An ASP.NET w eb form m ay access resour ce m anagers ( such as dat abases) direct ly, and it should do so under t he prot ect ion of a t r ansact ion. The page m ay also want t o cr eat e a few t ransact ional com ponent s and com pose t heir work int o a single t ransact ion. The problem again is t hat a web for m der ives from t he System.Web.UI.Page base class, not from ServicedComponent, and t herefore cannot use t he [Transaction] at t ribut e. To pr ov ide t r ansact ion support for a web for m , t he Page base class has a wr it e- only proper ty called TransactionMode of t ype TransactionOption. You can assign a v alue of t ype TransactionOption t o TransactionMode, t o configure t r ansact ion suppor t for your web for m . You can assign TransactionMode program m at ically in y our form cont r act or, or declar at ively by set t ing t hat proper t y in t he v isual designer. The designer uses t he Transact ion page dir ect ive t o insert a dir ect iv e in t he aspx form file. For ex am ple, if y ou set t he pr opert y using t he designer t o RequiresNew, t he designer added t his line t o t he beginning of t he aspx file:
Be aware t hat pr ogram m at ic set t ing will ov er ride any designer set t ing. The default is no t ransact ion suppor t ( disabled) . The form can even vot e on t he out com e of t he t r ansact ion ( based on it s int er act ion wit h t he com ponent s it creat ed) by using t he ContextUtil m et hods. Finally, t he for m can subscr ibe t o event s not ifying it w hen a t ransact ion is init iat ed and when a t r ansact ion is abor t ed.
1 0 .1 3 COM + Syn ch r on iza t ion Mult it hreaded m anaged com ponent s can use .NET- pr ov ided sy nchr onizat ion locks. These ar e classic lock s, such as m ut exes and event s. However , t hese solut ions all suffer from t he deficiencies described at t he beginning of Chapt er 5. .NET ser viced com ponent s should use COM+ act iv it y- based sy nchr onizat ion by adding t he Synchronization at t r ibut e t o t he class definit ion. The
318
Synchronization at t r ibut e’s const ruct or accept s an enum par am et er of t ype SynchronizationOption, defined as: public enum SynchronizationOption { Disabled, NotSupported, Supported, Required, RequiresNew } For ex am ple, use t he SynchronizationOption.Required v alue t o configure y our serviced com ponent t o require act ivit y - based sy nchr onizat ion: [Synchronization(SynchronizationOption.Required)] public class MyComponent :ServicedComponent {...} The five enum v alues of SynchronizationOption m ap t o t he fiv e COM+ sy nchr onizat ion suppor t opt ions discussed in Chapt er 5. The Synchronization at t ribut e has an over loaded default const ruct or, which set s sy nchronizat ion support t o SynchronizationOption.Required. As a r esult , t he following t wo st at em ent s ar e equiv alent : [Synchronization] [Synchronization(SynchronizationOption.Required)] The System.Runtime.Remoting.Context nam espace cont ains a cont ext at t r ibut e called Synchronization t hat can be applied t o cont ex t bound .NET classes. This at t ribut e accept s sy nchronizat ion flags sim ilar t o SynchronizationOption, and init ially look s lik e anot her version of t he Synchronization class at t ribut e. However , t he Synchronization at t r ibut e in t he Context nam espace prov ides sy nchronizat ion based on physical t hreads, unlik e t he Synchronization at t ribut e in t he EnterpriseServices nam espace, which uses causalit ies. As explained in Chapt er 5, causalit y and act iv it ies are a m or e elegant and fine- t uned sy nchronizat ion st rat egy.
1 0 .1 4 Pr ogr a m m in g t h e COM + Ca t a log You can access t he COM+ Cat alog from wit hin any .NET m anaged com ponent ( not only serv iced com ponent s) . To w rit e inst allat ion or configurat ion code ( or m anage COM+ event s) , you need t o add t o
319
your pr oj ect a reference t o t he COM+ Adm in t ype library . Aft er y ou add t he r eference, t he Cat alog int erfaces and obj ect s are par t of t he COMAdmin nam espace. Exam ple 10- 10 shows how t o creat e a cat alog obj ect and use it t o it er at e over t he applicat ion collect ion, t r acing t o t he Out put window t he nam es of all COM+ applicat ions on your com put er. Ex a m p le 1 0 - 1 0 . Acce ssin g t h e COM + Ca t alog an d t r a cing t h e COM + ap plicat ion n a m es
using COMAdmin; ICOMAdminCatalog catalog; ICatalogCollection applicationCollection; ICatalogObject application; int applicationCount; int i;//Application index catalog = (ICOMAdminCatalog)new COMAdminCatalog( ); applicationCollection = (ICatalogCollection)catalog.GetCollection("Applications") ; //Read the information from the catalog applicationCollection.Populate( ); applicationCount = applicationCollection.Count; for(i = 0;i< applicationCount;i++) { //Get the current application application= (ICatalogObject)applicationCollection.get_Item(i); int index = i+1; String traceMessage = index.ToString()+". "+application.Name.ToString( ); Trace.WriteLine(traceMessage); } The System.EnterpriseServices.Admin nam espace cont ains t he COM+ Cat alog obj ect and int erface definit ions. However, in t he Visual St udio.NET Bet a 2, t he int erfaces are defined as priv at e t o t hat assem bly . As a r esult , you cannot access t hem . The obvious workaround is t o im por t t he COM+ Adm in t ype library y our self, as dem onst r at ed in Exam ple 10- 10. I n t he fut ur e, you will probably be able t o use System.EnterpriseServices.Admin nam espace direct ly. The result ing code, when program m ing 320
direct ly. The result ing code, when program m ing direct ly using t he System.EnterpriseServices.Admin nam espace, is alm ost ident ical t o Exam ple 10- 10.
1 0 .1 5 COM + Se cu r it y .NET has an elaborat e com ponent - orient ed securit y m odel. .NET securit y m odel m anages what t he com ponent is allowed t o do and what per m issions ar e given t o t he com ponent and all it s client s up t he call chain. You can ( and should) st ill m anage t he securit y at t r ibut es of y our host ing COM+ applicat ion t o aut hent icat e incom ing calls, aut horize callers, and cont rol im personat ion lev el. .NET also has what .NET calls r ole- based securit y, but t hat ser vice is lim it ed com pared wit h COM+ r ole- based securit y. A role in .NET is act ually a Windows NT user gr oup. As a r esult , .NET role- based securit y is only as gr anular as t he user gr oups in t he host ing dom ain. Usually, you do not have cont rol ov er your end cust om er’s I T depar t m ent . I f you deploy your applicat ion in an env ironm ent wher e t he user gr oups are coarse, or where t hey do not m ap well t o act ual r oles user s play in y our applicat ion, t hen .NET role- based securit y is of lit t le use t o you. COM+ roles ar e unr elat ed t o t he user groups, allowing you t o assign roles direct ly fr om t he applicat ion business dom ain. 1 0 .1 5 .1 Con f igu r in g Applica t ion - Le ve l Se cu r it y Se t t in gs The assem bly at t ribut e ApplicationAccessControl is used t o configure all t he set t ings on t he host ing COM+ applicat ion’s Secur it y t ab. You can use ApplicationAccessControl t o t ur n applicat ion- level aut hent icat ion on or off: [assembly: ApplicationAccessControl(true)] The ApplicationAccessControl at t ribut e has a default const r uct or, which set s aut horizat ion t o true if you do not provide a const ruct ion v alue. Consequent ly, t he following t wo st at em ent s are equivalent : [assembly: ApplicationAccessControl] [assembly: ApplicationAccessControl(true)] I f you do not use t he ApplicationAccessControl at t ribut e at all, t hen when you regist er y our assem bly, t he COM+ default t ak es effect and applicat ion- level aut horizat ion is t ur ned off. The ApplicationAccessControl at t ribut e has t hree public proper t ies you can use t o set t he access checks, aut hent icat ion, and
321
im personat ion lev el. The AccessChecksLevel proper t y accept s an enum param et er of t ype AccessChecksLevelOption, defined as: public enum AccessChecksLevelOption { Application, ApplicationComponent } AccessChecksLevel is used t o set t he applicat ion- lev el access checks t o t he pr ocess only ( AccessChecksLevelOption.Application) or process and com ponent level ( AccessChecksLevelOption.ApplicationComponent) . I f you do not specify an access level, t hen t he ApplicationAccessControl at t r ibut e’s const ruct ors set t he access level t o AccessChecksLevelOption.ApplicationComponent, t he sam e as t he COM+ default . The Authentication propert y accept s an enum par am et er of t ype AuthenticationOption, defined as: public enum AuthenticationOption { None, Connect, Call, Packet, Integrity, Privacy, Default } The v alues of AuthenticationOption m ap t o t he six aut hent icat ion opt ions discussed in Chapt er 7. I f you do not specify an aut hent icat ion lev el or if you use t he Default v alue, t he ApplicationAccessControl at t ribut e’s const r uct ors set t he aut hent icat ion lev el t o AuthenticationOption.Packet, t he sam e as t he COM+ default . The Impersonation proper t y accept s an enum param et er of t y pe ImpersonationLevelOption, defined as: public enum ImpersonationLevelOption { Anonymous, Identify, Impersonate, Delegate, Default } The v alues of ImpersonationLevelOption m ap t o t he four im personat ion opt ions discussed in Chapt er 7. I f you do not specify an im per sonat ion level or if you use t he Default value, t hen t he ApplicationAccessControl at t ribut e’s const r uct ors set t he 322
im personat ion lev el t o ImpersonationLevelOption.Impersonate, t he sam e as t he COM+ default . Exam ple 10- 11 dem onst rat es using t he ApplicationAccessControl at t ribut e w it h a server applicat ion. The exam ple enables applicat ion- lev el aut hent icat ion and set s t he securit y lev el t o perform access checks at t he pr ocess and com ponent level. I t set s aut hent icat ion t o aut hent icat e incom ing calls at t he packet level and set s t he im personat ion level t o Identify. Ex a m p le 1 0 - 1 1 . Con fig u r in g a se rve r ap plica t ion se cu r it y
[assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl( true,//Authentication is on AccessChecksLevel=AccessChecksLevelOption.ApplicationComp onent, Authentication=AuthenticationOption.Packet, ImpersonationLevel=ImpersonationLevelOption.Identify)] A libr ar y COM+ applicat ion has no use for im per sonat ion level, and it can only choose whet her it want s t o t ak e part in it s host ing process aut hent icat ion level ( t hat is, it cannot dict at e t he aut hent icat ion lev el) . To t urn aut hent icat ion off for a library applicat ion, set t he aut hent icat ion proper t y t o AuthenticationOption.None. To t urn it on, use any ot her value, such as AuthenticationOption.Packet. Exam ple 10- 12 dem onst r at es how t o use t he ApplicationAccessControl t o configure t he securit y set t ing of a libr ary applicat ion. Ex a m p le 1 0 - 1 2 . Con fig u r in g a libr a ry a pp lica t ion se cu r it y
[assembly: ApplicationActivation(ActivationOption.Library)] [assembly: ApplicationAccessControl( true,//Authentication AccessChecksLevel=AccessChecksLevelOption.ApplicationComp onent, //use AuthenticationOption.None to turn off authentication, //and any other value to turn it on Authentication=AuthenticationOption.Packet)] 1 0 .1 5 .2 Com pone n t - Le ve l Acce ss Che ck s
323
The com ponent at t r ibut e ComponentAccessControl is used t o enable or disable access checks at t he com ponent level. Recall from Chapt er 7 t hat t his is your com ponent ’s r ole- based securit y m ast er swit ch. The ComponentAccessControl at t ribut e’s const ruct or accept s a Boolean param et er, used t o t urn access cont r ol on or off. For ex am ple, you can configur e y our serv iced com ponent t o requir e com ponent - level access checks: [ComponentAccessControl(true)] public class MyComponent :ServicedComponent {...} The ComponentAccessControl at t ribut e has an ov erloaded default const ruct or t hat uses truefor t he at t r ibut e const r uct ion. Consequent ly , t he following t wo st at em ent s ar e equiv alent : [ComponentAccessControl] [ComponentAccessControl(true)] 1 0 .1 5 .3 Adding Role s t o a n Applica t ion You can use t he Com ponent Serv ices Explorer t o add roles t o t he COM+ applicat ion host ing your serviced com ponent s. You can also use t he SecurityRole at t r ibut e t o add t he r oles at t he assem bly lev el. When you regist er t he assem bly wit h COM+ , t he roles in t he assem bly are added t o t he roles defined for t he host ing COM+ applicat ion. For ex am ple, t o add t he Manager and Teller roles t o a bank applicat ion, sim ply add t he t wo r oles as assem bly at t ribut es: [assembly: SecurityRole("Manager")] [assembly: SecurityRole("Teller")] The SecurityRole at t ribut e has t wo public proper t ies you can set . The first is Description. Any t ex t assigned t o t he Description proper t y will show up in t he Com ponent Serv ices Explorer in t he Descript ion field on t he r ole’s Gener al t ab: [assembly: SecurityRole("Manager",Description = "Can access all components")] [assembly: SecurityRole("Teller",Description = "Can access IAccountsManager only")] The second pr opert y is t he SetEveryoneAccess Boolean pr opert y. I f you set SetEveryoneAccess t o true, t hen w hen t he com ponent is regist ered, t he regist rat ion pr ocess adds t he user Every one as a user for t hat role, t hus allowing ev eryone access t o what ever t he role is assigned t o. I f you set it t o false, t hen no user is added during r egist r at ion and y ou have t o explicit ly add users during deploy m ent using t he Com ponent Services Ex plor er . The SecurityRole at t ribut e set s t he value of SetEveryoneAccess by default t o true. As a result , t he following st at em ent s ar e equivalent : [assembly: SecurityRole("Manager")] [assembly: SecurityRole("Manager",true)]
324
[assembly: SecurityRole("Manager",SetEveryoneAccess = true)] Aut om at ically grant ing everyone access is a nice debugging feat ur e; it elim inat es securit y problem s, let t ing y ou focus on analy zing your dom ain- r elat ed bug. However, y ou m ust suppr ess gr ant ing every one access in a release build, by set t ing t he SetEveryoneAccess pr opert y t o false: #if DEBUG [assembly: SecurityRole("Manager")] #else [assembly: SecurityRole("Manager",SetEveryoneAccess = false)] #endif 1 0 .1 5 .4 Assignin g Role s t o Com pon e n t , I n t e r fa ce , a n d M e t h od The SecurityRole at t ribut e is also used t o grant access for a r ole t o a com ponent , int er face, or m et hod. Exam ple 10- 13 shows how t o grant access t o Role1 at t he com ponent level, t o Role2 at t he int er face level, and t o Role3 at t he m et hod level. Ex a m p le 1 0 - 1 3 . Assig n in g role s a t t h e com p on en t , in t e r face , an d m e t h od leve ls
[assembly: SecurityRole("Role1")] [assembly: SecurityRole("Role2")] [assembly: SecurityRole("Role3")] [SecurityRole("Role2")] public interface IMyInterface { [SecurityRole("Role3")] void MyMethod( ); } [SecurityRole("Role1")] public class MyComponent :ServicedComponent,IMyInterface {...} Figure 10- 2 shows t he result ing r ole assignm ent in t he Com ponent Serv ices Explorer at t he m et hod level. Not e t hat Role1 and Role2 are inherit ed from t he com ponent and int erface lev els. Figu r e 1 0 - 2 . Th e r esu lt in g r ole assign m e n t of Exa m ple 1 0 - 1 3 in t h e Com p on en t Se rvices Ex plor er , a s se e n a t t h e m e t h od le ve l
325
I f you only assign a r ole ( at t he com ponent , int erface, or m et hod lev el) but do not define it at t he assem bly lev el, t hen t hat r ole is added t o t he applicat ion aut om at ically dur ing r egist r at ion. However, you should define roles at t he assem bly level t o provide one cent ralized place for r oles descr ipt ion and configurat ion. 1 0 .1 5 .5 Ve r if ying Ca lle r ’s Role M e m be r ship Som et im es it is useful t o v er ify program m at ically t he caller’s role m em bership before grant ing it access. Your serv iced com ponent s can do t hat j ust as easily as configured COM com ponent s. .NET provides you t he helper class SecurityCallContext t hat gives you access t o t he secur it y param et ers of t he curr ent call. SecurityCallContext encapsulat es t he COM+ call- obj ect ’s im plem ent at ion of ISecurityCallContext, discussed in Chapt er 7. The class SecurityCallContext has a public st at ic proper ty called CurrentCall. CurrentCall is a r ead- only propert y of t ype SecurityCallContext ( it r et urns an inst ance of t he sam e t ype) . You use t he SecurityCallContext obj ect ret ur ned fr om CurrentCall t o access t he cur rent call. Exam ple 10- 14 dem onst r at es t he use of t he secur it y call cont ext t o v er ify a caller’s role m em ber ship, using t he sam e use- case as Exam ple 7- 1. Ex a m p le 1 0 - 1 4 . V e rif yin g t h e calle r’s role m e m b er sh ip u sin g t h e Se cu r it yCa llCon t e x t cla ss
public class Bank :ServicedComponent,IAccountsManager { void TransferMoney(int sum,ulong accountSrc,ulong accountDest) { bool callerInRole = false; callerInRole = SecurityCallContext.CurrentCall.IsCallerInRole("Customer" );
326
if(callerInRole)//The caller is a customer { if(sum > 5000) throw(new UnauthorizedAccessException(@"Caller does not have sufficient credentials to transfer this sum")); } DoTransfer(sum,accountSrc,accountDest);//Helper method } //Other methods } You should use t he Boolean propert y IsSecurityEnabled of SecurityCallContext t o verify t hat securit y is enabled before accessing t he IsCallerInRole( ) m et hod: bool securityEnabled = SecurityCallContext.CurrentCall.IsSecurityEnabled; if(securityEnabled) { //the rest of the verification process }
1 0 .1 6 COM + Qu eu ed Com pon e nt s .NET has a built - in m echanism for inv oking a m et hod call on an obj ect : using a delegat e asynchr onously. The client creat es a delegat e class t hat wraps t he m et hod it want s t o inv oke sy nchr onously, and t he com piler pr ov ides definit ion and im plem ent at ion for a BeginInvoke( ) m et hod, w hich asynchronously calls t he requir ed m et hod on t he obj ect . The com piler also generat es t he EndInvoke( ) m et hod t o allow t he client t o poll for t he m et hod com plet ion. Addit ionally , .NET pr ovides a helper class called AsyncCallback t o m anage asynchr onous callbacks fr om t he obj ect once t he call is done. Com pared w it h COM+ queued com ponent s, t he .NET approach leaves m uch t o be desired. First , .NET does not support disconnect ed work. Bot h t he client and t he ser ver have t o be running at t he sam e t im e, and t heir m achines m ust be connect ed t o each ot her on t he net work. Second, t he client ’s code in t he asynchronous case is v ery different fr om t he usual sy nchr onous inv ocat ion of t he sam e m et hod on t he obj ect ’s int erface. Thir d, t here is no built - in support for t ransact ional for war ding of calls t o t he serv er , nor is t here an aut o- ret ry m echanism . I n shor t , y ou should use COM+ queued com ponent s if you want t o invoke asynchronous m et hod calls in .NET.
327
The ApplicationQueuing assem bly at t ribut e is used t o configure queuing suppor t for t he host ing COM+ applicat ion. The ApplicationQueuing at t r ibut e has t wo public pr opert ies t hat y ou can set . The Boolean Enabledpr opert y cor responds t o t he Queued checkbox on t he applicat ion’s queuing t ab. When set t o true, it inst ruct s COM+ t o cr eat e a public m essage queue, nam ed as t he applicat ion, for t he use of any queued com ponent s in t he assem bly. The second public proper t y of ApplicationQueuing is t he Boolean QueueListenerEnabled proper t y . I t cor responds t o t he List en checkbox on t he applicat ion’s queuing t ab. When set t o true, it inst ruct s COM+ t o act ivat e a list ener for t he applicat ion w hen t he applicat ion is launched. For ex am ple, here is how you enable queued com ponent support for your applicat ion and enable a list ener: //Must be a server application to use queued components [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationQueuing(Enabled = true,QueueListenerEnabled = true)] The ApplicationQueuing at t r ibut e has an ov er loaded default const ruct or t hat set s t he Enabled at t r ibut e t o true and t he QueueListenerEnabled at t r ibut e t o false. As a result , t he following t wo st at em ent s ar e equiv alent : [assembly: ApplicationQueuing] [assembly: ApplicationQueuing(Enabled = true,QueueListenerEnabled = false)] 1 0 .1 6 .1 Con f igu r in g Qu e u e d I n t e r f a ce s I n addit ion t o enabling queued com ponent support at t he applicat ion level, you m ust m ark your int erfaces as capable of receiving queued calls. You do t hat by using t he InterfaceQueuing at t r ibut e. InterfaceQueuing has one public Boolean propert y called Enabled t hat cor responds t o t he Queued checkbox on t he int er face’s Queuing t ab. [InterfaceQueuing(Enabled = true)] public interface IMyInterface { void MyMethod( ); } The InterfaceQueuing at t r ibut e has an overloaded default const ruct or t hat set s t he Enabled proper t y t o true and a const ruct or t hat accept s a Boolean param et er. As a result , t he following t hree st at em ent s ar e equiv alent : [InterfaceQueuing] [InterfaceQueuing(true)] [InterfaceQueuing(Enabled = true)]
328
Not e t hat y our int er face m ust adher e t o t he queued com ponent s design guidelines discussed in Chapt er 8, such as no out or ref par am et ers. I f you configure your int erface as a queued int er face using t he InterfaceQueuing at t r ibut e and t he int er face is incom pat ible wit h queuing requirem ent s, t he regist rat ion pr ocess fails. 1 0 .1 6 .2 A Qu e u e d Com pon e n t ’s M a na ge d Clie n t The client of a queued com ponent cannot creat e t he queued com ponent dir ect ly . I t m ust cr eat e a recorder for it s calls using t he queue m oniker . A C+ + or a Visual Basic 6.0 program uses t he CoGetObject( ) or GetObject( ) calls. A .NET m anaged client can use t he st at ic m et hod BindToMoniker( ) of t he Marshal class, defined as: public static object BindToMoniker(string monikerName); BindToMoniker( ) accept s a m onik er st r ing as a par am et er and ret ur ns t he corr esponding obj ect . The Marshal class is defined in t he System.Runtime.InteropServices nam espace. The BindToMoniker( ) m et hod of t he Marshal class m ak es wr it ing m anaged client s for a queued com ponent as easy as if it w ere a COM client : using System.Runtime.InteropServices;//for the Marshal class IMyInterface obj; obj =(IMyInterface)Marshal.BindToMoniker("queue:/new:MyNamesp ace.MyComponent"); obj.MyMethod( );//call is recorded I n t he case of a COM client , t he recorder r ecords t he calls t he client m ak es. The r ecorder only dispat ches t hem t o t he queued com ponent queue ( m or e pr ecisely, t o it s applicat ion’s queue) when t he client r eleases t he recorder. A m anaged client does not use reference count ing, and t he recorded calls ar e dispat ched t o t he queued com ponent queue when t he m anaged wrapper around t he recorder is garbage collect ed. The client can ex pedit e dispat ching t he calls by explicit ly forcing t he m anaged wrapper ar ound t he recorder t o release it , using t he st at ic DisposeObject( ) m et hod of t he ServicedComponent class, passing in t he r ecorder obj ect : using System.Runtime.InteropServices;//for the Marshal class IMyInterface obj; obj =(IMyInterface)Marshal.BindToMoniker("queue:/new:MyNamesp ace.MyComponent"); obj.MyMethod( );//call is recorded
329
//Expedite dispatching the recorded calls by disposing of the recorder ServicedComponent sc = obj as ServicedComponent; If(sc !=null) ServicedComponent.DisposeObject(sc); You can use t he IDisposable int er face inst ead of calling DisposeObject(). 1 0 .1 6 .3 Qu e u e d Com pon e nt Er r or H a n dling Due t o t he nat ure of an asynchr onous queued call, m anaging a failure on bot h t he client ’s side ( failing t o dispat ch t he calls) and t he server ’s side ( repeat edly failing t o execut e t he call—a poison m essage) r equires a special design appr oach. As discussed in Chapt er 8, bot h t he client s and server can use a queued com ponent except ion class t o handle t he err or. You can also prov ide your product adm inist rat or wit h an adm inist rat ion ut ilit y for m oving m essages bet ween t he r et ry queues. 1 0 .1 6 .3 .1 Qu e u e d com p on en t ex ce pt ion cla ss
You can designat e a m anaged class as t he except ion class for your queued com ponent using t he ExceptionClass at t ribut e. Exam ple 10- 15 dem onst r at es using t he ExceptionClass at t r ibut e. Ex a m p le 1 0 - 1 5 . Usin g t h e Ex cep t ion Cla ss at t rib u t e t o de sig n a t e a n er r or h a n dlin g class for you r qu e u ed com p on e nt
using COMSVCSLib; public class MyQCException : IPlaybackControl,IMyInterface { public void FinalClientRetry( ) {...} public void FinalServerRetry( ) {...} public void MyMethod( ){...} } [ExceptionClass("MyQCException")] public class MyComponent :ServicedComponent,IMyInterface {...} I n Ex am ple 10- 15, when you regist er t he assem bly cont aining MyComponent wit h COM+ , on t he com ponent 's Adv anced t ab, t he Queuing except ion class field will cont ain t he nam e of it s ex cept ion class—in t his case, MyQCException, as shown in Figur e 10- 3. Figu re 1 0 - 3 . Aft e r re gist e r in g t h e com pon en t in Ex a m ple 1 0 - 1 5 w it h COM + , it s Adva n ce d t a b con t ain s t h e e x ce pt ion class
330
You need t o k now a few m or e t hings about designat ing a m anaged class as a queued com ponent ’s except ion class. First , it has not hing t o do wit h .NET er ror handling via ex cept ions. The w ord except ion is overloaded. As far as .NET is concerned, a queued com ponent ’s except ion class is not a .NET ex cept ion class. Second, t he queued com ponent ex cept ion class has t o adhere t o t he r equirem ent s of a queued com ponent except ion class descr ibed in Chapt er 8. These requir em ent s include im plem ent ing t he sam e set of queued int er faces as t he queued com ponent it self and im plem ent ing t he IPlaybackControl int er face. To add IPlaybackControl t o your class definit ion you need t o add a reference in your proj ect t o t he COM+ Serv ices t ype library. IPlaybackControl is defined in t he COMSVCSLib nam espace. 1 0 .1 6 .3 .2 Th e M e ssa ge M over cla ss
As explained in Chapt er 8, COM+ provides you wit h t he IMessageMover int er face, and a st andard im plem ent at ion of it , for m oving all t he m essages fr om one ret ry queue t o anot her. Managed client s can access t his im plem ent at ion by im por t ing t he COM+ Serv ices t ype library and using t he MessageMover class, defined in t he COMSVCSLib nam espace. Exam ple 10- 16 im plem ent s t he sam e use- case as Ex am ple 8- 2. Ex a m p le 1 0 - 1 6 . M e ssag e M ove r is u sed t o m ove m essa ge s fr om t h e last re t r y q u e u e t o t h e applica t ion ’s qu e u e
using COMSVCSLib; IMessageMover messageMover; int moved;//How many messages were moved messageMover = (IMessageMover) new MessageMover(
);
//Move all the messages from the last retry queue to the application’s queue messageMover.SourcePath = @".\PRIVATE$\MyApp_4"; messageMover.DestPath = @".\PUBLIC$\MyApp"; moved = messageMover.MoveMessages(
);
331
1 0 .1 7 COM + Loosely Couple d Eve n t s .NET prov ides m anaged classes wit h an easy way t o hook up a server t hat fires event s wit h client sink s. The .NET m echanism is cert ainly an im provem ent ov er t he som ewhat cum bersom e COM connect ion point prot ocol, but t he .NET m echanism st ill suffer s fr om all t he disadvant ages of t ight ly coupled event s, as ex plained at t he beginning of Chapt er 9. Fort unat ely , m anaged classes can easily t ake advant age of COM+ loosely coupled event s. The EventClass at t ribut e is used t o m ar k a serviced com ponent as a COM+ event class, as shown in Ex am ple 10- 17. Ex a m p le 1 0 - 1 7 . D e sign at in g a se r viced com pon e n t a s a n eve n t cla ss u sin g t h e Eve n t Cla ss at t rib u t e
public interface IMySink { void OnEvent1( ); void OnEvent2( ); } [EventClass] public class MyEventClass : ServicedComponent,IMySink { public void OnEvent1( ) { throw(new NotImplementedException(exception)); } public void OnEvent2( ) { throw(new NotImplementedException(exception)); } const string exception = @"You should not call an event class directly. Register this assembly using RegSvcs /reconfig"; } The event class im plem ent s a set of sink int erfaces you want t o publish ev ent s on. Not e t hat it is point less t o hav e any im plem ent at ion of t he sink int er face m et hods in t he event class, as t he ev ent class’s code is never used. I t is used only as a t em plat e, so t hat COM+ could sy nt hesize an im plem ent at ion, as ex plained in Chapt er 9 ( com pare Exam ple 10- 17 wit h Ex am ple 9- 1) . This is why t he code in Ex am ple 10- 17 t hrows an except ion if anybody t ries t o act ually call t he m et hods ( m aybe as a r esult of r em oving t he ev ent class from t he Com ponent Ser vices Explor er ) . When y ou r egist er t he assem bly wit h COM+ , t he ev ent class is added as a COM+ event class, not as a r egular COM+ com ponent .
332
Any m anaged class ( not j ust ser viced com ponent s) can publish event s. Any m anaged class can also im plem ent t he sink’s int er faces, subscribe, and receiv e t he event s. For exam ple, t o publish ev ent s using t he event class fr om Exam ple 10- 17, a m anaged publisher would writ e: IMySink sink; sink = (IMySink)new MyEventClass( ); sink.OnEvent1( ); The OnEvent1( ) m et hod ret ur ns once all subscribers have been not ified, as ex plained in Chapt er 9. Per sist ent subscript ions are m anaged direct ly via t he Com ponent Serv ices Explorer because adding a persist ent subscr ipt ion is a deploy m ent - specific act iv it y. Tr ansient subscript ions are m anaged in your code, sim ilar t o COM+ t ransient subscr ibers. The EventClass at t ribut e has t wo public Boolean proper t ies you can set , called AllowInprocSubscribers and FireInParallel. These t wo proper t ies cor respond t o t he Fir e in parallel and Allow inprocess subscribers, r espect iv ely, on t he event class’s Adv anced t ab. You can configure t hese values on t he event class definit ion: [EventClass(AllowInprocSubscribers = true,FireInParallel=true)] public class MyEventClass : ServicedComponent,IMySink {...} The EventClass at t ribut e has an ov er loaded default const r uct or . I f you do not specify a v alue for t he AllowInprocSubscribers and FireInParallel propert ies, it set s t hem t o true and false, respect ively. Consequent ly, t he following t wo st at em ent s are equivalent : EventClass] [EventClass(AllowInprocSubscribers = true,FireInParallel=false)]
1 0 .1 8 Su m m a r y Throughout t his book, you have learned t hat y ou should focus y our dev elopm ent effort s on im plem ent ing business logic in y our com ponent s and rely on COM+ t o provide t he com ponent serv ices and connect iv it y t hey need t o operat e. Wit h .NET, Micr osoft has reaffirm ed it s com m it m ent t o t his dev elopm ent paradigm . Fr om a configurat ion m anagem ent point of view, t he .NET int egrat ion wit h COM+ is super ior t o COM under Visual St udio 6.0 because .NET allows you t o capt ure your design decisions in y our code, r at her t han use t he separ at e COM+ Cat alog. This dev elopm ent is undoubt edly j ust t he beginning of seam less suppor t and bet t er int egr at ion of t he .NET dev elopm ent t ools, runt im e, com ponent serv ices, and t he com ponent adm inist rat ion env ir onm ent . COM+ it self ( see Appendix B) cont inues t o ev olv e, bot h in feat ur es and in 333
usabilit y, while drawing on t he new capabilit ies of t he .NET plat for m . The recent ly added abilit y t o expose any COM+ com ponent as a web service is only a preview of t he t ight er int egr at ion of .NET and COM+ we can expect t o see in t he fut ur e.
334
Appe ndix A. The COM + Logbook One of t he m ost effect ive st eps you can t ake t owar ds achieving a m ore robust applicat ion t hat is fast er t o m ark et is adding a logging capabilit y t o your applicat ion. This appendix present s y ou wit h t he COM+ Logbook, a sim ple ut ilit y y ou can im plem ent t o log m et hod calls, event s, er ror s, and v arious COM+ inform at ion. The logbook is your pr oduct ’s flight recorder. I n a dist ribut ed COM+ environm ent , it is wort h it s weight in gold. I t sav ed m y skin whenever I t r ied t o analyze why som et hing did not work t he way it was supposed t o. By exam ining t he log files, you can analyze what t ook place acr oss m achines and applicat ions, and t he sour ce of t he pr oblem is alm ost im m ediat ely evident . The logbook is also useful in post - deploy m ent scenar ios t o t roubleshoot cust om er pr oblem s—j ust hav e your cust om er send you t he log files.
A.1 Logbook Requ ir e m en t s The goals for t his logbook are as follows: •
• • • • • • • • •
Trace t he calling t r ee ( t he causalit y) from t he original client down t o t he lowest com ponent s, acr oss t hreads, processes, and m achines—t r acing t he logical t hread of execut ion. Log t he call's/ event 's/ error's t im e and locat ion. I nt er leave all t he calls fr om all applicat ions int o one log file. Log t he curr ent COM+ ex ecut ion cont ex t . Allow adm inist rat ive cust om izat ion t o det erm ine what is logged—for ex am ple, j ust er ror s, or event s and er ror s. Allow adm inist rat ive cust om izat ion of t he log filenam e. Make logging and t racing as easy as possible. Save log dat a in t w o form at s: HTML or XML. Hav e a different lifeline for t he logbook applicat ion and t he applicat ions using it . Be able t o t oggle logging on or off.
The COM+ Logbook is a COM+ serv er applicat ion t hat im plem ent s t hese requirem ent s. I n addit ion t o being used by COM+ applicat ions, it can be used in any Win32 applicat ion ( such as MFC or classic COM.) The only requir em ent is t hat t he applicat ion needs t o r un on Windows 2000.
A.2 Log File Ex a m ple
335
Figures A- 1 and A- 2 show t he sam e t racing and logging ent r ies— one in HTML for m at and t he ot her in XML. The HTML log file is alr eady well for m at t ed and can be viewed by a user as is. The XML log file is less pr esent able. Each ent r y in a log file cont ains t he ent r y num ber ( different num bers for m et hod calls, event s, and err or s) ; t he call, er ror , and event t im e; m achine nam e; pr ocess I D; t hread I D; cont ext I D; t r ansact ion I D; act ivit y I D; t he m odule nam e ( t he EXE or DLL nam e) ; t he m et hod nam e or t he error/ event descript ion; t he sour ce filenam e; and t he line num ber . Figu r e A- 1 . Logging en t ries in H TM L
Figu r e A- 2 . Logging en t r ie s in XM L
336
A.3 Usin g t h e Logbook Befor e using t he logbook, you need t o inst all it . Dow nload t he logbook.m si inst allat ion package and t he header file Com LogBook.h fr om t he O’Reilly web sit e for t his book at ht t p: / / www.or eilly .com / cat alog/ com dot net svs ( t he logbook sour ce files and a Windows 2000 help file are also available for download) . Then inst all t he m si file. Aft er inst alling t he logbook applicat ion, all y ou have t o do on t he side of t he applicat ion doing t he logging is include t he Com LogBook.h header file in your applicat ion. The Com LogBook.h header file defines four helper m acros for logging, descr ibed in Table A- 1. I nser t t hese m acros in your code. The m acr os collect infor m at ion on t he applicat ion side and post it t o t he logbook . 7DEOH$7KHORJJLQJPDFURV 0DFURQDPH
'HVFULSWLRQ
LOGMETHOD( )
Traces a m et hod call int o t he logbook
LOGERROR( )
Logs an err or int o t he logbook
LOGEVENT( )
Logs an event int o t he logbook LOGERROR_AND_RETURN( Logs an err or int o t he logbook and r et ur ns in case of an ) er ror, or cont inues t o run if no error has occurr ed
The m acros can be used independent ly of one anot her and in ev er y possible com binat ion. For exam ple, t o t race a m et hod call int o t he logbook , pass t he m et hod nam e as a st r ing param et er t o t he LOGMETHOD( ) m acro: void CMyClass::MyMethod( ) { LOGMETHOD("CMyClass::MyMethod"); //Real work starts here } I recom m end using LOGMETHOD( ) before doing any t hing else in t he m et hod body . Along w it h t he m et hod nam e, t he m acr o logs all t he requir ed inform at ion m ent ioned ear lier. Sim ilarly , y ou can use LOGEVENT( ) t o log event s and LOGERROR( ) t o log errors ( see Exam ple A- 1) . Ex a m p le A- 1 . Usin g t h e LOGERROR( ) a n d t h e LOGEV EN T( ) m acr os
//Logging an error: void CMyClass::MyMethod( ) { LOGMETHOD("CMyClass::MyMethod"); //Real work starts here /* some code that encountered an error with a pointer
337
*/ LOGERROR(IID_MyInterface,E_POINTER,"CMyClass::MyMethod"," The server returned an invalid address"); //Continue to run } //logging an event into the logbook: specify in free form text describing the event: void CMyClass::MyMethod( ) { LOGMETHOD("CMyClass::MyMethod"); //Real work starts here /* some code that decides to log an event */ LOGEVENT("The User is banging on the keyboard"); //Continue to run }
A.4 Con figu r ing t h e Logbook Configuring t he various logging opt ions is done direct ly via t he Com ponent Services Explorer. Aft er inst alling t he logbook , y ou will hav e a new COM+ applicat ion called Logbook wit h t hr ee com ponent s—t he HTML logger, t he XML logger , and an event class ( see Figure A- 3) . All t hr ee com ponent s im plem ent t he ILogbook int er face wit h t he m et hods LogError( ), LogEvent( ), and LogMethod( ). The HTML and XML com ponent s hav e four per sist ent subscript ions—one for each ILogBook m et hod and one for all t he m et hods on t he int erface. Figu re A- 3 . Th e Log book a pp lica t ion h as t h r ee com pon e n t s: t he H TM L logge r, t h e X M L logge r , a n d a n e ven t cla ss
338
The m ain m echanism behind t he logging is COM+ loosely coupled event s. The m acros publish t he dat a as COM+ event s, and t he logbook com ponent s ar e persist ent subscr ibers. Each logbook com ponent has four persist ent subscr ipt ions in it s Subscript ion folder: Errors Only, Met hods Only , Event s Only , and Log All. By enabling or disabling a subscr ipt ion, you can cont rol what is being logged and in what for m at . Aft er inst allat ion, by default , bot h t he HTML and t he XML Log All subscript ions are enabled and t he ot her subscript ions ar e disabled. I f, for exam ple, you wish t o have only HTML logging of event s and error s, you should follow t hese st eps: •
• •
Go t o t he XML com ponent s, select t he Log All subscript ion, display it s pr opert ies page, go t o t he Opt ions t ab, and disable t he subscript ion. Disable t he HTML com ponent ’s Log All subscr ipt ion. Enable t he HTML com ponent ’s Errors Only and Ev ent s Only subscript ions.
The HTML and XML com ponent s, by default , will log t o t he files C: \ Tem p\ Logbook.ht m and C: \ Tem p\ Logbook.xm l, r espect ively. The filenam es are provided as const r uct or st rings t o t he com ponent s. To specify a different filenam e ( for exam ple, D: \ My Log.ht m for t he HTML com ponent ) , display t he HTML com ponent pr opert ies and select t he Act ivat ion t ab ( see Figure A- 4) . Under Obj ect const ruct ion, specify t he new filenam e. Figu r e A- 4 . Th e logbook com pon e n t p rope r t ies pa ge Act iva t ion t a b
339
One int erest ing aspect of t he logbook is t hat it s lifeline is independent from t hat of t he applicat ions using it because it uses per sist ent subscript ions. As a result , logging fr om m any applicat ion runs will all be concat enat ed in t he sam e file. I f you want t he logbook t o st art a new log file, you have t o shut down t he logbook applicat ion m anually ( right - click it in t he Com ponent Services Explorer and select Shut Down) . Nex t t im e an applicat ion publishes t o t he logbook, t he logbook clear s t he file and st ar t s afr esh. You can do t hat ev en when t he applicat ion doing t he logging is running.
A.5 H ow Doe s t h e Logbook W or k ? The logbook uses COM+ event s t o pass t he inform at ion collect ed fr om t he applicat ion t o t he logbook com ponent s. The com ponent s ( t he HTML and XML v er sions) im plem ent t he ILogbook int er face ( see Figure A- 3) , a cust om int erface wit h m et hods corr esponding t o what is being logged—m et hod call, ev ent , or err or. The ILogbook int er face is defined as: interface ILogbook : IUnknown { typedef struct tagLOG_ENTRY { HRESULT hres; DWORD dwErrorCode; DWORD dwProcessID; DWORD dwThreadID; GUID guidActivityID;
340
GUID guidTransactionID; GUID guidContextID; BSTR bstrMachineName; BSTR bstrSourceFileName; BSTR bstrModuleName; BSTR bstrMethodName; DWORD dwLineNumber; BSTR bstrDescription; IID iidError; FILETIME eventTime; }LOG_ENTRY; HRESULT LogError ([in]LOG_ENTRY* pErrorEntry); HRESULT LogMethod([in]LOG_ENTRY* pMethodEntry); HRESULT LogEvent ([in]LOG_ENTRY* pEventEntry); }; The helper m acros collect t he infor m at ion on t he applicat ion side, pack it int o a LOG_ENTRY st ruct , creat e a COM+ event class t hat im plem ent s ILogbook, and fir e t he appropriat e event . The logbook receives t he event , form at s it appr opr iat ely ( t o HTML or XML) , and writ es it t o t he log file. Deciding t o use COM+ ev ent s was t he easy part of t he design. Deciding how t o channel all t he event s t o t he sam e logbook com ponent and how t o collect all t he t racing inform at ion y ou are int er est ed in is m or e challenging. To solve t he first challenge, you can use COM+ inst ance m anagem ent s serv ices. The com ponent s in t he logbook applicat ion are configured t o use Obj ect Pooling and Just - in- Tim e Act iv at ion ( JI TA) t o creat e t he COM+ equiv alent of a singlet on ( as discussed in Chapt er 3) . Each com ponent ( HTML and XML) im plem ent s t he IObjectControl int erface and ret urns TRUE from IObjectControl::CanBePooled( ). The obj ect pool is configured t o hav e a m inim um and a m ax im um pool size of 1, ensuring t hat t here is alw ays exact ly one inst ance of a com ponent of t hat t ype ( see Figure A- 4) . When a logging client applicat ion publishes an ev ent , COM+ delivers t he ev ent t o t he persist ent subscript ions of t he logbook com ponent . But because t he logbook com ponent is pooled, wit h a pool size of exact ly 1, COM+ does not creat e a new inst ance of t he persist ent subscriber. I nst ead, it r et rieves t he logbook com ponent fr om t he pool, hands t he event t o t he com ponent , and releases it back t o t he pool once t he m et hod ret ur ns. However, what would happen if a greedy applicat ion creat ed t he logbook com ponent direct ly and held on t o it ? The m axim um pool size is 1, so COM+ wouldn’t creat e anot her inst ance of t he logbook com ponent t o publish t he event t o it , but would inst ead wait for t he exist ing obj ect t o r et urn t o t he pool. The obj ect wouldn’t r et urn, t hough, since t he greedy applicat ion would be holding a reference t o it . As a result , all
341
at t em pt s fr om ot her applicat ions t o publish t o t he logbook would fail aft er t he t im eout specified in t he Cr eat ion Tim eout ( see Figur e A- 4) . As y ou saw in Chapt er 3, JI TA is designed t o handle such greedy client s. I f t he logbook com ponent indicat es t o COM+ t hat it is w illing t o be deact ivat ed and is configured t o use JI TA, COM+ deact ivat es t he com ponent . I n t his case, it ret ur ns back t o t he pool, as opposed t o a real r elease. The greedy applicat ion does not k now t he differ ence because it st ill has a refer ence t o a valid pr oxy. The nex t t im e t he gr eedy client applicat ion t ries t o access t he logbook , COM+ det ect s it , r et rieves t he obj ect from t he pool, and hooks it up wit h t he int er cept or so the gr eedy applicat ion’s logging call goes t hr ough. The logbook com ponent s ar e t her efor e configur ed t o use JI TA ( see Figure A- 4) . However , a logbook com ponent st ill has t o let COM+ know w hen it is okay t o deact ivat e it . The logical place would be at m et hod boundar ies when it is done logging t o t he file. Therefore, t he logbook com ponent s use COM+ m et hod aut o- deact iv at ion ( see Figure A- 5) . Every logging m et hod is configured t o aut om at ically deact iv at e t he obj ect on r et urn. Figu re A- 5 . COM + d e act iva t e s t h e ob j ect a ft er e a ch m e t h od ca ll
Because t he logbook applicat ion is a server applicat ion, t here is lit t le im pact for t he com ponent ’s t hreading m odel, since all calls ar e m ar shaled acr oss process boundar ies anyway. For t he r em ot e possibilit y of ever being deploy ed in a library applicat ion, t he logbook com ponent s use t he Both t hreading m odel. Sy nchronizat ion is provided by having t he com ponent s’ sy nchr onizat ion configur ed as Required. Not e t hat , as explained in Chapt er 5, JI TA r equir es sy nchr onizat ion, so t he only available synchr onizat ion set t ings are Required and Requir es New. One ot her configur at ion set t ing used is t o hav e COM+ leave t he logbook applicat ion r unning when idle ( on t he Logbook applicat ion proper t ies page, Adv anced t ab) . This is requir ed t o keep t he pool alive, even if t here ar e no ext er nal client s using logging. As a result , all logging is writ t en t o t he sam e file. Because you only have t o 342
cr eat e a new applicat ion and com ponent inst ance once, per form ance im proves. You already saw t hat t he filenam e is passed as a const ruct or st ring. As explained in Chapt er 3, t he logbook com ponent s im plem ent IObjectConstruct t o access t hat st r ing. COM+ queries for t hat int er face aft er creat ing t he obj ect . Then it passes t o t he only m et hod, Construct( ), a point er t o an IObjectConstructString obj ect . You can use t hat point er t o get t he const ruct or st r ing, which is a filenam e in t his case. Look at Exam ple 3- 2 in Chapt er 3 t o learn how t o gain access t o t he const r uct or st ring. The ot her m aj or challenge in developing t he logbook is collect ing t he inform at ion on t he client side. Som e of it , lik e line num ber , file, and m odule nam e, has not hing t o do wit h COM+ and ar e j ust neat program m ing t r icks t hat use predefined com piler m acros; look at t he sour ce files if you are curious. Obt aining t he ex ecut ion and cont ext I Ds is anot her t hing. Fort unat ely, COM+ has an ex cellent infrast r uct ure for t his pur pose: t he IObjectContextInfo int erface. As dem onst r at ed in Chapt er 2 and and ot her chapt ers in t he book, you can use t he IObjectContextInfo int erface t o ret r ieve t he cont ext , t r ansact ion, and act ivit y I D. This is ex act ly w hat t he helper m acros ( Table A- 1) do on t he client side. The m acros act ually use a helper class, CEventLogger, t o collect t he inform at ion and publish it t o t he logbook. Exam ple A- 2 show s how t he LOGEVENT m acro is im plem ent ed. Ex a m p le A- 2 . Th e LOGEV EN T h e lp e r m a cro
#define LOGEVENT(x)
DoLogEvent(x)
inline void DoLogEvent(const CString& sEvent) { CEventLogger eventLogger; eventLogger.DoLogEvent(sEvent); } inline void CEventLogger::DoLogEvent(const CString& sEvent) const { LOG_ENTRY logEntry; HRESULT hres = S_OK; ILogbook* pLogbook = NULL; FillLogEntry(&logEntry,sEvent);//using IObjectContextInfo to get the IDs //Create the event class and publish hres = ::CoCreateInstance(CLSID_LogbookEventClass,.., IID_ILogbook,&pLogbook);
343
//Publish to the logbook hres = pLogbook->LogEvent(&logEntry); pLogbook->Release( ); }
A.6 Su m m a r y The logbook m akes elegant use of m any COM+ feat ures, such as t he ev ent syst em , Just - in- Tim e Act ivat ion ( JI TA) , obj ect pooling, idle t im e m anagem ent , aut om at ic deact iv at ion of obj ect s, sy nchr onizat ion, and t he obj ect const r uct or st ring. The logbook is a good exam ple of t he sy nergies generated by using m ult iple ser vices sim ult aneously. You can ext end t he logbook or im pr ov e it by cust om izing it t o fit specific requirem ent s ( such as adding verbosit y lev els) . I n any case, once you st art enj oying t he product iv it y boost of t he logbook, you will find y our self asking one quest ion: " How did I ev er m anage wit hout it ?"
344
Appe ndix B. COM + 1 .5 The next release of Windows 2000, Windows XP, will be t he first Windows operat ing syst em t o include t he nex t ver sion of COM+ , called COM+ 1.5. This appendix describes t he new feat ures and capabilit ies of t his fut ure release of COM+ . The curr ent version of COM+ is referred t o as COM+ 1.0. I n COM+ 1.5, Micr osoft im pr ov ed COM+ usabilit y in a num ber of ways and addressed som e of COM+ 1.0’s pit falls described in t his book . Microsoft also added new feat ures t o exist ing ser vices and laid t he foundat ion for int egr at ion w it h .NET services. COM+ 1.5 is fully back ward- com pat ible wit h COM+ 1.0 com ponent s and applicat ions. I n fact , when you export a COM+ 1.5 applicat ion, t he export wizard let s y ou export t he applicat ion in COM+ 1.0 form at t o be inst alled on m achines running COM+ 1.0 ( alt hough t he new feat ur es and propert ies will be lost in such an export ) . The COM+ Cat alog int er faces and collect ions have been ex tended t o handle t he new addit ions. When describing a new service, t he new corr esponding Cat alog it em s are prov ided whenever possible because no ot her public docum ent at ion is curr ent ly available.
B.1 I m pr oved User I n t e r f a ce Usa bilit y Under COM+ 1.0, t he only way t o k now t he act ivat ion t ype of a COM+ applicat ion was t o br ing up it s Act iv at ion t ab and exam ine it . The COM+ 1.5 Explorer assigns different icons t o different applicat ion t ypes, so y ou can deduce t he applicat ion’s t y pe ( serv er , librar y, or proxy) j ust by viewing it . Ser v ice applicat ions, ( discussed short ly ) , a fourt h applicat ion t ype av ailable in COM+ 1.5, also have a dist inct icon. A new folder under My Com put er called Running Processes cont ains all t he curr ent ly ex ecut ing applicat ions for easy runt im e adm inist r at ion.
B.2 Lega cy Applica t ion s a n d Com pon e n t s The COM+ 1.0 Explorer only allows you t o m anage configured com ponent s. I f your pr oduct is m ade up ent ir ely of configur ed com ponent s, t hen t hat lim it at ion is probably fine t o you. How ever, not all developers ar e t hat luck y. I n r eal life, configured com ponent s oft en have t o int eract wit h in- house or t hird- par t y legacy COM com ponent s. I n such a het er ogeneous env ironm ent , developer s use such t ools as DCOMCNFG, OLEView, Visual St udio, or cust om t ools
345
t o m anage legacy com ponent s in addit ion t o t he Com ponent Serv ices Explorer. Developers also have t o m anage t wo t ypes of deploy m ent approaches—one t hat uses export ed COM+ applicat ions ( MSI files) and anot her t hat is what ever t hey need t o inst all t he legacy com ponent s. One new feat ur e of COM+ 1.5 is com plet e suppor t for legacy applicat ions and com ponent s, which allows you t o m anage every aspect of your legacy applicat ions and com ponent s j ust as well as DCOMCNFG and OLEView do. B.2 .1 Le ga cy Applica t ion s I n t he COM+ 1.5 Explorer, under t he My Com put er icon, t here is a new folder called DCOM Config. This folder is a sibling t o t he COM+ Applicat ions folder ( see Figur e B- 1) . Figu r e B- 1 . Th e COM + 1 .5 Ex plor er
The DCOM Config folder cont ains all t he regist er ed COM local server s ( EXE ser vers) on y our m achine. Each local ser ver is called a legacy applicat ion. Unlike a COM+ applicat ion, y ou cannot expand a legacy applicat ion down t o t he com ponent , int er face, or m et hod lev el. A legacy applicat ion is opaque as far as COM+ 1.5 is concer ned. The DCOM Config folder sim ply gives y ou a cent ralized place t o m anage bot h your COM+ applicat ions and your legacy local server s wit hout resort ing t o ot her ut ilit ies. When y ou right - click on a legacy applicat ion and select Pr opert ies fr om t he pop- up cont ex t m enu, y ou get a proper t ies page t hat let s y ou m anage ev er y aspect of t he legacy applicat ion, m uch like w hat DCOMCNFG pr ov ides ( see Figure B- 2) . 346
Figu re B- 2 . Th e pr op ert ie s p ag e of a lega cy ap plica t ion
The General t ab let s y ou change t he applicat ion nam e and set t he aut hent icat ion lev el for incom ing calls t o t his applicat ion. The Locat ion t ab let s you cont rol whet her t o run t he applicat ion on your com put er or on anot her com put er on t he net work. The Endpoint s t ab let s you configure t he t ransport prot ocols for t he DCOM calls. The I dent it y t ab let s you specify under which secur it y ident it y t o launch t he ser ver , including t he syst em account ( for services only) . The Securit y t ab let s you configure user s’ access, launch, and change per m issions. COM+ 1.5 defines a new t op- lev el cat alog collect ion called LegacyServers. Every cat alog obj ect in t hat collect ion cor r esponds t o a local ser ver and provides t he m ain Regist ry ent ries ( CLSID, ProgID, ClassName, LocalServer32, and InprocServer32) as nam ed propert ies. B.2 .2 Le ga cy Com pon e n t s COM+ 1.5 calls nonconfigur ed in- pr oc COM com ponent s legacy com ponent s. I f your COM+ applicat ion uses legacy com ponent s, t he COM+ 1.5 Explor er let s you m anage t hem wit hin t he scope of y our applicat ion as well. Ev ery COM+ 1.5 applicat ion has a new folder called Legacy Com ponent s ( see Figur e B- 1) . The Legacy Com ponent s folder is a sibling t o t he Com ponent s folder. To add a legacy com ponent t o t he folder, ex pand it , right - click on it , and select New from t he cont ex t m enu. The COM+ 1.5 Explor er brings up t he Legacy Com ponent I m port Wizar d. The wizar d let s you choose legacy com ponent s ( regist ered in- proc com ponent s) t o add t o y our applicat ion. Like configured com ponent s, legacy com ponent s can t ake par t in at m ost one COM+ 1.5 applicat ion. The m aj or benefit of hav ing y our legacy com ponent s as part of your COM+ 1.5 applicat ion is deploym ent . When you expor t a COM+ applicat ion, it s MSI file cont ains t he legacy com ponent s and t heir set t ings. When you inst all t he MSI file on anot her m achine, t he Window s I nst aller
347
regist ers t he com ponent s, t hus sav ing you t he t r ouble of wr it ing a separ at e inst allat ion program . The propert ies page of a legacy com ponent present s y ou wit h every relev ant Regist r y ent r y for t hat com ponent ( see Figure B- 3) . Figu re B- 3 . Th e pr opert ie s p a ge of a lega cy com p on en t
You can change only t he v alues of set t ings t hat do not collide wit h regist ry set t ings in t he com ponent it self. For ex am ple, you cannot change t he t hreading m odel v alue, but you can pr ov ide t he nam e of a surr ogat e process. You can even pr om ot e a legacy com ponent t o a configur ed com ponent . Sim ply br ing up t he legacy com ponent ’s cont ext m enu and select Pr om ot e ( see Figure B- 4) . The legacy com ponent is rem oved from t he Legacy Com ponent s folder and added t o t he Com ponent s folder in t he sam e COM+ 1.5 applicat ion. Figu r e B- 4 . A le ga cy com pon e n t p op - u p con t e x t m e n u
348
The COM+ 1.5 Cat alog r oot obj ect ( COMAdminCatalog) support s a new int erface called ICOMAdminCatalog2, which derives fr om ICOMAdminCatalog. ICOMAdminCatalog2 cont ains t he following m et hods for handling legacy com ponent s: [id(0x2b)] HRESULT ImportComponentAsLegacy([in]BSTR bstrAppIdOrName, [in]BSTR bstrCLSIDOrProgId, [in]long lComponentType); [id(0x2c)] HRESULT PromoteLegacyComponent([in] BSTR bstrAppIdOrName, [in] BSTR bstrCLSIDOrProgId); ImportComponentAsLegacy( ) adds a legacy com ponent t o t he specified applicat ion and PromoteLegacyComponent( ) pr om ot es an alr eady im por t ed legacy com ponent t o a configur ed com ponent . I n addit ion, ev ery applicat ion in t he COM+ 1.5 Cat alog has a LegacyComponents collect ion. You can t r averse t his collect ion program m at ically and configure it .
B.3 D isa blin g Applica t ion s a n d Com pon e n t s The COM+ 1.5 Explorer let s you disable applicat ions and com ponent s. When y ou disable an applicat ion, all client at t em pt s t o
349
cr eat e any com ponent from t hat applicat ion fail, and t he following m essage is associat ed wit h t he HRESULT: " The com ponent has been disabled." To disable an applicat ion, display it s pop- up cont ex t m enu and select Disable. A disabled applicat ion has a r ed square on it ( lik e a play er ’s St op but t on) in t he COM+ 1.5 Explorer ( see Figur e B- 5) . To enable a disabled applicat ion, br ing up t he cont ext m enu again and select Enable. You can only disable a COM+ 1.5 applicat ion. Legacy applicat ions cannot be disabled. I nt erest ingly , a client t hat already has a r efer ence t o a COM+ obj ect is not affect ed by t he disabled applicat ion. Only client s t hat t ry t o cr eat e new obj ect s ar e affect ed. Consequent ly , y ou can hav e a disabled applicat ion running indefinit ely . Figu r e B- 5 . D isab lin g or e n a bling a COM + 1 .5 ap plica t ion from it s p op - u p con t ex t m e n u
You can also disable on a com ponent - by- com ponent basis inst ead of disabling an ent ire applicat ion. Every com ponent pop- up cont ext m enu has a Disable opt ion. Like a disabled applicat ion, a disabled com ponent has a red square on it . All client at t em pt s t o cr eat e a disabled com ponent fail, and t he following m essage is associat ed wit h t he HRESULT: " The com ponent has been disabled." You can disable any com ponent in a COM+ 1.5 applicat ion, including legacy com ponent s ( see Figure B- 4) . To enable a com ponent , select Enable fr om it s cont ext m enu. Lik e a disabled applicat ion, a disabled com ponent only affect s new act ivat ion r equest s. Ex ist ing r efer ences t o obj ect s are not affect ed. Enabled st at us for applicat ions and com ponent s is st ored in t he COM+ Cat alog and is t herefore m aint ained bet ween m achine reboot s. 350
Disabling applicat ions or com ponent s is useful in t wo cases. The first sit uat ion is when y ou want t o gracefully shut down an applicat ion on a liv e ser ver m achine t o perform m aint enance or upgr ades. I f you sim ply shut down t he applicat ion, you m ight cause failures on client m achines holding ex ist ing r efer ences. By disabling an applicat ion, y ou can have ex ist ing client s finish t heir work , while new act iv at ions m ay be rout ed t o anot her m achine, pr oviding you t he opport unit y t o per form m aint enance. The ot her sit uat ion in which disabling an applicat ion is useful is during developm ent and t est ing. I t provides a guarant eed way t o fail client calls and is t hus a way t o t est your client - side error handling. Current ly, t he COM+ Cat alog int er face ICOMAdminCatalog2 does not hav e m et hods used t o program m at ically disable or enable an applicat ion, but t hat sit uat ion could change by release t im e. Anot her possibilit y is t hat every COM+ applicat ion cat alog obj ect will hav e an Enabled nam ed proper t y . Cur rent ly, an applicat ion obj ect has a Boolean proper ty called IsEnabled t hat is set t o TRUE if t he applicat ion is enabled and FALSE if it is disabled. Sim ilarit y, com ponent s t oday do not have an Enabled nam ed pr opert y, only a Boolean proper ty called IsEnabled, used t he sam e way as in t he applicat ion obj ect .
B.4 Pa u sin g Applica t ion s Pausing an applicat ion is sim ilar t o disabling an applicat ion, except it is used t o disable a part icular r unning applicat ion only, and t he paused st at us does not sur vive an applicat ion shut down. To pause a running applicat ion, open t he Running Pr ocesses folder and select Pause from t he applicat ion cont ex t m enu. A paused applicat ion has a paused icon on it ( lik e a play er’s Pause but t on) , while a r unning applicat ion has a play icon ( like a player ’s Play but t on) . To r esum e a paused applicat ion, select Resum e fr om it s cont ex t m enu ( see Figure B- 6) . Figu r e B- 6 . PCOM + 1 .5 ru nn ing a pp lica t ion Act iva t ion t a b
351
To pause an applicat ion pr ogr am m at ically, use t he ICOMAdminCatalog2 int erface and t he PauseProcess( ) m et hod. The ResumeProcess( ) m et hod is used t o r esum e t he applicat ion, and t he IsProcessPaused( ) m et hod allows you t o find out t he st at us of t he applicat ion. The definit ions of t hese m et hods follow: [id(0x1c)] HRESULT PauseProcess([in] BSTR bstrApplInstanceId); [id(0x1d)] HRESULT ResumeProcess([in] BSTR bstrApplInstanceId); [id(0x1e)] HRESULT IsProcessPaused([in] BSTR bstrApplInstanceId, [out,retval]VARIANT_BOOL* bPaused);
B.5 Se r v ice Act iva t ion Type COM+ 1.5 allows you t o configure a ser ver applicat ion t o r un as a sy st em service. Configur ing your applicat ion as a serv ice allows you t o have your applicat ion running as soon as t he m achine boot s, independent of client act iv at ion request s. Anot her benefit is t hat a serv ice applicat ion is t he only way t o run under t he syst em ident it y account . The syst em account is t he m ost powerful account on a giv en m achine. The applicat ion Act iv at ion t ab cont ains t he check box " Run applicat ion as NT Service" ( see Figure B- 7) . When t his opt ion is select ed, y ou can also configure t he various serv ice param et ers by clicking t he Set up New Serv ice but t on, saving you t he t r ouble of using t he Cont rol Panel ser vices applet . Figu re B- 7 . COM + 1 .5 ap plica t ion Act iva t ion t a b
352
ICOMAdminCatalog2 pr ov ides y ou wit h program m at ic abilit y t o configure a serv ice wit h t he CreateServiceForApplication( ) m et hod and t o unconfigure a server applicat ion as a sy st em serv ice wit h t he DeleteServiceForApplication( ) m et hod. The service nam e is av ailable t hrough t he ServiceName proper t y of t he applicat ion’s cat alog obj ect .
B.6 I m pr oved Qu eu ing Suppor t As explained in Chapt er 8, queued com ponent s under COM+ 1.0 requir e t he pr esence of a dom ain cont r oller t o pr ov ide aut hent icat ion for t he queued call. I f you do not have a dom ain cont roller, you m ust t ur n off COM+ 1.0 applicat ion aut hent icat ion ( set it t o None) . COM+ 1.5 pr ovides bet t er configur able support for queued calls by separat ing t hem from nor m al sy nchr onous calls. The applicat ion Queuing t ab now let s y ou configur e aut hent icat ion for queued calls explicit ly ( see Figur e B- 8) . Your available opt ions are t o: •
•
Use MSMQ dom ain cont roller aut hent icat ion when t he applicat ion is configur ed t o use aut hent icat ion for sy nchr onous calls ( w hen t he applicat ion aut hent icat ion is set t o any v alue except None) . Nev er aut hent icat e queued calls int o t his applicat ion. Choosing t his opt ion allows y ou t o use queued com ponent s fr eely wit hout a dom ain cont roller. 353
•
Alway s aut hent icat e incom ing queued calls, regardless of t he applicat ion aut hent icat ion set t ing. Figu r e B- 8 . COM + 1 .5 se rve r a p plicat ion Qu e u in g t a b
The Queuing t ab also allows you t o cont r ol t he m ax im um num ber of concurr ent players t he applicat ion can cont ain. Because ev ery player is creat ed on a separat e t hread, som e overhead is associat ed wit h creat ing and m aint aining a player . I n ext rem e sit uat ions, your applicat ion m ay grind t o a halt if t he num ber of concur rent play er s is t oo large ( a few hundr ed) . When you set a lim it on t he num ber of player s and t hat lim it is reached, t he list ener does not cr eat e new player s. Rat her, queued calls r em ain in t he applicat ion queue, allowing calls in pr ogr ess t o execut e and com plet e. The lim it is also good for load- balancing purposes and can be used in conj unct ion wit h applicat ion pooling, discussed nex t . The COM+ 1.5 Cat alog let s you configure t he queuing suppor t program m at ically as nam ed pr opert ies of t he applicat ion cat alog obj ect . The aut hent icat ion lev el is accessible via t he QCAuthenticateMsgs nam ed pr opert y, and t he m ax im um num ber of players is accessible v ia t he QCListenerMaxThreads proper ty .
B.7 Applica t ion Poolin g a n d Recyclin g COM+ 1.5 provides t wo new applicat ion lifecycle m anagem ent opt ions: applicat ion pooling and recycling. Bot h opt ions ar e
354
configurable on a new t ab on t he applicat ion’s pr opert ies page. Pooling and recy cling serv ices are available only for a server applicat ion. You cannot configur e library applicat ions t o use pooling and r ecycling because t hey do not own t heir host ing pr ocess. Library applicat ions hav e, in effect , t he pooling and recycling par am et ers of what ever server applicat ion happens t o load t hem . B.7 .1 Applica t ion pooling Applicat ion pooling allows you t o configure how m any sur rogat e processes ar e launched t o host your server applicat ion’s com ponent s. Under COM+ 1.0, all inst ances of com ponent s fr om a server applicat ion always share t he sam e host ing pr ocess. Alt hough t his sharing is also t he classic COM default , classic COM local server dev eloper s had t he opt ion of allocat ing a pr ocess per obj ect ( by regist ering t he class fact ories wit h t he REGCLS_SINGLEUSE flag) . COM+ 1.5 giv es you ex plicit cont rol over how m any processes ar e launched by configur ing a processes pool. The applicat ion pr opert ies page now cont ains t he Pooling & Recy cling t ab ( see Figure B- 9) . You can configure t he pool size in t he Pool size edit box. The default pool size is one—a single pr ocess host s all inst ances of com ponent s fr om t he applicat ion, lik e in COM+ 1.0. However, if you set it t o a value great er t han one, COM+ 1.5 cr eat es a process per each new inst ance unt il it reaches t he pool size, at which point COM+ st ar t s m ult iplex ing new inst ances t o t he ex ist ing processes, apparent ly in a round- robin fashion. The m ax im um configurable pool size is 999,999, enough for all pract ical pur poses. Applicat ion pooling is useful as a fault isolat ion t echnique. I f one process has t o shut down because of som e err or or except ion, t he ot her pr ocesses and t heir client s are not affect ed. Applicat ion pooling also giv es you sam e- m achine load balancing—you do not have t o use a separat e load balancing ser vice wit h m ult iple m achines t o allocat e differ ent inst ances t o different processes. The pool size is available as t he ConcurrentApps nam ed propert y of an applicat ion cat alog obj ect . I f you st ar t a COM+ 1.5 applicat ion m anually or program m at ically , COM+ 1.5 cr eat es as m any processes as t he configured pool size. This behavior is analogous t o com ponent m inim um pool size, discussed in Chapt er 3, and it only com es int o play when t he applicat ion is st ar t ed explicit ly. This behavior is useful w hen you want t o m it igat e ant icipat ed spikes in client r equest s—you shouldn't pay t he overhead of cr eat ing new processes ( and pot ent ially , pools of obj ect s in t hose processes as well) .
355
Figu re B- 9 . COM + 1 .5 pr ovid e s se rve r a p plicat ion s w it h pooling a n d re cyclin g ser vice s
B.7 .2 Applica t ion Re cyclin g The ot her new applicat ion lifet im e m anagem ent service is recy cling. Applicat ion r ecy cling is used t o increase over all applicat ion robust ness and availabilit y by com pensat ing for code defect s. For exam ple, one of t he m ost com m on defect s is a m em ory leak. Not all product s have t he necessary qualit y assurance resources or com m it m ent during developm ent ; as a result , m em or y leaks can be present in t he r eleased product . An issue arises when a COM+ applicat ion can be left running indefinit ely serv icing client s. Ev en a ver y sm all m em or y leak can have a devast at ing effect over a long per iod of t im e. For exam ple, im agine a syst em wit h an " insignificant " m em ory leak of only 10 by t es per m et hod call. A m odern sy st em t hat processes in excess of 100 t r ansact ions per second will, aft er one day , leak 100 MB of m em ory. The pr ocess host ing t he applicat ion will consum e t his am ount of m em ory , t hus severely ham pering per for m ance and availabilit y, as a r esult of addit ional page fault s and m em or y allocat ion penalt ies. The way dev eloper s t r eat ed such a leak in COM+ 1.0 ( ot her t han fixing it ) was by periodically t er m inat ing t he host ing pr ocess and rest art ing it . This t echnique is called applicat ion r ecycling. COM+ 1.5 allows you t o configur e aut om at ic r ecycling on t he Pooling and Recycling t ab ( see Figure B- 9) . You can have COM+ recy cle t he process when it r eaches a m em or y lim it ( t he Mem or y Lim it edit box) t o cope w it h m em ory leaks. A v alue of zero is t he default value, which m eans no lim it .
356
By specify ing t he Lifet im e Lim it value, y ou can also inst r uct COM+ t o shut down your applicat ion aft er a predet er m ined am ount of t im e. This inst ruct ion allows you t o cope wit h defect s in handling ot her k inds of resources ( such as syst em handles) by specify ing t he Lifet im e Lim it v alue. A value of zero is t he default value, which m eans no lifet im e lim it . Not e t hat t he sem ant ics of t he lifet im e lim it is differ ent from t he idle t im e m anagem ent opt ion on t he applicat ion Advanced t ab. The Ser ver Pr ocess Shut down value on t he Adv anced t ab specifies aft er how m any m inut es of idle t im e ( i.e., not serv icing client s) t o shut down t he applicat ion. The lifet im e value specifies aft er how m any m inut es t o shut down t he applicat ion, irr espect iv e of t he wor k in progress inside t he process. COM+ provides t w o m or e r ecycling t riggers. You can hav e COM+ recy cle y our applicat ion aft er a specified num ber of m et hod calls int o your applicat ion by specifying such a lim it in t he Call Lim it edit box . The num ber of calls is defined as com bined num ber of calls m ade on all obj ect s in t he applicat ion. The default value is set t o zero—no lim it . You can also request applicat ion r ecycling aft er a cert ain num ber of act ivat ions. Act ivat ions is defined as t he t ot al num ber of obj ect s t hat COM+ 1.5 cr eat ed in t hat applicat ion. You specify t he act ivat ion lim it in t he Act iv at ion Lim it edit box and, again, t he default value is set t o zero. Regardless of how t he decision t o recycle t he applicat ion is m ade ( t he m em ory lim it r eached, t he lifet im e elapsed, or t he call or act ivat ion lim it was reached) , COM+ 1.5 r out es new act ivat ion request s t o a new host process and wait s for ex ist ing client s t o release t heir references t o obj ect s in t he recy cled process. However, you can specify how long COM+ 1.5 should wait in t he Expirat ion Tim eout edit box. Aft er t hat expirat ion t im eout , COM+ 1.5 t erm inat es t he applicat ion, even if client s are st ill holding live references. The default expirat ion t im eout is 15 m inut es. Finally, not e t hat r ecycling is not available for a COM+ applicat ion configured as syst em ser vice, nor can you recycle a paused applicat ion. B.7 .3 Pr ogr a m m a t ic Re cycling The COM+ 1.5 Cat alog provides you wit h pr ogr am m at ic abilit y t o configure t he recy cling par am et ers discussed previously. To configure m em ory and t im e- bound recycling, use t he RecycleMemoryLimit and RecycleLifetimeLimit nam ed proper t ies of t he applicat ion's cat alog obj ect . To configur e t he expirat ion t im eout , use t he RecycleExpirationTimeout nam ed proper t y . To configure call or act iv at ion lim it program m at ically , set t he v alues of t he RecycleCallLimit or RecycleActivationLimit nam ed proper t ies.
357
Exam ple B- 1 shows how t o set a recy cling lim it program m at ically. I t im plem ent s t he SetRecycleByActivations( ) helper funct ion, which set s a specified lim it of act ivat ions for recy cling a specified applicat ion. Ex a m p le B- 1 . Se t t in g a re cyclin g lim it pr ogr am m at ica lly
//usage: "MyApp" will be recycled after 1000 object activations //hres = SetRecycleByActivations("MyApp",1000); HRESULT SetRecycleByActivations(LPCSTR lpcszAppName,DWORD dwActivations) { //Verify app name is valid if(_bstr_t(lpcszAppName) == _bstr_t("")) { return E_INVALIDARG; } HRESULT hres = S_OK; ICOMAdminCatalog2* pCatalog = NULL; hres = ::CoCreateInstance(CLSID_COMAdminCatalog, NULL,CLSCTX_SERVER, IID_ICOMAdminCatalog2,(void**)&pCatalog); ICatalogObject* pApplication = NULL; ICatalogCollection* pApplicationCollection = NULL; long nApplicationCount = 0; int i = 0;//Application index //Get the application collection hres = pCatalog>GetCollection(_bstr_t("Applications"), (IDispatch**)&pApplicationCollection); pCatalog->Release( ); hres = pApplicationCollection->Populate(
);
hres = pApplicationCollection>get_Count(&nApplicationCount); hres = COMADMIN_E_OBJECT_DOES_NOT_EXIST; for(i=0;iget_Item(i,(IDispatch**)&pApplication); _variant_t varName; 358
pApplication->get_Name(&varName); _bstr_t bstrName(varName); if(bstrName == _bstr_t(lpcszAppName)) { long ret = 0; _variant_t varActivationLimit((long)dwActivations); hres = pApplication>put_Value(_bstr_t("RecycleActivationLimit"), varActivationLimit); hres = pApplicationCollection>SaveChanges(&ret); } pApplication->Release( ); } pApplicationCollection->Release( ); return hres; }
B.8 Applica t ion D u m p For debug and analy sis purposes, get t ing a com plet e m em or y dum p of an applicat ion is som et im es useful, especially at t he t im e of a cr ash. COM+ 1.5 allows y ou t o configure a dum p of a st at ic m em or y im age of t he process host ing y our COM+ applicat ion. You can use a ut ilit y such as WinDbg t o view and analy ze t his im age. Ev er y COM+ 1.5 applicat ion ( including libr ary applicat ions) has a new t ab on it s proper t ies page called Dum p ( see Figure B- 10) . You can specify a locat ion for t he im age dum p and how m any im ages t o st ore t here. When t he m axim um num ber of im ages is reached, a new dum p im age over writ es t he oldest one. The m ax im um num ber of im ages you can hav e COM+ 1.5 st or e for y ou is 200. You can generat e a dum p file in several way s. The fir st ( and m ost useful) w ay is t o inst ruct COM+ t o dum p a m em ory im age on applicat ion fault ( at t he bot t om of t he Dum p t ab—see Figur e B- 10) . I n t his cont ext , an applicat ion's fault is when an ex cept ion is t hrown. Figu r e B- 1 0 . COM + 1 .5 a pplica t ion D u m p t ab
359
The second way t o gener at e a dum p file is t o select Dum p fr om a running applicat ion cont ex t m enu ( see Figur e B- 6) . Finally, you can also r equest a dum p explicit ly by using t he DumpProcess( ) m et hod of t he ICOMAdminCatalog2 int er face, defined as: [id(0x1f)] HRESULT DumpProcess([in] BSTR bstrApplInstanceId, [in] BSTR bstrDirectory, [in] long lMaxImages, [out,retval] BSTR* pbstrDumpFile); When y ou use t he DumpProcess( ) m et hod, you have t o prov ide t he dum p direct ory and filenam e and y ou cannot rely on t he configured values. Request ing a dum p ( eit her by calling DumpProcess( ) or select ing Dum p from t he cont ex t m enu) on a running applicat ion is nonint rusive—t he process can cont inue t o run and is only frozen t em por ar ily for t he durat ion of t he dum p. When COM+ generat es a dum ped file, it uses t he following nam ing convent ion as a filenam e: {< App-ID>}_year_month_day_hour_minute_second.dmp This convent ion let s y ou easily associat e a dum p file wit h a r eport ed sy st em failur e. For ex am ple, here is a t ypical dum p filenam e: {02d4b3f1-fd88-11d1-960d00805fc79235}_2001_06_14_13_28_51.dmp To avoid calling DumpProcess( ) needlessly, ICOMAdminCatalog2 has a helper m et hod called IsProcessDumpSupported( ), used t o find out whet her im age dum p is enabled on t he m achine:
360
[id(0x20)]HRESULT IsProcessDumpSupported([out,retval]VARIANT_BOOL* pbSupported); You can set t he var ious dum p proper t ies program m at ically as well, using nam ed propert ies of t he applicat ion cat alog obj ect . The DumpEnabled propert y let s y ou enable or disable im age dum p for t he applicat ion, DumpOnException let s you request a dum p on except ions, MaxDumpCount let s you configure t he m axim um num ber of dum ped files, and DumpPath let s you specify where t o sav e t he dum ped im age files.
B.9 Applica t ion Pa r t it ion in g Applicat ion part it ioning is an int ricat e new service aim ed t o r efine and im prove m anagem ent of COM+ applicat ions in a large- scale env ironm ent . An in- dept h discussion of applicat ion part it ions is bey ond t he scope of t his appendix and requir es an under st anding of Act iv e Direct ory. I nst ead, t his appendix provides a sim plified overv iew of t he par tit ion concept . An applicat ion par t it ion is a group of COM+ 1.5 applicat ions. Par t it ions pr ov ide you wit h an econom ic way t o present each user ( be it a logged- on user or a call com ing in across t he net work) wit h it s own set of applicat ions and com ponent s. Part it ions are usually configured in Act ive Dir ect or y. Under COM+ 1.0, a com ponent can belong t o only one COM+ applicat ion on a giv en m achine. I f you want t o inst all t he sam e com ponent ( sam e CLSI D) in m ult iple applicat ions, you have t o do so on m ult iple m achines. COM+ part it ions allow you t o inst all t he sam e com ponent in m ore t han one applicat ion, provided t he applicat ions belong t o differ ent par t it ions. A giv en m achine can cont ain m ult iple part it ions, and each par t it ion can hav e it s own set of applicat ions and com ponent s. You can assign user s t o part it ions in t he Act ive Dir ect or y. COM+ 1.5 also defines a base part it ion—a par t it ion t hat all user s share. When a user t r ies t o creat e a com ponent , COM+ first looks in t he par t it ion t he user is associat ed wit h. I f t hat par t it ion has t hat com ponent , t hen COM+ creat es it . I f it does not , COM+ look s in t he base par t it ion; if it is found in t he base part it ion, COM+ creat es it . I f t he base par t it ion does not cont ain t he com ponent , t hen t he cr eat ion fails, even if t he com ponent is part of anot her part it ion. For ex am ple, consider t he par t it ion lay out in Figur e B- 11. I f a user associat ed wit h Part it ion A only t r ies t o cr eat e t he com ponent wit h CLSI D1, t hat com ponent is creat ed from Part it ion A; t he configurat ion set t ings of App1 in Part it ion A and any com ponent lev el configurat ion ar e applied. However, if t he user t r ies t o creat e
361
t he com ponent wit h CLSI D3, t he com ponent from t he base par t it ion is creat ed and t he base par t it ion set t ings are applied. I f t he user t r ies t o creat e wit h CLSI D7, t he cr eat ion fails. Figu re B- 1 1 . Con figu ring m u lt ip le se t s of ap plica t ion s on t h e sa m e m a ch in e u sing p ar t it ion s
Figure B- 11 dem onst rat es som e ot her point s. A giv en CLSI D can belong t o m ore t han one par t it ion, but a given par t it ion can hav e only one copy of t he com ponent . Different par t it ions can cont ain applicat ions wit h t he sam e nam e. The differ ent part it ions inherit t he base part it ion’s applicat ions and com ponent s, but t hey can over ride t hem , rem ove t hem , add new com ponent s, and change t heir set t ings. Applicat ion part it ions provide an easier w ay t o m anage act iv at ions and isolat e applicat ions bet ween part it ions. Each part it ion can be m anaged independent ly of t he ot hers, and you can ev en inst all differ ent versions of t he sam e com ponent in differ ent part it ions, t hus t ailor ing a part icular com pat ibilit y solut ion. Under COM+ 1.5, t he obj ect cont ex t has a part it ion proper t y . The cont ext obj ect suppor t s a new int erface called IObjectContextInfo2 t hat der ives from IObjectContextInfo, which enables you t o get inform at ion about t he part it ion and t he applicat ion t he obj ect is par t of. Client s can request t o creat e an obj ect in a par t icular par t it ion using a special m oniker. The ICOMAdminCatalog2 int erface provides you wit h num er ous m et hods for m anaging part it ions, including copying an applicat ion fr om one part it ion t o anot her, copy ing and m oving a com ponent fr om one part it ion t o anot her, get t ing t he base applicat ion part it ion I D, and get t ing t he curr ent par t it ion I D.
362
B.1 0 Alia sin g Com pon en t s Under COM+ 1.0 y ou cannot use t he sam e com ponent wit h m or e t han one set of configurat ions—like in classic COM, a com ponent is associat ed wit h j ust one CLSI D. COM+ 1.5 allows you t o alias an exist ing configur ed com ponent w it h a new CLSI D and apply a new set of configurat ions t o t he " new" com ponent . This process is called aliasing a com ponent . Aliasing is oft en a useful feat ure—you can dev elop a piece of business logic and assign m ore t han one set of configurat ion param et er s t o it by copy ing it as m any t im es as you lik e. The com ponent 's client can now decide which configur at ion set t ing and business logic im plem ent at ion t o inst ant iat e by creat ing a par t icular CLSI D. To alias a com ponent , select Alias fr om it s popup cont ex t m enu in t he Com ponent Ser vices Explor er . This select ion brings up t he Alias Com ponent dialog box ( see Figur e B- 12) Figu r e B- 1 2 . Alia sin g a com pon e n t
The dialog box let s y ou select a dest inat ion applicat ion for t he new com ponent . Because you are assigning a new CLSI D t o t he com ponent , you can ev en copy it back t o it s curr ent applicat ion. The dialog generat es t he new CLSI D for t he copy and a new pr ogI D ( ., see Figure B- 12) . You can prov ide your own v alues for t he CLSI D and prog- I D, if you like. I nit ially , t he new com ponent has configurat ion set t ings t hat are ident ical t o t he original com ponent . Once you copy a com ponent , t he original and t he clone ar e consider ed different com ponent s from t he COM+ point of view. You can configure t hem different ly, even t hough t he configurat ions apply t o t he sam e act ual com ponent at r unt im e. Copying com ponent s is also handy in t he case of event classes. As you m ay recall from Chapt er 9, you m ust supply COM+ wit h a sk elet al im plem ent at ion of an ev ent class ( st ub out all
363
im plem ent at ion of t he sink s) so t hat COM+ can synt hesize it s own im plem ent at ion of t he event class. You m ay oft en provide m ore t han one ev ent class so t hat som e subscriber s can subscribe t o one event class and som e t o anot her . Wit h com ponent copying, you only need t o pr ovide one, and t hen j ust copy it .
B.1 1 Con figu r a ble Tr a nsa ct ion I sola t ion Leve l COM+ 1.0 handles t ransact ion isolat ion v er y conserv at ively . COM+ 1.0 only allows t he highest level of isolat ion, an isolat ion level called serialized. Wit h ser ialized t r ansact ions, t he result s obt ained fr om a set of concurr ent t r ansact ions are ident ical t o t he r esult s obt ained by r unning each t r ansact ion ser ially . Such a high degr ee of isolat ion com es at t he expense of over all syst em t hr oughput ; t he resour ce m anager s inv olved have t o hold ont o bot h r ead and writ e locks for as long as a t ransact ion is in pr ogr ess, and all ot her t ransact ions are blocked. However, you m ay w ant t o t r ade syst em consist ency for t hroughput in som e sit uat ions by lower ing t he isolat ion level. I m agine, for exam ple, a bank ing sy st em . One of t he requir em ent s is t o ret riev e t he t ot al am ount of m oney in all cust om er account s com bined. Alt hough execut ing t hat t r ansact ion w it h t he serialized isolat ion lev el is possible, if t he bank has hundreds of t housands of account s, it m ay t ak e quit e a while t o com plet e. The t r ansact ion m ay t im e out and abort because som e account s m ay be accessed by ot her t ransact ions at t he sam e t im e. But t he num ber of account s could be a blessing in disguise. On av erage, st at ist ically speak ing, if t he t r ansact ion is allow ed t o run at a lower isolat ion lev el, it m ay get t he wrong balance on som e account s. However , t hose incorr ect balances would t end t o cancel each ot her out . The act ual result ing err or m ay be accept able for t he bank’s need. COM+ 1.5 allows you t o configure t he isolat ion lev el for a t r ansact ional com ponent . The Transact ions t ab has a dr op- down list box w it h five isolat ion lev els ( see Figur e B- 13) . The available isolat ion lev els ar e Any , Read Uncom m it t ed, Read Com m it t ed, Repeat able Read, and Serialized. The default is set t o Serialized. Figu re B- 1 3 . Se t t in g t r an sa ct ion isola t ion le vel for individu al com p on en t s
364
The under ly ing t ransact ion processing m onit or, t he DTC, support s ot her t r ansact ion isolat ion lev els besides Serialized, but COM+ 1.0 passes in a hard- coded isolat ion level of Serialized when it creat es a new DTC t r ansact ion. All COM+ 1.5 does t o expose t hese levels is pass t he configur ed isolat ion level, inst ead of t he or iginal har dcoded Serialized level in COM+ 1.0, t o t he DTC. Select ing an isolat ion level ot her t han Serialized is com m only used for r ead- int ensive syst em s. I t requir es a solid under st anding of t r ansact ion processing t heor y and t he sem ant ics of t he t ransact ion it self, t he concur rency issues inv olv ed, and t he consequences for sy st em consist ency. A good st ar t ing point is t he bible on t ransact ion processing: Transact ion Processing: Concept s and Technologies by Jim Gray and Andreas Reut er ( Mor gan Kaufm ann, 1992) . I n addit ion, not all r esour ce m anagers support all levels of isolat ion, and t hey m ay elect t o t ak e part in t he t ransact ion at a higher level t han t he one configured. Every isolat ion lev el besides Serialized is suscept ible t o som e sort of inconsist ency r esult ing from hav ing ot her t r ansact ions access t he sam e inform at ion. The differ ence bet ween t he four isolat ion lev els is in t he way t hey use read and writ e locks. A lock can be held only when t he t ransact ion accesses t he dat a in t he resour ce m anager, or it can be held unt il t he t r ansact ion is com m it t ed or abor t ed. The form er is bet t er for t hr oughput ; t he lat t er for consist ency . The t wo k inds of locks and t he t wo kinds of operat ions ( r ead/ wr it e) give four isolat ion levels. See a t r ansact ion- pr ocessing t ext book for a com prehensive descript ion of isolat ion lev els. I n a COM+ t r ansact ion, t he root does m ore t han j ust st ar t and end a t r ansact ion. I t also det er m ines t he isolat ion level for t he t r ansact ion. Once det erm ined, t he isolat ion lev el is fixed for t he life 365
of t he t ransact ion. A com ponent cannot t ake part in a t r ansact ion if t he isolat ion lev el it r equires is great er t han t hat of t he t ransact ion. Consequent ly , every com ponent in a t r ansact ion m ust hav e it s isolat ion lev el set t o a level equal t o or less t han t hat of t he root com ponent . I f a com ponent in a t ransact ion t r ies t o creat e anot her com ponent wit h a great er isolat ion level, COM+ 1.5 refuses t o cr eat e t he com ponent and CoCreateInstance( ) r et urns CO_E_ISOLEVELMISMATCH. When isolat ion is set t o Any , t he com ponent is indiffer ent t o t he isolat ion lev el of t he t r ansact ion it is par t of. I f t hat com ponent is not t he root of a t ransact ion, t hen it sim ply assum es t he isolat ion lev el of t he t r ansact ion it is part of when it accesses resource m anager s. I f t hat com ponent is t he r oot of a t ransact ion, t hen COM+ 1.5 decides on t he isolat ion lev el for it and uses Ser ialized. As a result , any com ponent w it h isolat ion set t o Ser ialized or Any can be t he root of a COM+ 1.5 t r ansact ion because by definit ion, all ot her com ponent s hav e isolat ion levels equal t o or less t han t hey do. Any ot her isolat ion level for a root m ay not guar ant ee successful act ivat ion of int ernal com ponent s. The COM+ 1.5 Explorer display s a war ning m essage when you change isolat ion level fr om Serialized or Any , w hich is alm ost cor rect in it s cont ent ( see Figur e B- 14) . Figu re B- 1 4 . W ar n in g m e ssa ge w h e n ch an ging t h e isola t ion le ve l fr om Se rializ e d or An y t o an ot h er le ve l
I t is possible for one com ponent t o call anot her com ponent wit h a higher configur ed isolat ion level, as long as t he t r ansact ion isolat ion is great er t han or equal t o t hat higher level. For exam ple, Com ponent R wit h isolat ion set t o Repeat able Read is t he root of a t r ansact ion, and it cr eat es t wo ot her com ponent s, A and B, wit h isolat ion lev els of Read Com m it t ed and Read Uncom m it t ed, respect ively. Com ponent B can call Com ponent A because t he isolat ion lev el of A and B is less t han t hat of t he root R. The correct warning m essage should read: " Changing t his level fr om Serialized or Any requires t hat when t his com ponent is t he root of a t r ansact ion, all com ponent s in t he t r ansact ion hav e an isolat ion lev el less t han or equal t o t he isolat ion level set for t his com ponent ." You can also set t he com ponent ’s isolat ion level progr am m at ically by set t ing t he TxIsolationLevel nam ed pr opert y of a com ponent cat alog obj ect .
366
N ET Se r vice d Com pone nt I sola t ion A .NET t ransact ional ser viced com ponent can declar e it s isolat ion lev el under COM+ 1.5 using t he Transaction at t r ibut e’s Isolation pr opert y: [Transaction(Isolation= TransactionIsolationLevel.Serializable)] public class MyComponent :ServicedComponent {} The Isolation propert y is of t he enum t ype TransactionIsolationLevel, defined as: public enum TransactionIsolationLevel { Any, ReadUncommitted, ReadCommitted, RepeatableRead, Serializable } The default v alue of t he TransactionIsolationLevel proper t y is TransactionIsolationLevel.Serializable.
B.1 2 I m pr ove d Con t e x t Act iva t ion Se t t in g As explained in Chapt er 3, configuring your com ponent t o use JI TA requir es hav ing it s own cont ex t . COM+ 1.0 let s y ou configur e y our com ponent t o use JI TA, and configure it t o r equire t hat t he com ponent alway s m ust be act ivat ed in it s cr eat or ’s cont ex t , by checking t he checkbox " Must be act ivat ed in caller’s cont ext " on t he com ponent ’s Act ivat ion t ab. ( As discussed in Chapt er 3, t his nam e is inaccurat e and should r ead " Must be act iv at ed in creat or’s cont ext ." ) These t wo set t ings are m ut ually exclusive. I f you configure a com ponent in t his way, all act iv at ions fail. You face a sim ilar pit fall when configur ing t he com ponent t o use t ransact ions or enforce secur it y access check s for t he com ponent —all requir e a privat e cont ext . The COM+ 1.5 Explor er r em edies t his sit uat ion by redesigning t he com ponent Act ivat ion t ab ( see Figur e B- 15) and adding a new act iv at ion opt ion. The Act ivat ion Cont ext proper t ies group cont ains t hree radio but t ons. You can select only one of t he but t ons at a t im e—t hus enforcing m ut ual ex clusion. I f you select " Don't force act ivat ion cont ext ," you act ually select t he regular COM+ cont ext act iv at ion behavior. I n t his m ode, you can enable JI TA, t ransact ions, and secur it y access checks. I n fact , as long as t r ansact ion support or access securit y ar e enabled, you cannot
367
select anot her opt ion; enabling securit y checks set s t he select ion back t o " Don’t force act ivat ion cont ex t " fr om any ot her set t ing. COM+ 1.5 adds a new cont ext act ivat ion select ion—" Must be act ivat ed in t he default cont ext ." This new opt ion can be useful when y ou k now t hat client s of t he com ponent r eside in t he default cont ext and m ak e frequent calls of shor t dur at ion t o y our com ponent , and t hat y our com ponent does not use m ost of t he COM+ serv ices. Figu r e B- 1 5 . Th e n ew COM + 1 .5 com pon e nt Act iva t ion t a b
B.1 3 Pr iva t e Com pon en t s COM+ 1.5 provides a new feat ur e called privat e com ponent s. Every com ponent has, at t he bot t om of it s act ivat ion t ab, t he " Mar k com ponent privat e t o applicat ion" checkbox ( see Figur e B- 15) . A privat e com ponent can only be accessed by ot her com ponent s in t he sam e applicat ion. Privat e com ponent s ar e needed in alm ost every decent size COM+ proj ect . To prom ot e loose coupling bet ween client s and obj ect s, y ou should avoid pr oviding t he client s wit h access t o t he int ernal obj ect s by m ar king t hem as priv at e.
.N ET Pr iva t e Se r vice d Com pone nt A .NET t ransact ional ser viced com ponent can declar e it self as a privat e com ponent , using t he Pr ivat eCom ponent at t ribut e:
368
[PrivateComponent] public class MyComponent :ServicedComponent {} Not e t hat a priv at e com ponent is different from an int ernal com ponent . Declar ing t he class as int er nal inst ead of public prevent s access t o it from out side it s assem bly. A pr ivat e com ponent cannot be accessed by client s out side it s COM+ applicat ion, but it can be accessed by ot her client s in t he sam e applicat ion, including com ponent s from ot her assem blies.
B.1 4 W e b Se r v ices in COM+ 1 .5 Web services support is t he m ost ex cit ing new feat ur e of t he .NET plat for m . As explained in Chapt er 10, web serv ices allow a m iddlet ier com ponent in one web sit e t o inv oke m et hods on anot her m iddle- t ier com ponent at anot her web sit e, wit h t he sam e ease as if t he t wo com ponent s wer e on t he sam e sit e and m achine. But .NET web services com e w it h a pr ice—com panies have t o invest in learning new languages such as C# and cope wit h a new program m ing m odel and new class libraries. I n m ost or ganizat ions, t his cost is subst ant ial. To preserve exist ing inv est m ent in COM com ponent s and dev elopm ent expert ise, while providing a m igrat ion pat h t o t he .NET wor ld, COM+ 1.5 can expose any COM+ com ponent t hat com plies wit h w eb ser vices design guidelines as a web service. The applicat ion act iv at ion t ab let s you configure SOAP act ivat ion for your applicat ion ( see Figure B- 7) . All y ou need t o do is specify t he vir t ual dir ect ory of t he web serv ice associat ed wit h t his applicat ion, and COM+ provides t he necessary adapt ors as a com ponent service. Each com ponent is exposed as a separat e web serv ice, ident ified by t he com ponent prog- I D under t he v irt ual dir ect ory . COM+ inst alls t he web services wit h I I S and generat es t he proper web service configur at ion and infor m at ion files. Not e t hat I I S and .NET m ust be inst alled on t he serv er and client m achine t o enable t he SOAP act iv at ion m ode for y our applicat ion.
B.1 5 Su m m a r y COM+ is essent ial for rapid com ponent dev elopm ent and r obust , scalable applicat ions. COM+ 1.5 sm oot hes COM+ 1.0's rough edges, and it s new feat ur es ar e a m ost welcom e addit ion t o y our dev elopm ent arsenal. Fut ure r eleases of COM+ will m ost lik ely int roduce new feat ur es and com ponent ser vices, probably t o
369
com plem ent new capabilit ies available wit h .NET ( such as t he web serv ices support ) and im prove t he int egr at ion bet ween t he t wo. Especially not ewort hy is COM+ 1.5’s support for legacy com ponent s and applicat ions. The m essage is clear: use COM+ as a support ing com ponent services plat for m and unify in t he sam e ar chit ect ure all your com ponent s—fr om classic COM com ponent s, t o COM+ configured com ponent s, t o .NET serv iced com ponent s. As m ent ioned at t he beginning of t he book, COM+ offers a m igr at ion pat h for com panies and developers. Com panies can st ar t ( or cont inue) t heir pr oj ect s in COM, using COM+ for com ponent serv ices. When t he t im e com es t o m ov e t o .NET, t hey can st ar t plugging int o t he sam e ar chit ect ure .NET serv iced com ponent s in a seam less m anner , r eusing and int er act ing wit h t heir exist ing COM and COM+ configur ed com ponent s.
370
Appe ndix C. I nt r oduct ion t o .N ET .NET is based on a Com m on Language Runt im e ( CLR) environm ent t hat m anages every runt im e aspect of your code. All .NET com ponent s, regardless of t he language in which t hey are dev eloped, execut e in t he sam e r unt im e ( hence t he nam e) . The CLR is lik e a warm blanket t hat surr ounds your code, providing it wit h m em ory m anagem ent , a secur e env ir onm ent t o run in, obj ect locat ion t ranspar ency, concurr ency m anagem ent , and access t o t he underly ing operat ing syst em services. Because t he CLR m anages t hese aspect s of y our obj ect ’s behavior, code t hat t arget s t he CLR is called m anaged code. The CLR provides absolut e language int er operabilit y , allowing a high degree of com ponent int er operabilit y . COM, t oo, prov ides language independence, allowing binary com ponent s developed in t wo differ ent languages ( such as Visual Basic and C+ + ) t o call one anot her’s m et hods, but COM language int er operabilit y is only at r unt im e. Dur ing dev elopm ent , .NET allow s a com ponent developed in one language t o derive from a com ponent developed in anot her language seam lessly . .NET is capable of t his pr ocess because t he CLR is based on a st rict t ype syst em . To qualify as a .NET language, all const ruct s ( such as class, st ruct , or pr im it ive t ypes) in every language m ust com pile t o CLR- com pat ible t ypes. The language int er operabilit y gain is at t he expense of exist ing languages and com piler s. Exist ing com piler s produce CLR- ignorant code—code t hat does not com ply wit h t he CLR t ype syst em and t hat is not m anaged by t he CLR. Visual St udio.NET com es wit h four CLR- com pliant languages: C# , Visual Basic.NET, JScript .NET, and Managed C+ + . Thir d- part y com piler vendor s also t ar get t he CLR, wit h m ore t han 20 ot her languages, from COBOL t o Eiffel.
C.1 .N ET Pr ogra m m in g La ngua ge s All .NET program m ing languages use t he sam e set of base classes, dev elopm ent environm ent , and CLR t ypes and com ply w it h t he sam e CLR design const raint s. Com piling code in .NET is a t wo- phase process. Fir st , t he high- level code is com piled int o a gener ic m achine- code- like language called int erm ediat e language ( I L) . At runt im e, on t he first call int o t he I L code, t he I L is com piled int o nat iv e code and execut es as nat iv e code. The nat iv e code is used unt il t he program t erm inat es. The I L is t he com m on denom inat or of all .NET pr ogram m ing languages, and equivalent const ruct s in t wo differ ent languages should t heoret ically pr oduce ident ical I L. As a
371
result , all .NET pr ogr am m ing languages are equal in per form ance and ease of dev elopm ent . The differ ence bet ween t he languages is m ost ly aest het ic, and choosing one over anot her is a m at t er of personal preference. For exam ple, t o m ak e C+ + CLR com pliant , Microsoft had t o add num erous nonst andard com piler dir ect ives and ext ensions, result ing in less r eadable code t han st andar d unm anaged C+ + . Sim ilar ly , Visual Basic.NET bears lit t le resem blance t o it s Visual Basic 6.0 ancest or , r equir ing y ou t o unlearn t hings you used t o do in Visual Basic 6.0. Only C# has no legacy and is a fr esh .NET language. C# is a C+ + der iv at ive language, com bining t he power of C+ + wit h t he ease of Visual Basic 6.0, and offer ing you r eadable, CLR- com pliant C+ + lik e code. I n fact , C# looks m or e like norm al C+ + t han m anaged C+ + . This appendix and Chapt er 10 t herefore use C# in it s code sam ples. Bear in m ind, however , t hat y ou can do all t he code sam ples in Visual Basic.NET, m anaged C+ + , or any ot her .NET language. Ot her feat ures of .NET languages include t heir t reat m ent of every ent it y as an obj ect ( including prim it ive t ypes) , result ing in a cleaner program m ing m odel. .NET provides com m on er ror handling based on ex cept ions. The CLR has a r ich pr edefined set of ex cept ion classes t hat you can use as is, or derive and ext end for a specific need. An except ion t hr own in one language can be caught and handled in anot her language.
C.2 Pa ck a gin g .N ET Com pon e n t s: Assem blies The basic code packaging unit in .NET is t he assem bly . An assem bly is a logical DLL—i.e., assem bly can com bine m ore t han one phy sical DLL int o a single deploym ent , v er sioning, and secur it y unit . However, an assem bly usually cont ains j ust one DLL ( t he default in Visual St udio.NET) and you have t o use com m and- line com piler swit ches t o incor porat e m ore t han one DLL in your assem bly . An assem bly is not lim it ed t o cont aining only DLLs. An assem bly can also cont ain an EXE. As a com ponent developer, you usually dev elop com ponent s t hat reside in a single or m ult iple DLL assem bly t o be consum ed by a client applicat ion residing in an assem bly t hat has an EXE. The code in t he assem bly ( in t he DLLs or t he EXE) is only t he I L code, and at runt im e t he I L is com piled t o nat iv e code, as ex plained previously . An assem bly cont ains m ore t han j ust t he I L code. Em bedded in every assem bly is m et adat a, a descr ipt ion of all t he t ypes declared in t he assem bly and a m anifest , a descript ion of t he assem bly and all ot her r equir ed assem blies. The m anifest cont ains v ar ious assem bly- wide inform at ion, such as t he assem bly version
372
infor m at ion. The v er sion infor m at ion is t he pr oduct of a v ersion num ber prov ided by t he developer and a build and revision num ber capt ured by t he com piler ( or provided by t he developer as well) during t he build. All DLLs in t he assem bly shar e t he sam e version num ber and ar e deployed as one unit . The assem bly boundary serv es as t he .NET secur it y boundary — securit y per m issions ar e gr ant ed at t he assem bly level. All com ponent s in an assem bly share t he sam e set of per m issions. The assem bly can also cont ain a com piled resource file for icons, pict ures, et c., like any t radit ional DLL or EXE.
C.3 D e ve lopin g .N ET Com pon ent s To creat e a .NET com ponent in C# ( or any ot her .NET Language) , you sim ply declare a class. When t he class is inst ant iat ed by t he CLR, t he result is a binar y com ponent . Ex am ple C- 1 shows a sim ple class nam ed MyClass t hat im plem ent s t he IMessage int er face and displays a m essage box wit h t he word " Hello" w hen t he int erface's ShowMessage( ) m et hod is called. Ex a m p le C- 1 . Bu ilding a com p on e n t in .N ET
namespace MyNamespace { using System; using System.Windows.Forms; public interface IMessage { void ShowMessage( ); } public class MyComponent :IMessage { public MyComponent(){}//constructor ~ MyComponent(){}//destructor public void ShowMessage( ) { MessageBox.Show("Hello!","MyComponent"); } } } The MyComponent class in Exam ple C- 1 is defined as public, m ak ing it accessible t o any .NET or COM client once y ou export t he com ponent t o COM. You can define a class const ruct or t o do obj ect init ializat ion, as in t his exam ple, but t he dest ruct or has different sem ant ics t han t he classic C+ + dest r uct or because .NET uses 373
nondet erm inist ic obj ect finalizat ion. You can im plem ent ot her m et hods t o do obj ect cleanup as well. The im plem ent at ion of ShowMessage( ) uses t he st at ic Show( ) m et hod of t he MessageBox class. Like in C+ + , C# allows you t o call a class ( st at ic) m et hod wit hout inst ant iat ing an obj ect fir st . Exam ple C- 1 dem onst rat es a few addit ional key point s r egarding dev eloping .NET com ponent s: using nam espaces and int erfacebased program m ing. These point s are discussed next . C.3 .1 Usin g N a m e spa ce s The class definit ion is scoped in a nam espace. Nam espaces are opt ional, but y ou ar e encour aged t o use t hem . Nam espaces in .NET hav e t he sam e purpose t hey have in C+ + : t o scope classes so a client can use different classes from different vendors t hat hav e t he sam e nam e. For a nam espace, you t ypically use t he pr oduct ’s nam e, y our com pany’s nam e, or t he assem bly’s nam e. A client of t he class MyComponent in Exam ple C- 1 m ust now r efer t o it by qualifying it wit h it s cont aining nam espace: MyNamespace.MyComponent Alt ernat ively, t he client can say t hat it is using t he MyNamespace nam espace, and avoid put t ing t he " MyNam espace" prefix on every t y pe cont ained in t hat nam espace: using MyNamespace; //MyComponent is now the same as MyNamespace.MyComponent You can also nest nam espaces wit hin one anot her . For exam ple, if your com pany dev elops m ore t han one pr oduct , you would t ypically define in t he scope of t he MyCompany nam espace, t he nest ed nam espaces Product1, Product2, and so on: namespace MyCompany { namespace Product1 { //classes and type definitions public class Component1 {...} } namespace Product2 { //other classes and type definitions } } Client s of y our com ponent s m ust giv e t he full qualify ing nam espace t o access your com ponent : MyCompany.Product1.Component1 Or, client s can use t he using st at em ent : using MyCompany.Product1;
374
//Component1 is now the same as MyCompany.Product1.Component1 The ShowMessage( ) m et hod in Ex am ple C- 1 uses t he st at ic m et hod Show( ) of t he MessageBox class, defined in t he System.Windows.Forms nam espace. Ex am ple C- 1 t herefore cont ains t he st at em ent : using System.Windows.Forms; This st at em ent is used t o sim plify downst r eam code. C.3 .2 Usin g I n t e r fa ce s One t he m ost im port ant principles of com ponent - or ient ed dev elopm ent is t he separat ion of int erfaces fr om im plem ent at ion. COM enfor ces t his separat ion by hav ing y ou separ at e t he definit ions of int er faces and classes. .NET does not force you t o hav e your class m et hods be part of any int erface, but it is im per at iv e t hat you do so t o allow polym or phism bet ween different im plem ent at ions of t he sam e int erface. Exam ple C- 1 includes an int er face definit ion as part of t he code— t here is no need for a separ at e I DL file. The reserved C# word interface allow s y ou t o define a t y pe t hat is purely v ir t ual ( it has no im plem ent at ion and cannot be inst ant iat ed by client s) , j ust like a C+ + pure vir t ual or abst r act class. The int erface m et hods do not hav e t o ret ur n HRESULT or any ot her error handling t ype. I n case of an er ror, t he m et hod im plem ent at ion should t hrow an ex cept ion.
C.4 W r it in g .N ET Clie n t - Side Code All t hat a .NET client has t o do t o use a com ponent is add a reference in it s pr oj ect set t ing t o t he assem bly cont aining t he com ponent , creat e an obj ect , and t hen use it : using MyNamespace; //Interface-based programming: IMessage myObj; myObj = (IMessage)new MyComponent( ); myObj.ShowMessage( ); You usually do not use point er s in C# . Everyt hing is r efer enced using t he dot ( .) operat or. Not e also t hat t he client cast s t he newly cr eat ed obj ect t o t he IMessage int erface. This is t he .NET equiv alent of QueryInterface( ). I f t he obj ect does not support t he int erface it is being cast t o, an ex cept ion is t hrow n. The client can inst ead per form a cont rolled query for t he int erface using t he as k eyword. I f t he obj ect does not support t he int erface, t he ret ur ned reference is null: using MyNamespace; 375
//Even better: check for type mismatch IMessage myObj; myObj = new MyComponent( ) as IMessage; Debug.Assert(myObj!= null); myObj.ShowMessage( ); As m ent ioned before, .NET does not enfor ce separat ion of int erface fr om im plem ent at ion, so t he client could creat e t he obj ect t y pe dir ect ly: using MyNamespace; //Avoid doing this: MyComponent myObj; myObj = new MyComponent( ); myObj.ShowMessage( ); However, you should avoid writ ing client code t hat way because doing so m eans t hat t he client code is not polym orphic wit h ot her im plem ent at ions of t he sam e int erface. Such client code also couples int er act ing m odules. I m agine a sit uat ion in which Module 1 cr eat es t he obj ect and Module 2 uses it . I f all t hat t he Module 1 passes t o Module 2 is t he int er face t ype, Module 1 can change t he im plem ent at ion of t he int er face lat er wit hout affect ing Module 2.
C.5 .N ET a s a Com pon en t Tech n ology To sim plify com ponent developm ent , one of t he goals set for t he .NET fr am ework was t o im prove COM deficiencies. Som e of t hese deficiencies, such as awk ward concur rency m anagem ent v ia apart m ent s, wer e inher it ed wit h COM it self. Ot her deficiencies occur as a result of er ror- prone developm ent and deploy m ent phases. Exam ples include m em ory and resource leaks result ing from reference count defect s, fr agile r egist r at ion, t he need for developerprovided proxy st ubs pairs, and hav ing int erface and t y pe definit ion in I DL files separ at e from t he code. Fram ework s such as ATL do provide aut om at ion of som e of t he requir ed im plem ent at ion plum bing, such as class fact ories and regist rat ion, but t hey int roduce t heir ow n com plexit y. .NET is designed t o not only im pr ove t hese deficiencies, but also m aint ain t he core COM concept s t hat have proven t hem selves as core principles of com ponent - orient ed developm ent . .NET prov ides you fundam ent al com ponent - orient ed developm ent principles, such as binary com pat ibilit y bet ween client and com ponent , separat ion of int erface from im plem ent at ion, obj ect locat ion t ranspar ency, concurr ency m anagem ent , securit y , and language independence. A com pr ehensiv e discussion of .NET as a com ponent t echnology m er it s a book in it s own r ight and is bey ond
376
t he scope of t his appendix. Howev er, t he following sect ions descr ibe t he m ain char act erist ics of .NET as a com ponent t echnology. C.5 .1 Sim plif ie d Com pon e n t D e v e lopm e nt Com pared t o COM, .NET m ight seem t o be m issing m any t hings y ou t ake for gr ant ed as par t of developing com ponent s. However, in essence, t he m issing elem ent s ar e act ually present in .NET, alt hough in a different fashion: •
•
•
•
•
•
Ther e is no canonical base int er face ( such as IUnknown) t hat all com ponent s der iv e from . I nst ead, all com ponent s der ive fr om t he System.Object class. Every .NET obj ect is t herefore poly m or phic w it h System.Object. Ther e are no class fact or ies. I n .NET, t he r unt im e resolves a t y pe declar at ion t o t he assem bly cont aining it and t he ex act class or st ruct wit hin t he assem bly . Ther e is no reference count ing of obj ect s. .NET has a sophist icat ed garbage collect ion m echanism t hat det ect s when an obj ect is no longer used by client s. Then t he gar bage collect or dest r oys t he obj ect . Ther e are no I DL files or t ype libr aries descr ibing y our int er faces and cust om t y pes. I nst ead, you put t hose definit ions in your source code. The com piler is responsible for em bedding t he t ype definit ions in a special for m at in your assem bly called m et adat a. Ther e are no GUI Ds. Scoping t he t y pes wit h t he nam espace and assem bly nam e pr ov ides uniqueness of t ype ( class or int er face) . When shar ing an assem bly bet ween client s, t he assem bly m ust cont ain a st rong nam e—a unique binar y blob generat ed wit h an encr ypt ion key . Globally unique ident ifiers do ex ist in essence, but you do not have t o m anage t hem any m ore. Ther e are no apart m ent s. By default , every .NET com ponent execut es in a fr ee- t hreaded env ir onm ent and y ou are responsible for synchr onizing access t o y our com ponent s. Providing sy nchronizat ion is done by either r elying on .NET sy nchr onizat ion locks or using COM+ act ivit ies.
.NET has a superb developm ent environm ent and sem ant ics, t he product of years of obser ving how developer s use COM and t he hurdles t hey faced. C.5 .1 .1 Th e .N ET ba se cla sses
As dem onst r at ed in Exam ple C- 1, a hard- t o- learn com ponent dev elopm ent fr am ework such as ATL is not r equired t o build binary m anaged com ponent s. .NET t ak es car e of all t he under ly ing 377
plum bing for you. To help y ou develop your business logic fast er , .NET also pr ov ides y ou wit h m ore t han 3,500 base classes, av ailable in sim ilar form for all languages. The base classes are easy t o learn and apply. You can use t he base classes as is, or der ive from t hem t o ext end and specialize t heir behavior. C.5 .1 .2 Com pon e n t inh er it an ce
.NET enfor ces st r ict inherit ance sem ant ics and inher it ance conflict s resolut ion. .NET does not allow m ult iple inher it ance of im plem ent at ion. You can only der ive from one concret e class. You can, however, der ive from as m any int erfaces as you lik e. When you override a virt ual funct ion im plem ent at ion in a base class, you m ust declare y our int ent ex plicit ly . For exam ple, if you want t o overr ide it , y ou should use t he override reserved w ord. C.5 .1 .3 Com pon e n t visibilit y
While developing a set of int eroper at ing com ponent s, you oft en hav e com ponent s t hat ar e int ended only for privat e use and should not be shared wit h your client s. Under COM, t her e is no easy way of guar ant eeing t hat t he com ponent s ar e only used pr ivat ely . The client can always hunt t hr ough t he Regist ry , find t he CLSI D of your privat e com ponent , and use it . I n .NET, you can sim ply use t he internal key wor d on t he class definit ion ( inst ead of public, as in Exam ple C- 1) . The runt im e denies access t o your com ponent for any caller out side your assem bly. C.5 .1 .4 At t ribu t e - b a sed p rogr am m in g
When developing com ponent s, you can use at t ribut es t o declar e your com ponent needs, inst ead of coding t hem . Using at t ribut es t o declare com ponent needs is sim ilar t o t he way COM dev elopers declare t he t hreading m odel at t ribut e of t heir com ponent s. .NET provides you wit h num erous at t r ibut es, allowing you t o focus on your dom ain problem at hand ( COM+ services are accessed v ia at t r ibut es) . You can also define your own at t r ibut es or ext end exist ing ones. C.5 .1 .5 Com pon e n t - or ie n t e d se cu rit y
The classic Windows NT securit y m odel is based on what a given user is allowed t o do. This m odel has evolved in a t im e when COM was in it s infancy and applicat ions were usually st andalone, m onolit hic chunks of code. I n t oday’s highly dist r ibut ed, com ponent - orient ed environm ent , t her e is a need for a securit y m odel based on w hat a given piece of code—a com ponent —is allowed t o do, and not only on w hat it s caller is allowed t o do.
378
.NET allows you t o configur e perm issions for a piece of code and provide an evidence t o prove t hat it has t he right cr edent ials t o access a resour ce or per form sensit iv e work. Ev idence- based securit y is t ight ly relat ed t o t he com ponent ’s or igin. Syst em adm inist rat ors can decide t hat t hey t rust all code t hat cam e from a par t icular vendor , but dist rust ev eryt hing else, fr om downloaded com ponent s t o m alicious at t ack s. A com ponent can also dem and t hat a per m ission check be perfor m ed to v er ify t hat all callers in it s call chain have t he right per m issions before it pr oceeds t o do it s work . This m odel com plem ent s COM+ r ole- based securit y and call aut hent icat ion. I t pr ov ides t he applicat ion adm inist r at or wit h granular cont r ol over not only what t he users are allowed t o do, but also w hat t he com ponent s ar e allowed t o do. .NET has it s own rolebased securit y m odel, but it is not as granular or user fr iendly as COM+ role- based securit y . C.5 .2 Sim plif ie d Com pon e n t D e ploy m e nt .NET does not r ely on t he Regist ry for anyt hing t hat has t o do wit h your com ponent s. I n fact , inst alling a .NET com ponent is as sim ple as copy ing it t o t he direct ory of t he applicat ion using it . .NET m aint ains t ight version cont r ol, enabling side- by- side execut ion of new and old versions of t he sam e com ponent on t he sam e m achine. The net result is zero- im pact inst all—by default , you cannot harm anot her applicat ion by inst alling yours, t hus ending t he predicam ent know n as DLL Hell. The .NET m ot t o is: it j ust work s. I f y ou want t o inst all com ponent s t o be shared by m ult iple applicat ions, y ou can inst all t hem in t he Global Assem bly Cache ( GAC) . I f t he GAC alr eady cont ains a pr ev ious v er sion of your assem bly , it k eeps it for use by client s t hat wer e built against t he old version. You can pur ge old ver sions as w ell, but t hat is not t he default . C.5 .3 Sim plif ie d Obj e ct Life Cycle M a n a ge m e nt .NET does not use reference count ing t o m anage an obj ect 's life cy cle. I nst ead, .NET keeps t rack of accessible pat hs in y our code t o t he obj ect . As long as any client has a r efer ence t o an obj ect , it is considered reachable. Reachable obj ect s are kept alive. Unr eachable obj ect s are considered garbage, and t herefore dest roying t hem harm s no one. One of t he cr ucial CLR ent it ies is t he gar bage collect or. The garbage collect or periodically t r av erses t he list of ex ist ing obj ect s. Using a sophist icat ed point ing schem a, it det ect s unreachable obj ect s and releases t he m em ory allocat ed t o t hese obj ect s. Consequent ly, client s do not have t o increm ent or decrem ent a reference count on t he obj ect s t hey creat e. C.5 .4 N on de t e r m in ist ic Fin a liza t ion 379
I n COM, t he obj ect k nows t hat it is no longer r equired by it s client s when it s r efer ence count goes down t o zer o. The obj ect t hen per form s cleanup and dest roys it self by calling delete this;. The ATL fr am ework even calls a m et hod on your obj ect called FinalRelease( ), let t ing you handle t he obj ect cleanup. I n .NET, unlike COM, t he obj ect it self is never t old when it is deem ed as garbage. I f t he obj ect has specific cleanup t o do, it should im plem ent a m et hod called Finalize( ). The garbage collect or calls Finalize( ) j ust before dest r oy ing t he obj ect . Finalize( ) is your .NET com ponent ’s dest ruct or . I n fact , even if you im plem ent a dest ruct or ( such as t he one in Exam ple C- 1) , t he com piler will conv ert it t o a Finalize() m et hod.
C# D e st r uct or I n C# , do not prov ide a Finalize( ) m et hod. I nst ead, provide a dest r uct or. The com piler bot h convert s t he dest ruct or definit ion t o a Finalize( ) m et hod and calls y our base class Finalize( ) m et hod. For ex am ple, for t his class definit ion: public class MyClass { public MyClass( ){} ~MyClass( ){} } The code t hat is act ually generat ed would be: public class MyClass { public MyClass( ){} protected virtual void Finalize( ) { try { //Your destructor code goes here } finally { base.Finalize( );//everybody has one, from Object } } } However, sim plify ing t he obj ect lifecycle com es wit h a cost in sy st em scalabilit y and t hroughput . I f t he obj ect holds on t o expensive r esources, such as files or dat abase connect ions, t hose resources are r eleased only when Finalize( ) is called. I t is called at an undet erm ined point in t he fut ur e, usually when t he process host ing y our com ponent is out of m em or y. I n t heor y, releasing t he
380
expensive r esources t he obj ect holds m ay never happen, and t hus severely ham per syst em scalabilit y and t hr oughput . Ther e are t wo solut ions t o t he problem s arising fr om nondet erm inist ic finalizat ion. The fir st solut ion is t o im plem ent m et hods on your obj ect t hat allow t he client t o explicit ly or der cleanup of expensive resour ces t he obj ect holds. I f t he obj ect holds ont o resour ces t hat can be r eallocat ed, t hen t he obj ect should expose m et hods such as Open( ) and Close( ). An obj ect encapsulat ing a file is a good ex am ple. The client calls Close( ) on t he obj ect , allowing t he obj ect t o r elease t he file. I f t he client w ant s t o access t he file again, it calls Open( ) wit hout recr eat ing t he obj ect . The m ore com m on case is when disposing of t he resources am ount s t o dest r oying t he obj ect . I n t hat case, t he obj ect should im plem ent a m et hod called Dispose( ). When a client calls Dispose( ), t he obj ect should dispose of all it s expensive r ecourses, and t he client should not t r y t o access t he obj ect again. The problem wit h bot h Close( ) and Dispose( ) is t hat t hey m ak e sharing t he obj ect bet ween client s m uch m or e com plicat ed t han COM’s reference count s. The client s hav e t o coordinat e which one of t hem is responsible for calling Close( ) or Dispose( ) and when Dispose( ) should be called; t hus, t he client s are coupled t o one anot her. The second solut ion t o nondet erm inist ic finalizat ion is t o use COM+ JI TA, as explained in Chapt er 10. C.5 .5 COM a n d W indow s I n t e r ope r a bilit y COM and .NET ar e fully int eroper able. Any COM client can call y our m anaged obj ect s, and any COM obj ect is accessible t o a m anaged client . To export y our .NET com ponent s t o COM, use t he TlbExp.exe ut ilit y , also available as a com m and from t he Tools m enu. The ut ilit y generat es a t ype libr ary t hat COM client s use t o CoCr eat e m anaged t ypes and int erfaces. You can use var ious at t r ibut es on your m anaged class t o direct t he ex por t process, such as providing a CLSI D and I I D. To im por t an exist ing COM obj ect t o .NET ( by far t he m ost com m on scenar io) , use t he TlbI m p.exe ut ilit y . The ut ilit y gener at es a m anaged w rapper class, which y our m anaged client uses. The wrapper m anages t he r eference count on t he act ual COM obj ect . When t he wrapper class is garbage collect ed, t he wrapper releases t he COM obj ect it w raps. You can also im port a COM obj ect fr om wit hin t he Visual St udio.NET env ir onm ent by select ing t he COM obj ect from t he proj ect reference dialog ( which m ak es Visual St udio.NET call TlbI m p for you) . .NET has support for invok ing nat iv e Win32 API calls, or any DLL export ed funct ions, by im port ing t he m et hod signat ures t o t he m anaged env ironm ent .
381
C.6 Com posin g Assem blies You prov ide t he com piler wit h y our assem bly inform at ion in an assem bly inform at ion file ( usually called in a C# proj ect , Assem blyI nfo.cs) . The assem bly inform at ion file is com piled wit h t he rest of t he proj ect ’s source files. The inform at ion in t he file is in t he form of assem bly at t ribut es—direct iv es t o t he com piler on t he infor m at ion t o em bed in t he assem bly. Ex am ple C- 2 shows a t ypical set of assem bly at t ribut es. Ex a m p le C- 2 . Th e a ssem bly in form a t ion file in clud es a va r iet y of asse m bly a t t rib u t e s
[assembly: AssemblyTitle("MyAssembly")] [assembly: AssemblyDescription("Assembly containing demo .NET components")] [assembly: AssemblyCompany("My Product")] [assembly: AssemblyCopyright("(c) 2001 My Company ")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("en-US")] [assembly: AssemblyVersion("1.0.*")] C.6 .1 Sh a r ing Asse m blie s Assem blies can be pr ivat e or shared. A privat e assem bly resides in t he sam e direct ory of t he applicat ion t hat uses it ( or in it s pat h) . A shared assem bly is in a know n locat ion, called t he global assem bly cache ( GAC) , m ent ioned in Chapt er 10Chapt er 10. To add an assem bly t o t he GAC, use eit her t he .NET adm inist rat ion t ool or t he GACUt il com m and- line ut ilit y. Once in t he GAC, t he assem bly can be accessed by m ult iple applicat ions, bot h m anaged and unm anaged. To avoid conflict s in t he GAC bet ween different assem blies t hat hav e t he sam e nam e, a shared assem bly m ust have a st r ong nam e. The st rong nam e aut hent icat es t he assem bly’s origin and ident it y and cannot be generat ed by a par t y ot her t han t he original publisher . The st rong nam e allows any client of t he assem bly ( usually t he assem bly loader) t o det er m inist ically v erify t hat t he assem bly was not t am pered wit h. Assigning a st r ong nam e t o an assem bly is also k nown as signing t he assem bly . To assign a st rong nam e t o your assem bly , y ou fir st need t o generat e privat e or public encrypt ion key s. You can generat e t he pair using t he SN.exe com m and- line ut ilit y: SN.exe -k MyAssembly.snk Fut ur e versions of Visual St udio.NET m ay enable you t o gener at e key s fr om wit hin t he v isual env ir onm ent . The - k swit ch inst ruct s SN t o generat e a new pair of keys and st ore t hem in t he filenam e 382
specified. The convent ion used for t he filenam e is t he assem bly nam e wit h t he st rong nam e key ( snk) ex t ension, but it can act ually be any nam e and ex t ension you lik e. You t hen add t he snk file t o t he assem bly ’s inform at ion file, using t he AssemblyKeyFile assem bly at t ribut e: [assembly:AssemblyKeyFile("MyAssembly.snk")] I n addit ion t o a version num ber and a st rong nam e, a shared assem bly m ust have a nam espace and locale ident ifier t hat ident ify t he hum an language used in it s user int er face. I n Exam ple C- 2 t he locale is specified by t he AssemblyCulture assem bly at t ribut e. C.6 .2 Asse m bly M e t a da t a Each assem bly m ust cont ain m et adat a. The m et adat a is t he .NET equivalent of COM’s t ype libraries, except t he m et adat a is m ore like a t y pe libr ar y on st er oids. The m et adat a cont ains descript ions of all t he t y pes defined in t he assem bly, such as int erfaces, classes and t heir base classes, m et hod signat ures, pr opert ies, event s, m em ber var iables, and cust om at t ribut es. The m et adat a is generat ed aut om at ically by t he com piler when it com piles t he source files of your pr oj ect . You can view t he m et adat a of y our assem bly using t he I LDASM ut ilit y . C.6 .3 Asse m bly M a n ife st While t he m et adat a describes t he t ypes in your assem bly, t he m anifest describes t he assem bly it self. The m anifest cont ains t he assem bly v ersion inform at ion, t he locale inform at ion, and t he assem bly’s st rong nam e. The m anifest also cont ains t he visibilit y of t he assem bly’s t y pes—which t ypes are public ( can be accessed by ot her assem blies) and which t ypes are int er nal ( can only be accessed fr om wit hin t he assem bly ) . Finally , t he m anifest cont ains t he secur it y per m ission check s t o run on behalf of t he assem bly. Like t he m et adat a, t he m anifest is generat ed aut om at ically by t he com piler during assem bly com pilat ion. You can v iew t he m anifest of your assem bly using t he I LDASM ut ilit y . C.6 .4 Asse m bly File s Because every assem bly m ust cont ain t he m anifest and m et adat a ( and usually I L code and resour ces) , a single DLL or EXE assem bly cont ains all of t hem in one file. Howev er, t he only requir em ent of a m ult ifile assem bly is t hat a file cont aining I L m ust also cont ain m et adat a descr ibing it . Such a file is called a m odule. A m ult ifile assem bly m ust st ill have one DLL file t hat cont ains t he m anifest . Figure C- 1 shows a few possibilit ies for com posing assem blies. Figu r e C- 1 . Assem b ly file s
383
As y ou can see, you can com pose and use com piler swit ches t o bind pract ice, m ost assem blies cont ain St udio.NET I DE pr ovides only t his file.
t he assem bly in alm ost any way all your files t oget her. I n j ust one DLL ( t he Visual opt ion) and are com posed of one
384
Colophon Our look is t he result of reader com m ent s, our own experim ent at ion, and feedback from dist r ibut ion channels. Dist inct iv e covers com plem ent our dist inct ive approach t o t echnical t opics, breat hing per sonalit y and life int o pot ent ially dry subj ect s. The anim als on t he cover of COM and .NET Com ponent Ser vices are m oray and conger eels. Eels m ake up t he 10 fam ilies of fish belonging t o t he order Arguillifor m es. Know n for t heir snakelik e body wit h no hind fins, eels can m ov e t hrough wat er, m ud, and rocky crevices. Most eels ar e less t han t hree feet long, but fr eshwat er conger eels can gr ow as large as nine feet . Unt il t he 20t h cent ury , lit t le was k now n about t he life cy cle and m igrat ion of eels. Scient ist s now know t hat Am er ican and European eels t r avel long dist ances during t heir reproduct iv e cycles. The fem ale eels generally m at ur e in fr eshwat er lakes and t ravel t o t he nearest ocean, oft en slit hering over wet grass and m ud during t he j ourney. Then t hey swim or dr ift from Europe or Nort h Am er ica t o t he Sargasso Sea. There, t he fem ales lay up t o 20 m illion eggs and t hen die. The egg- larvae t hen dr ift eit her t o Nort h Am er ica ( aft er one year) or back t o Eur ope ( aft er t hr ee years) . Aft er r eaching t heir hom e cont inent , t he eels com plet e t heir cycle by gat hering at t he m out hs of rivers and sw im m ing upst ream . Eels ar e also known for t heir oily m eat , cherished by som e as a culinar y delicacy. Ann Schir m er w as t he product ion edit or for COM and .NET Com ponent Services. Paulet t e Miley and Ann Schir m er were t he copyedit or s for t he book . Ann Schirm er and Leanne Soylem ez were t he proofreaders. Claire Clout ier, Mar y Brady, and Rachel Wheeler provided qualit y cont rol. Kim o Cart er, Ann Schirm er, and Sarah Sherm an did int erior com posit ion. Judy Hoer wrot e t he index . Ellie Volckhausen designed t he cover of t his book, based on a ser ies design by Edie Fr eedm an. The cov er im age is a 19t h- cent ury engr av ing fr om t he Dover Pict orial Ar chive. Em m a Colby produced t he cover layout wit h Quar k XPress 4.1 using Adobe’s I TC Gar am ond font . Dav id Fut at o designed t he int erior layout . Neil Walls conv ert ed t he files fr om Micr osoft Word t o Fram eMaker 5.5.6 using t ools cr eat ed by Mik e Sierr a. The t ext font is Linot ype Bir ka, t he heading font is Adobe Myriad Condensed, and t he code font is LucasFont ’s TheSans Mono Condensed. The illust r at ions t hat appear in t he book were produced by Robert Rom ano and Jessam yn Read using Macr om edia Fr eeHand 9 and Adobe Phot oshop 6. The t ip and war ning icons were drawn by Chr ist opher Bing. This colophon was w rit t en by Ann Schirm er.
385