XAP 9.6 Documentation > Back to Table of Contents
Step 2 - Using the Power of the Space to Scale Your Data Access Layer
Summary: This step explains how to utilize the power of the grid when implementing the data access layer of the PetClinic application (namely the
Clinic interface), and seamlessly use distributed computing paradigms such as Map/Reduce and data and processing colocation
Before You BeginWe recommend that you do the following before starting this step of the Tutorial:
Preparing the PetClinicServiceThe PetClinicService is an implementation of the Clinic interface that the Spring MVC layer of the application is using. The easiest way to implement this service is simply define an EntityManagerFactory that accesses the remote space from within the web application (similar the traditional database backed implementation). But in our case we want to be able to take advantage of the built in capabilities that GigaSpaces XAP provides for scaling your business logic:
We can easily achieve all of the above by using the GigaSpaces XAP Space Based Remoting support, and making a few small adaptations to the clinic interface (adding a few annotations to it). Adjusting the Clinic InterfaceAs mentioned above, we will use Space Based Remoting to back the Clinic interface. The actual implementation will run on each of the Space nodes we will deploy. The first thing we need to do is declare for each operation (method) whether it will be sent to the entire cluster or to a specific Space partition. In case an operation is sent to the entire cluster, we also need to tell the client how to aggregate (or reduce) the results on from all cluster members. This is done using a reducer class which implements the RemoteResultReducer interface. All of the above is done by applying the @ExecutorRemotingMethod annotation (new in 8.0.1) to each of the methods. This annotation has several attributes:
The Clinic service has 3 reducers: the getVets() method uses the GetVetsReducer class. The findOwners() method uses the FindOwnersReducer class, and the loadPet() method uses the LoadPetResultReducer class. Finally, we specify a RemoteInvocationAspect implementation which intercepts the Clinic methods before they are actually sent to the space for execution. In our case there are two aspects:
The Clinic Interface
... public interface Clinic { /** * Retrieve all <code>Vet</code>s from the data store. * @return a <code>Collection</code> of <code>Vet</code>s */ @ExecutorRemotingMethod(broadcast=true, remoteResultReducer = "getVetsReducer") Collection<Vet> getVets() throws DataAccessException; /** * Retrieve all <code>PetType</code>s from the data store. * @return a <code>Collection</code> of <code>PetType</code>s */ @ExecutorRemotingMethod(broadcast=false, remoteInvocationAspect="petTypesAspect") Collection<PetType> getPetTypes() throws DataAccessException; /** * Retrieve <code>Owner</code>s from the data store by last name, * returning all owners whose last name <i>starts</i> with the given name. * @param lastName Value to search for * @return a <code>Collection</code> of matching <code>Owner</code>s * (or an empty <code>Collection</code> if none found) */ @ExecutorRemotingMethod(broadcast=true, remoteResultReducer="findOwnersReducer") Collection<Owner> findOwners(String lastName) throws DataAccessException; ... The clinic service relies on a connection to the remote space. This connection is initialized in a Spring configuration file (or alternatively can be done within the application's code). The web application's Spring configuration file is located at WEB-INF/spring/applicationContext-gs.xml within the web application. See below the Spring configuration snippet.
WEB-INF/spring/applicationContext-gs.xml
... <os-core:space id="space" url="jini://*/*/petclinic" lookup-timeout="20000" lookup-groups="${user.name}"/> <os-core:distributed-tx-manager id="jiniTransactionManager" /> <os-core:giga-space id="petclinic" space="space" tx-manager="jiniTransactionManager"/> <bean id="dummyDataCreator" class="org.springframework.samples.petclinic.util.DummyDataCreator"> <property name="dataFileResource" value="classpath:META-INF/dummyData.json"/> </bean> <bean id="idGenerator" class="org.springframework.samples.petclinic.gigaspaces.idgen.IdGeneratorImpl"/> <bean id="idGeneratingInvocationAspect" class="org.springframework.samples.petclinic.gigaspaces.IdGeneratingInvocationAspect"/> <bean id="findOwnersReducer" class="org.springframework.samples.petclinic.gigaspaces.FindOwnersReducer"/> <bean id="getVetsReducer" class="org.springframework.samples.petclinic.gigaspaces.GetVetsReducer"/> <bean id="petTypesAspect" class="org.springframework.samples.petclinic.gigaspaces.PetTypesAspect"/> <bean id="loadPetResultReducer" class="org.springframework.samples.petclinic.gigaspaces.LoadPetResultReducer"/> <bean id="petRoutingHandler" class="org.springframework.samples.petclinic.gigaspaces.PetRoutingHandler"/> <bean id="visitRoutingHandler" class="org.springframework.samples.petclinic.gigaspaces.VisitRoutingHandler"/> <os-remoting:executor-proxy id="clinicProxy" giga-space="petclinic" interface="org.springframework.samples.petclinic.Clinic"/> <context:annotation-config/> <tx:annotation-driven transaction-manager="jiniTransactionManager"/> </beans> Changes to the Service Implementation:The following changes had been made to the service implementation:
The loadPet() Method
... @Transactional(readOnly = true) public Pet loadPet(int id) { Query query = em.createQuery("SELECT o FROM org.springframework.samples.petclinic.Owner o JOIN o.petsInternal p WHERE p.id = :id"); query.setParameter("id", id); List<Owner> owners = query.getResultList(); if (!owners.isEmpty()) { Owner owner = owners.get(0); for (Pet pet : owner.getPets()) { if (pet.getId() == id) { return pet; } } } return null; } ... The Id GeneratorThe Id Generator functionality is part of the processor module. It consists of the following classes:
The IdGeneratorImpl Class
... public class IdGeneratorImpl implements IdGenerator { @Resource private GigaSpace gigaSpace; private int currentId = 0; private int idLimit = -1; public IdGeneratorImpl(){} @Transactional(propagation= Propagation.REQUIRES_NEW) public synchronized Integer generateId() { if (currentId < 0 || currentId > idLimit) { getNextIdBatchFromSpace(); } return currentId++; } private void getNextIdBatchFromSpace() { IdCounterEntry idCounterEntry = gigaSpace.readById(IdCounterEntry.class,0,0, 5000, ReadModifiers.EXCLUSIVE_READ_LOCK); if (idCounterEntry == null) { throw new RuntimeException("Could not get ID object from Space"); } int[] range = idCounterEntry.getIdRange(); currentId = range[0]; idLimit = range[1]; gigaSpace.write(idCounterEntry, Lease.FOREVER, 5000, UpdateModifiers.UPDATE_ONLY); } } ... What's Next?
|
![]() |
GigaSpaces.com - Legal Notice - 3rd Party Licenses - Site Map - API Docs - Forum - Downloads - Blog - White Papers - Contact Tech Writing - Gen. by Atlassian Confluence |