Blog
Skip to end of metadata
Go to start of metadata

A JPA implementációk (és elődjük - a Hibernate) nagyon sok felesleges munkát levettek az ember válláról, s az egyszerűbb ORM műveletek mellé idővel - az adatbázisokkal való munka során felmerült problémák megoldásaként - egyre több eszköz került. Ilyen eszköz az Entity és/vagy Query Cache, amely a már beolvasott entitást adta vissza az adatbázishoz való kérdés nélkül, illetve a már lefuttatott kérdésre az adatbázisból egyszer már elkért halmazt adta vissza ugyanilyen módon.
Egy alkalmazáson belül tökéletesen dolgozott a gyorstár, hiszen csak az alkalmazás használta az entitásokat, s az alkalmazás alatti EntityManager képes volt a változásokat átvezetni a Cache területre is, így az itt tárolt entitások mindig friss és aktuális állapotban voltak.

A probléma az elosztott rendszereknél jelentkezett, mivel ezek több - egymástól lazán véve független - gépeken futottak, amelyekben az EntityManager mit sem sejtett a többi menedzser dolgáról és a másik tag által módosított entitásokról még a régi képet festette, így a cluster tagjai tartalomban elcsúsztak egymástól, a rendszer összetettsége szerint csekély - de potenciálisan durva - hibákat okozva ezzel a felhasználónak, hiszen azok nem azt látták, ami a rendszer egészéből következett volna.

A megoldás a JPA Cache összehangolása a cluster tagjain valamilyen közvetítő médiumon át, amely lehet JMS, RMI vagy CORBA. Az EclipseLink eléggé aluldokumentált ezen a téren és nehezen felderíthető a megoldáshoz vezető út, ezért nézzük az egyik - működő - megoldást, amikor JMS Topic Queue a közvetítő.

Szükség van az entitások annotációjára, amelyben feljegyezzük az EclipseLink számára a gyorstár paramétereit. Ez az annotáció a org.eclipse.persistence.annotations.Cache, amely egyelőre provider függőséget hoz a rendszerbe, de nemsokára - mint a JPA 2.0 specifikáció referencia implementációja - az EclipseLink a szebben mutató javax.persistence.Cache annotáció alatt fogja elhelyezni:

@Cache(coordinationType = CacheCoordinationType.SEND_OBJECT_CHANGES, disableHits = true, expiry = 300000, shared = true, type = CacheType.FULL)

Természetesen ez még nem elég, szükség van egy úgynevezett Session Customizer osztályra, amely a megfelelő beállításokkal látja el az EclipseLink Session példányát, ez egy konkrét implementációban az alábbi lehet:

/**
 * CC-LGPL 2.1
 * http://creativecommons.org/licenses/LGPL/2.1/
 */
package hu.javaforum.commons;

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.config.SessionCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.coordination.CommandProcessor;
import org.eclipse.persistence.sessions.coordination.RemoteCommandManager;
import org.eclipse.persistence.sessions.coordination.jms.JMSTopicTransportManager;

/**
 * JMS based Cache Coordination
 *
 * Changelog:
 * JFPORTAL-79 (2009. szeptember 21.)
 *
 * @author Auth Gábor <auth.gabor@javaforum.hu>
 */
public class JMSCacheCoordinatorSession implements SessionCustomizer, DescriptorCustomizer
{

  /**
   * Statikus naplózó példány
   */
  protected static final Logger logger = new Logger(JMSCacheCoordinatorSession.class);

  /**
   * Customize EclipseLink Session instance
   *
   * @param session The session
   * @throws Exception
   */
  @Override
  public void customize(Session session) throws Exception
  {
    PerfLogger perfLogger = new PerfLogger(logger);
    perfLogger.entryLog();
    try
    {
      perfLogger.doLogDebug("Before Session Customization");

      CommandProcessor commandProcessor = (CommandProcessor) session;
      DatabaseSession databaseSession = (DatabaseSession) session;

      RemoteCommandManager rcm = new RemoteCommandManager(commandProcessor);

      JMSTopicTransportManager tm = new JMSTopicTransportManager(rcm);
      tm.setTopicHostUrl("jnp://localhost:1100");
      tm.setInitialContextFactoryName("org.jboss.naming.NamingContextFactory");
      tm.setTopicName("topic/jfportal");
      tm.setTopicConnectionFactoryName("ClusteredXAConnectionFactory");
      tm.setUserName("guest");
      tm.setPassword("guest");

      rcm.setTransportManager(tm);

      databaseSession.setCommandManager(rcm);
      databaseSession.setShouldPropagateChanges(true);

      rcm.initialize();

      perfLogger.doLogDebug("After Session Customization");
    } catch (Exception except)
    {
      perfLogger.doLogError(except.toString(), except);
      throw except;
    } finally
    {
      perfLogger.exitLog();
    }
  }

  /**
   * Customize EclipseLink ClassDescriptor instance
   *
   * @param descriptor The descriptor
   * @throws Exception
   */
  @Override
  public void customize(ClassDescriptor descriptor) throws Exception
  {
    PerfLogger perfLogger = new PerfLogger(logger);
    perfLogger.entryLog();
    try
    {
      perfLogger.doLogDebug("Before ClassDescriptor Customization");

      perfLogger.doLogDebug("After ClassDescriptor Customization");
    } catch (Exception except)
    {
      perfLogger.doLogError(except.toString(), except);
      throw except;
    } finally
    {
      perfLogger.exitLog();
    }
  }
}

A megvalósítás lehet szebb is, itt éppenséggel beledrótoztuk a paramétereket az osztályba, ami nem szép dolog, de működik. :)

Elvileg az EclipseLink képes arra, hogy ugyanezen paramétereket a persistence.xml alapján olvassa be és használja fel, ekkor az alábbi módon kell ezeket megadnunk (de jelenleg nem működik sajnos):

  • "eclipselink.cache.coordination.protocol" = "jms"
  • "eclipselink.cache.coordination.jms.host" = "jnp://localhost:1100"
  • "eclipselink.cache.coordination.jms.topic" = "topic/jfportal"
  • "eclipselink.cache.coordination.jms.factory" = "ClusteredXAConnectionFactory"
  • "eclipselink.cache.coordination.naming-service" = "jndi"
  • "eclipselink.cache.coordination.jndi.user" = "guest"
  • "eclipselink.cache.coordination.jndi.password" = "guest"
  • "eclipselink.cache.coordination.jndi.initial-context-factory" = "org.jboss.naming.NamingContextFactory"

A megoldáshoz szükséges egy JMS Topic Queue, amelyet el lehet érni a cluster minden tagjáról. Ennek módszere egy független Message Queue cluster felépítése, vagy használjuk az alkalmazás szerverbe épített JMS megvalósítást, amelyet cluster alapokon kell beállítanunk, különben az EclipseLink Cache példányok önmaguknak üzengetnének, amelyre nincs szükség, hiszen magukkal könnyen szóbaállnak.

Ezek után az eredmény egy olyan JPA Cache, amely gyorsan és hatékonyan működik a cluster tagjain.

      
      
Page viewed times
#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))