This is the first of a series of articles on an approach to systems architecture called “Software Product Lines.” To introduce the concept, let’s ignore the word “software” for now and focus on what a product line is in a more general and tangible fashion.
If you are in the market for a laptop and go to Dell’s website, you have a choice of several different base models that can be configured to your specification. Once you select the base model, you can configure things such as the size of RAM, size of the hard drive, type of display, etc. What makes this possible is a hardware architecture of various components that can fit into the same slot – compatible electrically as well as physically. This allows Dell to create and mass produce (or purchase) several standardized components and simply plug them together to create a customized product at a reasonable cost in a short timeframe. When new technologies come out that require changes to the base product, like ATA laptop hard drives, Dell spins up a new product line with that one modification reusing the old technology and components that is still valid, tried, and true. This sounds like a great idea and solid common sense, doesn’t it?
As with anything, there are challenges to this approach. Take for instance a design flaw in one of the more heavily shared components, such as the motherboard, where the power connector solder joints go bad due to heat and constant plugging/unplugging. This one design flaw could end up costing dearly in repair and possibly recall expenses, as a significantly higher number of devices are impacted then if the system was not as modular. It is also significantly more difficult and time consuming up-front to design, build, and test a system that shares components. In a product line, tight adherence to specifications needs to be considered for future compatibility. Take for example the wireless network/Bluetooth riser cards found in many laptops. Let’s assume the bus timings are just a hair off spec, but the current crop of hardware is tolerant of it. Newer technologies coming out six months later may not be tolerant. A non-product line approach would be to make a revision of the entire system – which is required anyway; however, the product line approach requires significant expense to revise and test one or both of the components to assure compatibility.
Software is not very different than hardware when it comes to product lines. Most high-end software packages are heavily customizable so it can integrate into a variety of environments. Microsoft’s SharePoint product is one example, SAP is another. These two products are off-the-shelf packages intended to appeal to a very large market. There was heavy up-front investment by the vendor in design, documentation, interfaces, components, external training and certification programs, and more. But what if your product is very specialized with a small market? Even worse, what if your product is designed for small to medium sized businesses that typically can’t afford or will not have the resources to self-customize such a vast system? To top it all off, certainly, you cannot afford to invest in the amount of up-front work required to build, test, and debug a heavily customizable system given the small size of your target market.
Enter the concept of Software Product Lines. It is important to note that this is an engineering approach to solve a specific business problem. Using the Software Product Line methodology in a business environment that is not controlled or the problem domain of the customer is not well understood will cause you to paint yourself into a corner with no easy or cheap way out. Another salient comment is choosing this approach should not significantly change your development methodology (assuming, of course, that you have adopted a legitimate methodology in the first place). Finally, this approach is NOT a method of implementation, merely a way of thinking from a design perspective of what needs componentized. The decision to use a Service Oriented Architecture to implement a Software Product Line is off-topic – these are two different animals entirely.
Part II of this series will delve into some of these concepts deeper.
Thursday, February 14, 2008
Software Product Lines
Sunday, January 22, 2006
Go Steelers Redux
Not only has it been a long time since my last post, but it has been exactly a year since my last post on my hometown NFL football team, the Pittsburgh Steelers. Sadly, last year we lost the AFC championship. This year we won it against the Denver Bronco's. This means we are off to the Super Bowl against the Seattle Seahawks. Superbowl XL is on February 5th, 2006. In preparation, my blog banner has been updated to "All Steelers, all the time" from the revolving banner of the past.
One of the great things about living in (or coming from) the Pittsburgh area is the friendliness of the people in and around the city. Last year, I blogged a bit about Myron Cope and the "Terrible Towel." One thing I neglected to mention is that all proceeds on the sale of the towel go to the Allegheny Valley School that provides care for children, adults, and seniors with mental retardation and physical disabilities. If you would like to support the Steelers, and the school, please use the above link (instead of the Google AdSense ones) so that the school gets additional credit.
I believe the Steelers have been playing for the 'Bus' - a.k.a. running back Jerome Bettis. The Bus has been a Pittsburgh fixture for years, not only as a dependable player, but as an excellent example of what a celebrity should be like. To illustrate my point, Hines Ward (a teammate and Steelers Wide Receiver) was in tears last year after the AFC Championship loss because he believed the team let Jerome down in his potentially last season. This year is different. Jerome will have the opportunity to play, and in his home city, Detroit. Jerome is active in the community, and is currently raising awareness about Asthma, a disease he is afflicted with, in addition to a program called "The Bus Stops Here," benefiting children from both Pittsburgh and Detroit. Jerome's parents, Gladys and John, have every reason to proud of their son, and the values they instilled in him.
Other team members have had tremendous impact on the Pittsburgh region. I don't want to leave out any players, but Charlie Batch, a backup quarterback, is also famous for not only coming from the Pittsburgh area, but for his contributions to it. There are several other examples of how the team and its players gives back to the community.
I believe much of the reason for the positive impact of the players of the Steelers goes back to the team's ownership and philosophy. The most famous owner is Art Rooney, a.k.a. "The Chief", who bought the franchise in 1933. If any Pittsburgher's have not yet seen the play "The Chief" at the Pittsburgh Public Theatre, you don't know what you missed! This play ran in 2003, 2004, and earlier this month - keep checking for it to return (possibly) next fall at the PPT website. Franco Harris (star player in the 70's) was talking about Art Rooney when he said, "He was always there to help and to give. And this feeling filtered down to the players. I think the Steelers' players give more to their community than any other team in professional sport."
I'm hoping that the Super Bowl in a few weeks does not bring on any additional heart attacks among the fans, but that you watch it with the understanding that many of the athletes on both sides of the ball are using their celebrity status and monies to better their communities. I think we can all learn from this.
To my readers in the Seattle/Redmond area - I didn't mean to leave out the players of your team, I merely am not familiar with their good works. I am, however, familiar with Victor's Celtic Coffee Company, and am currently out of the "Redmond Slough" blend as of this morning. I'd be willing to trade for some of that in exchange for posting Seattle Seahawks charity links and an equivalent amount of coffee from one of our local coffee houses (Coffee Tree Roasters)...
Go Steelers!
Silence is a virtue...
I was looking for some way of explaining my recent blog silence. As one who likes to use the words of others to explain things that are difficult to explain in my own words, I hit the quote book and encountered this gem by Sally Berger: "You never saw a fish on the wall with its mouth shut." That doesn't really fit, but I found it kinda funny and that will have to do.
In addition to the typical excuse of the holidays, I also recently changed employers. This has the side effect of changing the type of development I do. Keeping with my philosophy that I speak only for myself and not my employer, I will not divulge the name of my current employer. I will also not divulge anything that could be considered something other than generally available developer knowledge. So in reality, the only thing that could possibly change is the type of content.
Most likely I will be delving into topics such as .NET Development, the Compact Framework, and native Windows Mobile (Windows CE) development/debugging. Some of these postings may be about deployment concerns in the Windows Mobile environment. Content suggestions related to this area of development are welcome. I also plan on finishing up a few posts on MSI related subjects I started a while back after some editing and reviewing.
If you have questions regarding MSI of other installer technologies, please keep them coming - many of the privately asked (and answered) questions I have saved as blog post drafts to be completed later, and I will continue to attempt to answer them time permitting.
In the meantime, constant reader, please bear with me while I adapt to my new environment and gather together some new (and old) content.
Sunday, December 04, 2005
Writing Clear Code
This topic is a bit of a divergence from Custom Actions, but useful for some general coding skills. I'm going to highlight the C# language in this post, although you don't need to know C# to benefit from this discussion - I'll cover the essentials in the post. The topic is how to write clear code that makes it easier to defer the understand the intention of the code rather than the behavior of the code. The subject came up about a few months ago in a discussion with a coworker who initially didn't quite agree with me. I'll lay out the argument here as I did with him.
Take for instance the following code snippet:
string a = "hello";Lets explore the last two lines. The a == b equivalence operator is working on strings, and by C# rule, two strings are equivalent if they are both null, or if both values are non-null references to string instances that have identical lengths and identical characters in each character position. Translation - two nulls are identical, and any two strings that match, case sensitive, will result in a true expression. In the latter comparison, when we cast both of the strings to objects, we are not comparing the values of the strings, but the objects themselves.
string b = "hello";
System.Console.WriteLine(a == b);
System.Console.WriteLine((object)a == (object)b);
What do you think it will print? The answer is True and True. Why? String literals that are identical within the same assembly refer to the same instance. Translated, 'a' and 'b' are variables that refer to the same underlying object.
Now lets replace the declaration and assignment for string b with:
string b = "he"+"llo";Now what do you think it will print? The answer is again True and True. Why? Because the Lexical analysis (lexer) in the compiler stripped out the needless additive operator in the literal. The same would have happened if we changed the string to be "hell\u006f" - since '\u006f' is the Unicode escape sequence of the lower-case letter 'o', and this expansion takes place (most likely) prior to the lexer during the transformation phase in the compilation process.
Now lets replace the same line as above with the following two lines:
string b = "he";Now what do you think it will print? The answer is now True and False. Why? The strings are equivalent, but since the preprocessing of the source file before the compilation did not identify the strings as being the same literal, they are in different objects. In fact, because a string is immutable (once created cannot be changed), there was the construction of the string "b" during declaration and initial assignment that was later garbage collected when a new instance of "b" was created during the string concatenation.
b += "llo";
Now that the necessary background information is understood, can you tell me the intention of the following code snippet?
string a,b;I'm hoping you are going to tell me that you have no clue as to the intent of the programmer. The intent could have been to compare the two strings in a case sensitive manner, ignoring cultural rules (which is what actually is happening in the above snippet). It also could have been to compare object equivalence, just that the programmer forgot to cast the strings to object first. The intention could also have been a case-insensitive comparison. Another possible intent was to compare strings using cultural rules. The intent of the programmer simply is not clear!
//code here that assigns and manipulates a and b
...
if (a==b)
compareOK();
else
compareBad();
Rewriting the comparison line in the above snippet correctly, assuming the actual behavior of the code was the intent should look like this:
if ( String.Compare(a, b, false,This tells me that the intent of the programmer was to compare the two strings in a case sensitive manner, ignoring cultural rules . There is no other possible intent given the above line of code. This is not saying that the code is correct - just that the intent of the code is clear to those lucky enough to read it, and that the developer thought it through.
System.Globalization.CultureInfo.InvariantCulture ) == 0 )
If I am skimming through some code and see a non-String.Compare() string comparison, I will sprinkle a //BUGBUG: comment above it. Why? The simple string comparison syntax has been a rather common cause of bugs in C# code - much like the C/C++ switch statement. When investigating an issue in a piece of code, I will first look for these BUGBUG's and the TODO's as a starting point - often with better-than-random chance results.
Tuesday, November 15, 2005
Custom Action Tutorial Part III – What we did in Part II
This is the third in a series of articles about building Custom Actions. If you are coming in late to the party, check out Part I and Part II first. If I tried to explain what we were doing and why in the last installment of this tutorial, you would be even more lost now than you probably are. This article is an attempt to explain exactly what it was we did in Part II and why. Let’s start by going over each project in our solution.
The HelloWorld project
We needed something to install. This happens to be it. I could have picked notepad.exe or something, but it is nicer to see how Visual Studio could grab project outputs and put them into an MSI fairly easily.
The CustomAction project
This one is really the meat of what we are trying to learn. The other projects are sort of extraneous if you are using a tool other than Visual Studio to build your MSI. Microsoft did a pretty good job of explaining why you may want to use Custom Actions in the first place, so I’m not going to rehash that.
We created a Custom Action DLL. The reason I chose a DLL is because it is the most flexible means of tightly integrating a Custom Action with the installation engine. Additionally, there is not much you cannot do in a Win32 C++ DLL. An executable Custom Action cannot interact with the MSI engine. A VBScript or JScript action does have this capability, but if you are not careful, these are not bulletproof, and usually won't allow you to do much anyway.
We added a "Module Definition File" (.def) file to the project. This .def file is read by the linker and is used (for our purposes, anyway) to prevent mangling of function names (to be technically correct, specify the calling convention to be used). Mangling is entirely normal when a DLL is created without using the extern "c" function declaration – in fact, it is called by the more pleasant name "Decorating" the function names. If you run "Dependency Walker" (depends.exe in the Bin folder of the Platform SDK), and open a DLL in it, the listing of function exports appears. Pick FrameDyD.Dll from the same Bin directory and look at it. You will notice some ? and @ symbols around the names of functions. The .def file allows us to not create these mangled exports – you can verify this by opening the custom action dll we built in our project and see this for yourself. Ultimately, this makes the process less prone to error when entering data in the custom action table of the MSI. When adding the Custom Action via Visual Studio, it will handle the mangling automatically, so for this specific project this was not strictly necessary. If you are adding this DLL using another installation designer, you will thank me for not mangling the function names. Anyway, the .def file must contain the function names of any function you wish to make callable from outside the DLL. In our case, we wanted all four of the functions we added to be callable by the MSI engine, which is why they were added to the DEF file. There are tons of other ways to accomplish this goal
The function signatures we added to the CustomAction.cpp file are based on the requirements of the DLL Custom Action type. We return an unsigned integer (UINT) and accept a single parameter that is the handle to the installation (MSIHANDLE). We must use this handle to interact with the MSI Engine. The UINT return type indicates our status to the MSI engine.
The file "msi.lib" is the link library for the MSI engine. All custom action DLL’s must link to this file in order to call any MSI functions. The instructions in Part I explained an alternate way to inform the linker what to link with. One way is not necessarily better or worse than the other. The functions defined in the header files msi.h and msiquery.h (that we included in the stdafx.h file) all require linking to this library.
We made additional solution configurations to give us a UNICODE project type. While this is not intended to be a primer on UNICODE, all NT kernel OS’s are UNICODE under the hood. Windows 95 through ME are not UNICODE, although they can support some UNICODE extensions with the Unicode Library. For dll's and executables that only will run under Windows NT, 2000, XP, or 2003, the only option you should use is UNICODE. Therefore, if you are targeting only NT based systems, build and use the UNICODE projects. If you are supporting Windows 9x based machines use the non Unicode versions. To make the Unicode transition easier, Microsoft provided the tchar.h header that we include in the stdafx.h file that deals with strings based on compiler settings (actually preprocessor macros), so you may write code that will compile and work as native UNICODE or ANSI based on compiler settings. The TEXT() macro used in the "Hello MSI!" exercise is an example of one such tchar.h macro. Future Custom Action examples presented here will use the tchar.h macros when the compatibility of the Custom Action spans both Windows 9x and NT based platforms. Actions targeting Windows NT based systems will only compile in UNICODE, and will not use the tchar.h macros.
The final thing we added to the CustomAction project was the header include for strsafe.h. This header file describes the safe string functions intended to replace the C/C++ standard library string functions as well as specific Windows string handling implementations. These functions always start with the word "String" followed by the type of count provided as a parameter to the function, either the "Cb" which is a count of bytes, or "Cch" which is a count of characters. Remember that Unicode implementations use 2 bytes per character as opposed to the ANSI 1 byte per character – so the Cb and Cch designations are important. The last important fact about this header is by default it will deprecate the unsafe string handling functions – if you use sprintf(), you will get a compiler warning.
The Setup1 Project
Hopefully we are pretty clear on the basics for the first part of this one – an EXE is generated, Visual Studio generates a basic MSI and adds entries in the Feature, Component, File, Shortcut, and other tables to install the HelloWorld exe and the shortcut for it.
When we added the Custom Actions, we did a few things that affected the compiled MSI. Use Orca (which you installed in Part I) to view the generated MSI and follow along - try to remember back to the Custom Action theory presented in Part I:
- Added a new Component and File table entry that installs the CustomAction.dll file.
- Added four entries to the CustomAction table, referencing the DLL that we installed with the product. Notice the Source column of the CustomAction table is a reference to the key of File table for our Custom Action DLL. The CustomAction table's Target column is the DLL entry point (remember this is nothing more magical than the function names in our .def file). The action name is just a randomly generated GUID. The type column starts its life as a Type 17 which indicates msidbCustomActionTypeDll + msidbCustomActionTypeSourceFile, and to this number is added the following:
Action Type Decimal Value Description Uninstall 1041 = 17 + msidbCustomActionTypeInScript Install 1041 = 17 + msidbCustomActionTypeInScript Rollback 1297 = 17 + msidbCustomActionTypeInScript + msidbCustomActionTypeRollback Commit 1553 = 17 + msidbCustomActionTypeInScript + msidbCustomActionTypeCommit - Looking at the InstallExecuteSequence table, note that all but the uninstall custom action is scheduled before the RegisterUser Standard action, and the uninstall custom action is scheduled before the UnpublishComponents action. Note that these actions are conditionalized on the action state of the component, denoted by the dollar-sign - you can see this clearly in the InstallExecuteSequence table. In a nutshell, this condition will cause the custom actions to run only when the component it is tied to (the Custom Action DLL) is being added or removed from the system.
That concludes Part III of the Custom Action Tutorial. In the next part, we will learn how to debug the Custom Action.