A Closer Look at the Extended Profiles Resource
A Closer Look at the Extended Profiles Resource
In this section, we take a closer look at how to add linking and reading strategies to a resource extension. By adding a link strategy, you can insert links pointing to your resource in other resources, which client developers can use to navigate to your resource.
By adding a read strategy, your resource can extract data from another resource's representation. This is how you can extend the functionality of an existing resource. You don't modify the existing resource's code; you add your customizations to a new resource and use a read strategy to get data from the existing resource's representation.
Implementing a Link Strategy
All link strategies implement the LinkCommandStrategy interface, which has two methods: createLinks() and getSupportedType().
The createLinks() method creates a link to the resource implementing the link strategy and has two parameters: the representation the link is inserted into, and the resourceOperator.
The createLinks() method in the ExtensionProfiles resource is shown below:
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>extension<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>extensionprofile<span class="j-pathsep">/</span>command<span class="j-pathsep">/</span>impl<span class="j-pathsep">/</span>LinkToExtensionProfileStrategy.java</pre> <pre class="java"><span class="j-jdoc">/** * Create link kernel will use to attach to profile representation. * * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">profile the profile * </span><span class="j-jdoc-key">@return </span><span class="j-jdoc">Link to attach to profile representation */ </span><span class="j-key">public </span>Collection<ResourceLink> createLinks<span class="j-sym">(</span><span class="j-key">final </span>Representation profile<span class="j-sym">) { </span><span class="j-blk">/* * Create the link to the extension profile resource by correctly concatenating the root * resource name (extensionprofile) with the specific profile URI (/profiles/scope/id). */ </span>String resourceUri = URIUtil.format<span class="j-sym">(</span>resourceServerName, RepresentationUtil.getSelfUri<span class="j-sym">(</span>profile<span class="j-sym">))</span>; ResourceLink link = ResourceLinkFactory.create<span class="j-sym">(</span>resourceUri, ExtensionProfileRepresentation.TYPE, ExtensionProfileResourceRels.EXTENSION_PROFILE_REL, ExtensionProfileResourceRels.PROFILE_REV<span class="j-sym">)</span>; <span class="j-key">return </span>Collections.singleton<span class="j-sym">(</span>link<span class="j-sym">)</span>; <span class="j-sym">}</span></pre> </body></html>
The getSupportedType() method tells the Cortex API kernel which representation to insert the link into. When a representation passes through the kernel, the kernel calls getSupportedType() on all link strategies. When the type returned by a getSupportedType() method matches the representation in the kernel, the kernel runs the link strategy and inserts the link.
For the ExtensionProfiles resource, getSupportedType() tells the kernel to insert the link into the profiles representation.
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>extension<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>extensionprofile<span class="j-pathsep">/</span>command<span class="j-pathsep">/</span>impl<span class="j-pathsep">/</span>LinkToExtensionProfileStrategy.java</pre> <pre class="java"><span class="j-jdoc">/** * </span><span class="j-jdoc-key">@return </span><span class="j-jdoc">The representation type; tells the kernel which resource representation to attach links to. */ </span><span class="j-key">public </span>String getSupportedType<span class="j-sym">() { </span><span class="j-key">return </span>ProfileRepresentation.TYPE; <span class="j-sym">}</span></pre> </body></html>
To apply the link strategy, the ExtensionProfiles resource operator is extended with the AbstractLinkStrategyAwareResourceOperator class, and a LinkCommandWithStrategiesBuilderProvider, defined in the linkToExtensionProfileCommandBuilder bean, is injected.
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>extension<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>extensionprofile<span class="j-pathsep">/</span>impl<span class="j-pathsep">/</span>ExtensionProfileResourceOperatorImpl.java</pre> <pre class="java"><span class="j-jdoc">/** * Processes the resource operations on ExtensionProfile. */ </span>@Singleton @Named<span class="j-sym">(</span><span class="j-str">"extensionProfileResourceOperator"</span><span class="j-sym">) </span>@Path<span class="j-sym">(</span>ResourceName.PATH_PART<span class="j-sym">) </span><span class="j-key">public class </span>ExtensionProfileResourceOperatorImpl <span class="j-key">extends </span>LinkStrategyAwareResourceOperator <span class="j-sym">{ </span><span class="j-key">private final </span>Provider<ReadFromOtherCommandWithStrategies.Builder> readCommandBuilderProvider; <span class="j-jdoc">/** * Constructor. Injecting via constructor is easier to test and more performant. * * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">linkCommandBuilder Link Command Builder. * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">readCommandBuilderProvider read command builder provider. */ </span>@Inject ExtensionProfileResourceOperatorImpl<span class="j-sym">( </span>@Named<span class="j-sym">(</span><span class="j-str">"linkToExtensionProfileCommandBuilder"</span><span class="j-sym">) </span><span class="j-key">final </span>LinkCommandWithStrategiesBuilderProvider linkCommandBuilder, @Named<span class="j-sym">(</span><span class="j-str">"readExtensionProfileCommandBuilder"</span><span class="j-sym">) </span><span class="j-key">final </span>Provider<ReadFromOtherCommandWithStrategies.Builder> readCommandBuilderProvider<span class="j-sym">) { </span><span class="j-key">super</span><span class="j-sym">(</span>linkCommandBuilder<span class="j-sym">)</span>; <span class="j-key">this</span>.readCommandBuilderProvider = readCommandBuilderProvider; <span class="j-sym">}</span></pre> </body></html>
The injected LinkCommandWithStrategiesBuilderProvider specifies the link strategy that creates the link.
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>spring<span class="j-pathsep">/</span>applicationContext-extensionprofile-resource.xml</pre> <pre class="j-text"><code><!-- Linking strategy kernel uses to attach links to hello world resource on other resource representations --> <bean name="linkToExtensionProfileCommandBuilder" class="com.elasticpath.rest.command.link.LinkCommandWithStrategiesBuilderProvider"> <constructor-arg> <array value-type="com.elasticpath.rest.command.link.LinkCommandStrategy"> <ref bean="linkToExtensionProfileStrategy"/commerce-legacy/> </array> </constructor-arg> </bean></code></pre> </body></html>
TheLinkStrategyAwareResourceOperator defines the processLink() method, which the kernel uses to execute the link strategy.
<html><body> <pre class="j-path">commons<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>elasticpath<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resource<span class="j-pathsep">/</span>dispatch<span class="j-pathsep">/</span>operator<span class="j-pathsep">/</span>LinkStrategyAwareResourceOperator.java</pre> <pre class="java"><span class="j-bkg"> </span><span class="j-jdoc">/** * Checks whether links should be added for a resource, and returns a collection of links to add. * Overridden in each Resource that supports linking. * * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">operation the resource operation. * </span><span class="j-jdoc-key">@return </span><span class="j-jdoc">the collection of resource links. */ </span>@Path<span class="j-sym">(</span><span class="j-str">"{path:.*}"</span><span class="j-sym">) </span>@OperationType<span class="j-sym">(</span>Operation.LINK<span class="j-sym">) </span><span class="j-key">public </span>OperationResult processLink<span class="j-sym">( </span><span class="j-key">final </span>ResourceOperation operation<span class="j-sym">) { </span>Representation rep = operation.getRepresentation<span class="j-sym">()</span>; Command<Collection<ResourceLink>> cmd = linkCommandBuilderProvider.get<span class="j-sym">() </span>.setRepresentation<span class="j-sym">(</span>rep<span class="j-sym">) </span>.build<span class="j-sym">()</span>; ExecutionResult<Collection<ResourceLink>> linkResult = cmd.execute<span class="j-sym">()</span>; <span class="j-key">return </span>processLinksToAdd<span class="j-sym">(</span>linkResult.getData<span class="j-sym">()</span>, operation<span class="j-sym">)</span>; <span class="j-sym">}</span></pre> </body></html>
Pulling Profile Information into ExtensionProfile
The ExtensionProfile resource echoes both the user's profileId and user name from the representation returned by the profiles resource. To do this, the ExtensionProfile resource implements a read strategy. A read strategy is required to read the output from other resources.
The ExtensionProfile read strategy's execute method, shown below, extracts information from a profiles representation and sets information into ExtensionProfile's representation:
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>extension<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>extensionprofile<span class="j-pathsep">/</span>command<span class="j-pathsep">/</span>impl<span class="j-pathsep">/</span>ReadProfileStrategy.java</pre> <pre class="java"><span class="j-bkg"> </span>@Override <span class="j-key">public </span>ExecutionResult<Representation> execute<span class="j-sym">(</span><span class="j-key">final </span>Representation profile<span class="j-sym">) { </span><span class="j-blk">/*Profile representation returned from the kernel.*/ </span>ProfileRepresentation profileRepresentation = ResourceTypeFactory.adaptRepresentation<span class="j-sym">(</span>profile, ProfileRepresentation.<span class="j-key">class</span><span class="j-sym">)</span>; Self profileSelf = profileRepresentation.getSelf<span class="j-sym">()</span>; String uri = URIUtil.format<span class="j-sym">(</span>resourceServerName, profileSelf.getUri<span class="j-sym">())</span>; Self self = SelfFactory.createSelf<span class="j-sym">(</span>uri, ExtensionProfileRepresentation.TYPE, <span class="j-num">0</span><span class="j-sym">)</span>; ExtensionProfileMutator extensionProfileMutator = ExtensionProfileMutator.create<span class="j-sym">()</span>; extensionProfileMutator.setProfileId<span class="j-sym">(</span>profileRepresentation.getProfileId<span class="j-sym">()) </span>.setFamilyName<span class="j-sym">(</span>profileRepresentation.getLastName<span class="j-sym">()) </span>.setName<span class="j-sym">(</span>profileRepresentation.getFirstName<span class="j-sym">()) </span>.setSelf<span class="j-sym">(</span>self<span class="j-sym">)</span>; ResourceLink linkToProfile = ResourceLinkFactory.createFromSelf<span class="j-sym">(</span>profileSelf, ExtensionProfileResourceRels.PROFILE_REL, ExtensionProfileResourceRels.EXTENSION_PROFILE_REL<span class="j-sym">)</span>; extensionProfileMutator.addLink<span class="j-sym">(</span>linkToProfile<span class="j-sym">)</span>; Representation extProfileRepresentation = extensionProfileMutator.getRepresentation<span class="j-sym">()</span>; <span class="j-key">return </span>ExecutionResultFactory.createReadOK<span class="j-sym">(</span>extProfileRepresentation<span class="j-sym">)</span>; <span class="j-sym">}</span></pre> </body></html>
Like the link strategy, the read strategy is injected into the resource operator. This time, a readExtensionProfileCommandBuilder is responsible for injecting the read strategy.
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>extension<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>extensionprofile<span class="j-pathsep">/</span>impl<span class="j-pathsep">/</span>ExtensionProfileResourceOperatorImpl.java</pre> <pre class="java"><span class="j-jdoc">/** * Processes the resource operations on ExtensionProfile. */ </span>@Singleton @Named<span class="j-sym">(</span><span class="j-str">"extensionProfileResourceOperator"</span><span class="j-sym">) </span>@Path<span class="j-sym">(</span>ResourceName.PATH_PART<span class="j-sym">) </span><span class="j-key">public class </span>ExtensionProfileResourceOperatorImpl <span class="j-key">extends </span>LinkStrategyAwareResourceOperator <span class="j-sym">{ </span><span class="j-key">private final </span>Provider<ReadFromOtherCommandWithStrategies.Builder> readCommandBuilderProvider; <span class="j-jdoc">/** * Constructor. Injecting via constructor is easier to test and more performant. * * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">linkCommandBuilder Link Command Builder. * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">readCommandBuilderProvider read command builder provider. */ </span>@Inject ExtensionProfileResourceOperatorImpl<span class="j-sym">( </span>@Named<span class="j-sym">(</span><span class="j-str">"linkToExtensionProfileCommandBuilder"</span><span class="j-sym">) </span><span class="j-key">final </span>LinkCommandWithStrategiesBuilderProvider linkCommandBuilder, @Named<span class="j-sym">(</span><span class="j-str">"readExtensionProfileCommandBuilder"</span><span class="j-sym">) </span><span class="j-key">final </span>Provider<ReadFromOtherCommandWithStrategies.Builder> readCommandBuilderProvider<span class="j-sym">) { </span><span class="j-key">super</span><span class="j-sym">(</span>linkCommandBuilder<span class="j-sym">)</span>; <span class="j-key">this</span>.readCommandBuilderProvider = readCommandBuilderProvider; <span class="j-sym">}</span></pre> </body></html>
Unlike the linkToExtensionProfileCommandBuilder, the readExtensionProfileCommandBuilder isn't a bean. This is because command builder classes, which are responsible for passing in input to the command, are different for each resource's CRUD commands. With link strategies, the same input (the list of link strategies a resource has) is passed into the link command, so they all use the same LinkCommandWithStrategiesBuilderProvider class.
The ExtensionProfile's command builder readExtensionProfileCommandBuilder is shown below. It's injected with the readExtensionProfileStrategies bean and passes a list of read strategies into the read command.
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>extension<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>extensionprofile<span class="j-pathsep">/</span>command<span class="j-pathsep">/</span>impl<span class="j-pathsep">/</span>ReadExtensionProfileCommandBuilder.java</pre> <pre class="java"><span class="j-jdoc">/** * Constructor. * * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">rfoBuilder ReadFromOtherCommandWithStrategies builder. * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">strategies Strategies map. */ </span>@SuppressWarnings<span class="j-sym">(</span><span class="j-str">"PMD.LooseCoupling"</span><span class="j-sym">) </span>@Inject ReadExtensionProfileCommandBuilder<span class="j-sym">( </span>@Named<span class="j-sym">(</span><span class="j-str">"readFromOtherCommandWithStrategiesBuilder"</span><span class="j-sym">) </span><span class="j-key">final </span>ReadFromOtherCommandWithStrategies.Builder rfoBuilder, @Named<span class="j-sym">(</span><span class="j-str">"readExtensionProfileStrategies"</span><span class="j-sym">) </span><span class="j-key">final </span>HashMap<String, ReadFromOtherCommandStrategy> strategies<span class="j-sym">) { </span><span class="j-key">super</span><span class="j-sym">(</span>rfoBuilder, strategies<span class="j-sym">)</span>; <span class="j-sym">}</span></pre> </body></html>
The readExtensionProfileStrategies bean is defined in applicationContext-resource-server.xml. It takes a read strategy as a constructor argument as shown:
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>spring<span class="j-pathsep">/</span>applicationContext-extensionprofile-resource.xml</pre> <pre class="j-text"><code><!-- Strategy used to read an extension profile representation --> <bean name="readExtensionProfileStrategies" class="java.util.HashMap"> <constructor-arg> <map key-type="java.lang.String" value-type="com.elasticpath.rest.command.read.ReadFromOtherCommandStrategy"> <entry key="application/vnd.elasticpath.profile" value-ref="readProfileStrategy"/commerce-legacy/> </map> </constructor-arg> </bean></code></pre> </body></html>
The read strategy executes when the resource operator's processRead() method is called:
<html><body> <pre class="j-path">rest-resource-extensionprofile<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>java<span class="j-pathsep">/</span>com<span class="j-pathsep">/</span>extension<span class="j-pathsep">/</span>rest<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>extensionprofile<span class="j-pathsep">/</span>impl<span class="j-pathsep">/</span>ExtensionProfileResourceOperatorImpl.java</pre> <pre class="java"><span class="j-bkg"> </span><span class="j-jdoc">/** * Handles a request to the extension profile resource. * * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">resourceName resource server name. * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">resourceUri the resource uri * </span><span class="j-jdoc-key">@param </span><span class="j-jdoc">operation The Resource Operation * </span><span class="j-jdoc-key">@return </span><span class="j-jdoc">the operation result */ </span>@Path<span class="j-sym">(</span>ResourceUri.PATH_PART<span class="j-sym">) </span>@OperationType<span class="j-sym">(</span>Operation.READ<span class="j-sym">) </span><span class="j-key">public </span>OperationResult processRead<span class="j-sym">( </span>@ResourceName <span class="j-key">final </span>String resourceName, @ResourceUri <span class="j-key">final </span>String resourceUri, <span class="j-key">final </span>ResourceOperation operation<span class="j-sym">) { </span>Command<Representation> cmd = readCommandBuilderProvider.get<span class="j-sym">() </span>.setResourceUri<span class="j-sym">(</span>URIUtil.normalize<span class="j-sym">(</span>resourceUri<span class="j-sym">)) </span>.setRedirectPrefix<span class="j-sym">(</span>resourceName<span class="j-sym">) </span>.build<span class="j-sym">()</span>; ExecutionResult<Representation> result = cmd.execute<span class="j-sym">()</span>; <span class="j-key">return </span>OperationResultFactory.create<span class="j-sym">(</span>result, operation<span class="j-sym">)</span>; <span class="j-sym">}</span></pre> </body></html>