Wednesday, September 19, 2012
PMI CVC PMP 2012 Fall Workshop - Communications and Risk Modules
I will be presenting on Sunday, October 7th, for the PMI Central Va Chapter during their Fall 2012 PMI Certification Workshop. My topics will be Project Communications Management and Project Risk Management.
PMI CVC PMP 2012 Fall Workshop - Framework Talk
I will be presenting on September 22nd for the PMI Central Va Chapter during their Fall 2012 PMI Certification Workshop. My topic will be the Project Management Framework. My talk goes from 10:15 AM to 11:45 AM and touches on items found in sections 1 & 2, chapters 1, 2, & 3, of the PMBOK, version 4.
In my session I will be discussing these topics and more:
Ever wondered how PMI keeps the PMP exam current and relevant? This year we have also added information of the PMI Role Delineation Study and the Crosswalk.
This workshop is a great way to come up to speed for the PMP exam as well as gain valuable study tips from fellow project managers that have already passed the exam. The workshop is also a great opportunity to gain the PDUs needed to maintain existing PMP certifications. Best of all, attendees receive copies of all the slides presented at the workshop as well as other resources to help them study for the exam.
Integration Module
I will also be supporting the Integration Module session that starts at 11:45 and continues after lunch to 14:00.
In my session I will be discussing these topics and more:
- Projects, Portfolios, and Program
- Process Groups, Knowledge Areas, and Processes
- PMO
- Project Life Cycle vs. Product Life Cycle
- Stakeholder Management
- Organizational Structure
Ever wondered how PMI keeps the PMP exam current and relevant? This year we have also added information of the PMI Role Delineation Study and the Crosswalk.
This workshop is a great way to come up to speed for the PMP exam as well as gain valuable study tips from fellow project managers that have already passed the exam. The workshop is also a great opportunity to gain the PDUs needed to maintain existing PMP certifications. Best of all, attendees receive copies of all the slides presented at the workshop as well as other resources to help them study for the exam.
Integration Module
I will also be supporting the Integration Module session that starts at 11:45 and continues after lunch to 14:00.
Thursday, September 6, 2012
Spring Provisional/Conditional Bean Loading (Part 2)
In Part1 I explained two solutions for dynamically loading beans based on environments. In this Part 2, I (quickly) extend the solution to handle conditional Bean Definition File imports. Below is my new main.xml with a new custom tag, <profile:importIf>. This new tag, along with the <profile:if> tag, will allow me to control individual bean and entire bean definition file loads via properties files.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oxm="http://www.springframework.org/schema/oxm" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:context="http://www.springframework.org/schema/context" xmlns:profile="http://icfi.com/springbeans/profile" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://icfi.com/springbeans/profile http://icfi.com/springbeans/profile/profile.xsd"> <import resource="beans.xml" /> <import resource="beans2.xml" /> <profile:if test="${Spring.ENV=='TEST'}" src="config.properties"> <bean id="testBean" class="com.icfi.spring.init.beans.TestBean" name="ibean"> <property name="valueOne" value="This is TEST." /> </bean> </profile:if> <profile:if test="${Spring.ENV=='PROD'}" src="config"> <bean id="prodBean" class="com.icfi.spring.init.beans.ProdBean" name="ibean"> <property name="valueOne" value="This is PROD." /> </bean> </profile:if> <profile:importIf test="${Spring.ENV=='DEV'}" src="config.properties" resource="context/DEV-beans.xml" /> </beans>To make this work I have to modify some additional artifacts, profile.xsd, ProfileBeanNamespaceHandler.java, and ProfileBeanDefinitionParser.java. These new artifacts are seen below.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://icfi.com/springbeans/profile" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://icfi.com/springbeans/profile" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:element name="if"> <xsd:complexType> <xsd:sequence> <xsd:any minOccurs="0" /> </xsd:sequence> <xsd:attribute name="test" type="xsd:string" use="required" /> <xsd:attribute name="src" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="importIf"> <xsd:complexType> <xsd:sequence> <xsd:any minOccurs="0" /> </xsd:sequence> <xsd:attribute name="test" type="xsd:string" use="required" /> <xsd:attribute name="src" type="xsd:string" use="required" /> <xsd:attribute name="resource" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:schema>
package com.icfi.springbeans.profile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class ProfileBeanNamespaceHandler extends NamespaceHandlerSupport { private static Logger log = LoggerFactory .getLogger(ProfileBeanNamespaceHandler.class); public void init() { log.debug(this.getClass().getSimpleName()+"::initStart"); super.registerBeanDefinitionParser("if", new ProfileBeanDefinitionParser()); super.registerBeanDefinitionParser("importIf", new ProfileBeanDefinitionParser()); log.debug(this.getClass().getSimpleName()+"::initEnd"); } }
package com.icfi.springbeans.profile; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class ProfileBeanDefinitionParser implements BeanDefinitionParser { private static Logger log = LoggerFactory .getLogger(ProfileBeanDefinitionParser.class); private ResourceBundle bundle; /** SpEL prefix: "${" */ public static final String PREFIX = "${"; /** SpEL suffix: "}" */ public static final String SUFFIX = "}"; /** * Parse the "if" element, check for the required "test" and "src" * attributes. */ public BeanDefinition parse(Element element, ParserContext parserContext) { try { if (DomUtils.nodeNameEquals(element, "if") || DomUtils.nodeNameEquals(element, "importIf")) { String test = element.getAttribute("test"); String src = element.getAttribute("src"); if (StringUtils.isNotEmpty(src)) { if (src.indexOf(".") > 0) { src = src.substring(0, src.indexOf(".")); } bundle = ResourceBundle.getBundle(src); } else { throw new IllegalArgumentException( "src attribute not found."); } if (StringUtils.isNotEmpty(test)) { Map<String, String> map = this.getExpressionMap(test); String left = this.bundle.getString(map.get("left")); String right = map.get("right"); if (left != null && right != null && left.equals(right)) { if (DomUtils.nodeNameEquals(element, "if")) { Element beanElement = DomUtils .getChildElementByTagName(element, "bean"); return registerBean(beanElement, parserContext); } else if (DomUtils.nodeNameEquals(element, "importIf")) { String resource = element.getAttribute("resource"); InputStream is = parserContext.getReaderContext() .getResourceLoader().getResource(resource) .getInputStream(); Document doc = this.parse(is); NodeList elements = doc .getElementsByTagName("bean"); for (int x = 0; x < elements.getLength(); x++) { Element bean = (Element) elements.item(x); this.registerBean(bean, parserContext); } } } } else { throw new IllegalArgumentException( "test attribute not found."); } } } catch (Exception e) { log.error(e.getMessage()); } return null; } private Map<String, String> getExpressionMap(String value) { Map<String, String> map = new HashMap<String, String>(); if (StringUtils.isEmpty(value)) { return null; } String entire = value.substring(PREFIX.length(), value.length() - SUFFIX.length()); String left = entire.substring(0, entire.indexOf("==")); String right = entire.substring(entire.indexOf('\'') + 1, entire.lastIndexOf('\'')); map.put("left", left); map.put("right", right); return map; } /* * Register Bean * * @param element * * @param parserContext * * @return */ private BeanDefinition registerBean(Element element, ParserContext parserContext) { BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); BeanDefinitionHolder holder = delegate .parseBeanDefinitionElement(element); BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry()); return holder.getBeanDefinition(); } /* * JAXP Parser * * @param is * * @return * * @throws Exception */ private Document parse(InputStream is) throws Exception { DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbfactory.newDocumentBuilder(); Document doc = builder.parse(is); return doc; } }This new solution will load individual beans based on conditions defined in Bean Definition Files, and will now import entire Bean Definition Files based on those same declared conditions. Of course the same caveats apply. Developers must manage bean ID and NAME collisions when using multiple conditions and imports.
Spring Provisional/Conditional Bean Loading (Part 1)
In Spring 3.1 (2011), profiles were introduced. These Bean Definition Profiles allow Spring developers to set up different profiles for loading different beans in different environments. I know several developers that are moving to 3.1 just for that feature.
However, what does one do if one needs to load different beans for different environments, but he/she cannot immediately upgrade to Spring 3.1? In this blog entry I will cover that scenario with two solutions from long ago that still work today in Spring 3.0.x.
Solution #1:
Far and away the easiest solution is to use config file properties, the Spring PropertyPlaceholderConfigurer, and Spring Expression Language (SpEL). Below is an XML snippet from a bean definition file. The code uses SpEL to get at the Spring.ENV property found in the config.properties, and referenced as the Spring container starts to load.
System properties can also be used in lieu of the PropertyPlaceholderConfigurer.
Solution #2:
This next solution is way more involved, requiring custom extensions to the Spring Container API, but is configuration properties file driven instead of system property driven. To understand this approach, we start with what we would like to see in the bean definition file. Below is the <profile:if> custom tag that will test whether certain beans are to be loaded.
The plumbing for this solution extends the Spring Container API via the Extensible XML Authoring API with Java and XML Schema (XSD), along with two additional Spring configuration files. It is rather complex, but I will try to simplify it here.
The Extensible XML Authoring API (EXAA for short) has been around since Spring 2.0. However, it never really got the visibility that other Spring features enjoyed. With EXAA, there are four steps to extending the API:
The ProfileBeanDefinitionParser is referenced in the handler. This parser does all the heavy lifting of parsing the custom tag, verifying the attributes, loading the defined resource bundle (properties file), testing the condition for bean load, parsing the bean definition, and then registering the bean with Spring. Below is the custom Bean Definition Parser that I wrote.
tag, test attribute. This file is found at the root of the src/main/resources Maven layout.
Below is a test class, SpringInitMotivator, that I used to exercise this solution, Also seen below is the main.xml used to configure Spring in my application. The SpringInitMotivator uses a helper class, SpringBeanFactory, to get at beans. For this demo, I use the IBeans interface, implemented by the DevBean and ProdBean classes. I load the beans via the name attribute and not the id, as multiple beans can not have the same ID, and I wanted to use a common name for both bean load conditions.
However, what does one do if one needs to load different beans for different environments, but he/she cannot immediately upgrade to Spring 3.1? In this blog entry I will cover that scenario with two solutions from long ago that still work today in Spring 3.0.x.
Solution #1:
Far and away the easiest solution is to use config file properties, the Spring PropertyPlaceholderConfigurer, and Spring Expression Language (SpEL). Below is an XML snippet from a bean definition file. The code uses SpEL to get at the Spring.ENV property found in the config.properties, and referenced as the Spring container starts to load.
…<bean id="icfi.propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config.properties</value> </list> </property> <property name="ignoreUnresolvablePlaceholders" value="false" /> </bean> … <import resource="beans.xml" /> <import resource="beans2.xml" /> <import resource="${Spring.ENV}-beans.xml" />…As long as the property is located before the Spring Container starts to load bean definitions, this will work to load files whose names match the resource arguments. This solution has issues, not the least of which are that properties must be used and they must be coordinated to match file names related to bean definition files.
System properties can also be used in lieu of the PropertyPlaceholderConfigurer.
Solution #2:
This next solution is way more involved, requiring custom extensions to the Spring Container API, but is configuration properties file driven instead of system property driven. To understand this approach, we start with what we would like to see in the bean definition file. Below is the <profile:if> custom tag that will test whether certain beans are to be loaded.
<profile:if test="${Spring.ENV=='DEV'}" src="config.properties"> <bean id="devBean" class="com.icfi.spring.init.beans.DevBean" name="ibean"> <property name="valueOne" value="This is DEV." /> </bean> </profile:if>In this example, the <profile:if> tag tests to see if the Spring.ENV property has the required value of "DEV" to load this bean. The Spring.ENV property should be found in the config.properties resource bundle that is declared in the src attribute of the <profile:if> tag. If the test passes (Spring.ENV == 'DEV') then the devBean will be loaded.
The plumbing for this solution extends the Spring Container API via the Extensible XML Authoring API with Java and XML Schema (XSD), along with two additional Spring configuration files. It is rather complex, but I will try to simplify it here.
The Extensible XML Authoring API (EXAA for short) has been around since Spring 2.0. However, it never really got the visibility that other Spring features enjoyed. With EXAA, there are four steps to extending the API:
- Authoring XML schemas
- Coding Namespace handlers
- Coding BeanDefinitionParser extensions
- Registering the XSD and code artifacts with the Spring Container
I will start will authoring the schema. Below is the XSD that defines the new custom tags we want to use. As a point of reference, I built this solution in Eclipse Helios SR2 using Maven. I placed the new profile.xsd into directory in the image below.
The contents of the schema are seen below. In this XSD I defined the target namespace (http://icfi.com/springbeans/profile) and the custom tag (if) with required attributes, test and src.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://icfi.com/springbeans/profile" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://icfi.com/springbeans/profile" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:element name="if"> <xsd:complexType> <xsd:sequence> <xsd:any minOccurs="0" /> </xsd:sequence> <xsd:attribute name="test" type="xsd:string" use="required" /> <xsd:attribute name="src" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:schema>Once the XSD was done, I needed to register the schema using the appropriate properties file, spring.schemas, in the META-INF directory.
http\://icfi.com/springbeans/profile/profile.xsd=com/icfi/springbeans/profile/profile.xsdNext, I needed to code the handler and parser. The handler, a simple class as it turns out, is used by the Spring Container to register the new custom Bean Definition Parser. My handler is below.
package com.icfi.springbeans.profile; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class ProfileBeanNamespaceHandler extends NamespaceHandlerSupport { public void init() { super.registerBeanDefinitionParser("if", new ProfileBeanDefinitionParser()); } }Next I needed to register the handler. For this I created the appropriate properties file, spring.handlers, in the META-INF directory.
http\://icfi.com/springbeans/profile=com.icfi.springbeans.profile.ProfileBeanNamespaceHandlerBelow is the location in my Maven layout for both registration properties files mentioned so far.
The ProfileBeanDefinitionParser is referenced in the handler. This parser does all the heavy lifting of parsing the custom tag, verifying the attributes, loading the defined resource bundle (properties file), testing the condition for bean load, parsing the bean definition, and then registering the bean with Spring. Below is the custom Bean Definition Parser that I wrote.
package com.icfi.springbeans.profile; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; public class ProfileBeanDefinitionParser implements BeanDefinitionParser { private static Logger log = LoggerFactory .getLogger(ProfileBeanDefinitionParser.class); private ResourceBundle bundle; /** SpEL prefix: "${" */ public static final String PREFIX = "${"; /** SpEL suffix: "}" */ public static final String SUFFIX = "}"; /** * Parse the "if" element, check for the required "test" and "src" attributes. */ public BeanDefinition parse(Element element, ParserContext parserContext) { try { if (DomUtils.nodeNameEquals(element, "if")) { String test = element.getAttribute("test"); String src = element.getAttribute("src"); if (StringUtils.isNotEmpty(src)) { if (src.indexOf(".") > 0) { src = src.substring(0, src.indexOf(".")); } bundle = ResourceBundle.getBundle(src); } else { throw new IllegalArgumentException( "src attribute not found."); } if (StringUtils.isNotEmpty(test)) { Map<String, String> map = this.getExpressionMap(test); String left = this.bundle.getString(map.get("left")); String right = map.get("right"); if (left != null && right != null && left.equals(right)) { Element beanElement = DomUtils .getChildElementByTagName(element, "bean"); return parseRegisterBean(beanElement, parserContext); } } else { throw new IllegalArgumentException( "test attribute not found."); } } } catch (Exception e) { log.error(e.getMessage()); } return null; } private Map<String, String> getExpressionMap(String value) { Map<String, String> map = new HashMap<String, String>(); if (StringUtils.isEmpty(value)) { return null; } String entire = value.substring(PREFIX.length(), value.length() - SUFFIX.length()); String left = entire.substring(0, entire.indexOf("==")); String right = entire.substring(entire.indexOf('\'') + 1, entire.lastIndexOf('\'')); map.put("left", left); map.put("right", right); return map; } private BeanDefinition parseRegisterBean(Element element, ParserContext parserContext) { BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); BeanDefinitionHolder holder = delegate .parseBeanDefinitionElement(element); BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry()); return holder.getBeanDefinition(); } }The config.properties file contains the Spring.ENV property that will be used to test the condition defined in the
package com.icfi.spring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import com.icfi.spring.init.beans.IBeans; public class SpringInitMotivator { private static Logger log = LoggerFactory .getLogger(SpringInitMotivator.class); public static void main(String[] args) { ApplicationContext ctx = new GenericXmlApplicationContext( "context/main.xml"); // Spring 3.0 IBeans ibean = (IBeans) SpringBeanFactory.getBean("ibean"); log.info(ibean.getValueOne()); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oxm="http://www.springframework.org/schema/oxm" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:context="http://www.springframework.org/schema/context" xmlns:profile="http://icfi.com/springbeans/profile" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://icfi.com/springbeans/profile http://icfi.com/springbeans/profile/profile.xsd"> <import resource="beans.xml" /> <import resource="beans2.xml" /> <profile:if test="${Spring.ENV=='DEV'}" src="config.properties"> <!-- <import resource="development-beans.xml" /> --> <bean id="devBean" class="com.icfi.spring.init.beans.DevBean" name="ibean"> <property name="valueOne" value="This is DEV." /> </bean> </profile:if> <profile:if test="${Spring.ENV=='PROD'}" src="config"> <!-- <import resource="development-beans.xml" /> --> <bean id="prodBean" class="com.icfi.spring.init.beans.ProdBean" name="ibean"> <property name="valueOne" value="This is PROD." /> </bean> </profile:if> </beans>
Subscribe to:
Posts (Atom)