Browse DevX
Sign up for e-mail newsletters from DevX


XMI: Capture UML Associations Using C# : Page 2

XMI is a great technology for anyone who builds applications from models, allowing you to use information contained in those models for big upside. Learn to extract dependency information from a UML deployment diagram using C# and how to use the Pipes and Filters pattern to simplify the whole process.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

The Pipes and Filters Pattern
The example you've seen does a good job of identifying nodes and associations. But suppose what you really want to replace the link ID values in the association with the association names. This is an easy but still interesting conversion problem with two steps. The first step is to get the hash tables for the nodes and associations, while the second is to use them as parameters to the matching objects. Here's the code:

NodeParse np = new NodeParse(name); mainNode = np.getNodes(); mainAssoc = np.getAssociations(); MatchAssociationNodes matchAN = new MatchAssociationNodes(mainNode, mainAssoc); mainMatch = matchAN.getMatched(); mEnum = mainMatch.GetEnumerator();

Figure 3. Output from the Pipe and Filters Example: Note that in this version, the "Association First" and "Association Second" lines contain the respective server names, "HR" and "CRM," rather than the IDs.
The code for doing this doesn't seem too unreasonable. I built a NodeParse object to read the nodes and associations and pass the two returned hash tables to a MatchAssociationNodes object which matches the names of the nodes with the association ends. It is a bit messy, what with calling all the hash tables, and moving them between two different objects—but it works. I could encapsulate the matching routine within NodeParse and return the associations with the normalized names. The problem with refactoring in this fashion is that it is very easy to create a "God" class. The "God" class is a classic anti-pattern that occurs when you continually add code with different functionality to the same class. One way to encapsulate the matching class and avoid the "God" anti-pattern is to use the pipes and filters pattern.

In the command line driven world of Unix it was common to write small programs that would filter and modify data. You could combine these to form more complex applications using the "pipe" character (|). This was the origin of the pipes and filters pattern. Here's an example that uses the technique to match server names to the association ends.

ls -l ~ | grep -v "^d" | awk '{print $5, $8, $3, $6, $7}' | sort -nr | awk '{print $2 "\t" $3 "\t" $4, $5 "\t" $1}'

Figure 4. Transitive Links: In this figure, the HR Server and the CRM Server are separated by the middleware server.
The preceding example prints a directory (ls -l ~), pipes the output to a grep command to remove the directories, and pipes the result to an awk command to print a few fields. Then it pipes the fields to a sort command, and finally, pipes the sorted output to another awk command that prints out the sorted list. (For more about this technique, see this article.)

The refactored code using a variation of the Pipes and Filters pattern is cleaner, as you can see in the code below and in Figure 3. Notice that I had to declare the NodeParse object np before using it in the get matched method. I can fix this problem by adding a static method to NodeParse. Unfortunately, that would complicate the class.

NodeParse np = new NodeParse(name); mainMatch = MatchAssociationNodes.getMatched(np); IDictionaryEnumerator matchEnum = mainMatch.GetEnumerator(); while (matchEnum.MoveNext()) { tAssociation = (Association)matchEnum.Value; System.Console.WriteLine("Association First= " + tAssociation.end1 + " : " + tAssociation.name1ID); System.Console.WriteLine("Association Second= " + tAssociation.end2 + " : " + tAssociation.name2ID); }

Two Servers Linked Through Another Server
Figure 5. Output after Searching for Dependencies: The output clearly shows that the CRM server depends on the middleware server that depends on the HR server.
You can extend the example to support transitive links. A transitive link is one where there an intermediate server has links between two different servers. In the beginning of the article I mentioned that we could check for dependencies using deployment diagrams. Some dependencies are hidden behind another server, as shown in Figure 4.

Refactoring the same code snippet that used before, it's now clear that the Pipes and Filters pattern is less complicated then encapsulating the new functionality into the MatchAssociationNode class, or passing the hash tables to other functions in the main program. Here's the revised code:

NodeParse np = new NodeParse(name); mainMatch = NodeList.getNodeList( MatchAssociationNodes.getMatched(np)); IDictionaryEnumerator matchEnum = mainMatch.GetEnumerator(); while (matchEnum.MoveNext()) { System.Console.WriteLine((string)matchEnum.Key); System.Console.WriteLine((string)matchEnum.Value); }

Figure 5 shows the output.

Figure 6. Servers Connected in a More Complicated Topology: Notice that the middleware server has three connections.
In searching for dependencies the code matches the second node of the first link with the first node of the second link. As long as the servers are in a single tier (having one server connect to another server with each server having a maximum of two connections) that code will find all the dependencies.

But what happens with a more complicated topology, where some of the servers have more than two connections, such as that shown in Figure 6?

When applied to this model, the code no longer finds every connection. The WebInterface server is connected to the Middleware server but is also transitively connected to the HR and CRM servers. But the code finds only the WebInterface connection to the CRM server, as shown in Figure 7. The links should also be bidirectional but that is simple to refactor.

Even though the code in this article doesn't find every possible transitive link and display bidirectionality, if you need those capabilities, you should be able to build on the code shown to meet your specific needs for reading XMI. For example, you may find collecting the node and association information useful when computing the shortest path from one server to another, determining critical points in the enterprise, and calculating performance numbers.

Figure 7. Output from a More Complicated Server Topology. Note that all the servers are represented, but that the Web interface to middleware to HR dependency is missing from this list, as are bidirectional links.
This article showed how to use C# to pull out dependency information from a UML deployment diagram. This works well enough, but becomes complicated when there are transitive dependencies to consider. You also saw how to use the Pipes and Filters pattern to reduce the complications when adding functionality to a program. The next article in this series will discuss components and how they relate to nodes.

Mark Goetsch is an Enterprise Software Architect with Wheels, Inc, and has more than fifteen years of experience in software development, enterprise modeling, and software architecture. He has another seven years experience as a trader, dealer, Broker, and an expert in e-trading. He was one of the enterprise modelers of the Tapestry Project at ABNAMRO, one of the most extensive uses of UML component and deployment diagrams to date. He is the lead architect for the MAP (Meta-Architectural Processes) framework, which is a framework for mapping the role of the software architect into software development processes. Mark is certified in Intermediate UML with the OMG and a member of WWISA. He also has a Masters in Distributed Systems from DePaul University.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date