View Javadoc

1   /* 
2    * Copyright (c) 2005-2011, Fraunhofer-Gesellschaft
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are
7    * met:
8    * 
9    * (1) Redistributions of source code must retain the above copyright
10   *     notice, this list of conditions and the disclaimer at the end.
11   *     Redistributions in binary form must reproduce the above copyright
12   *     notice, this list of conditions and the following disclaimer in
13   *     the documentation and/or other materials provided with the
14   *     distribution.
15   * 
16   * (2) Neither the name of Fraunhofer nor the names of its
17   *     contributors may be used to endorse or promote products derived
18   *     from this software without specific prior written permission.
19   * 
20   * DISCLAIMER
21   * 
22   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33   *  
34   */
35  package org.ogf.graap.wsag.server.persistence.impl;
36  
37  import java.lang.reflect.Constructor;
38  import java.text.MessageFormat;
39  import java.util.Iterator;
40  import java.util.Map;
41  import java.util.Observable;
42  import java.util.Observer;
43  
44  import javax.persistence.EntityManager;
45  import javax.persistence.NoResultException;
46  import javax.persistence.NonUniqueResultException;
47  import javax.persistence.Query;
48  import javax.persistence.RollbackException;
49  
50  import org.apache.log4j.Logger;
51  import org.apache.xmlbeans.XmlObject;
52  import org.ogf.graap.wsag.api.Agreement;
53  import org.ogf.graap.wsag.api.AgreementOffer;
54  import org.ogf.graap.wsag.api.exceptions.AgreementFactoryException;
55  import org.ogf.graap.wsag.api.logging.LogMessage;
56  import org.ogf.graap.wsag.api.types.AbstractAgreementType;
57  import org.ogf.graap.wsag.api.types.AgreementOfferType;
58  import org.ogf.graap.wsag.server.monitoring.MonitorableAgreement;
59  import org.ogf.graap.wsag.server.persistence.EmfRegistry;
60  import org.ogf.graap.wsag.server.persistence.PersistentAgreement;
61  import org.ogf.graap.wsag4j.types.engine.PersistenceAgreementContextType;
62  import org.ogf.graap.wsag4j.types.engine.PersistenceAgreementContextType.Entry;
63  import org.ogf.schemas.graap.wsAgreement.AgreementContextType;
64  import org.ogf.schemas.graap.wsAgreement.AgreementPropertiesType;
65  import org.ogf.schemas.graap.wsAgreement.AgreementStateType;
66  import org.ogf.schemas.graap.wsAgreement.AgreementTemplateType;
67  import org.ogf.schemas.graap.wsAgreement.GuaranteeTermStateType;
68  import org.ogf.schemas.graap.wsAgreement.ServiceTermStateType;
69  import org.ogf.schemas.graap.wsAgreement.TermTreeType;
70  import org.ogf.schemas.graap.wsAgreement.TerminateInputType;
71  import org.w3.x2005.x08.addressing.EndpointReferenceType;
72  
73  /**
74   * <p>
75   * Database-related implementation of the {@link PersistentAgreement} interface. This implementation uses
76   * {@link PersistentAgreementContainer} instances to rebuild the original agreement and provides a transparent
77   * access to the stored informations.
78   * </p>
79   * 
80   * <p>
81   * During object instantiation an existing {@link PersistentAgreementContainer} instance is used to rebuild
82   * the agreement or an agreement itself is passed to the constructor. In the second case a
83   * {@link PersistentAgreementContainer} instance is created and stored, to initially persist the agreement.
84   * </p>
85   * 
86   * @author T.Weuffel
87   */
88  public class DatabasePersistentAgreement
89      implements PersistentAgreement, Agreement, Observer
90  {
91  
92      /**
93       * Data associated with an agreement instance is stored in the database under a unique identifier. This
94       * key identifies the context of a particular agreement in the database.
95       */
96      public static final String AGREEMENT_CONTEXT_ENTRY = "agreement_context";
97  
98      /**
99       * Data associated with an agreement instance is stored in the database under a unique identifier. This
100      * key identifies the context properties of a particular agreement in the database.
101      */
102     public static final String AGREEMENT_PROPERTIES_ENTRY = "agreement_properties";
103 
104     private static final Logger LOG = Logger.getLogger( DatabasePersistentAgreement.class );
105 
106     /**
107      * The {@link MonitorableAgreement} wraps the domain specific agreement implementation that inherits from
108      * {@link AbstractAgreementType}. After reload all agreement instances are wrapped with a
109      * {@link MonitorableAgreement}. in case monitoring was active it will be restarted, otherwise the
110      * {@link MonitorableAgreement} acts as an isolation layer to the concrete implementation, i.e. all calls
111      * are simply delegated to the concrete implementation.
112      */
113     protected MonitorableAgreement agreement;
114 
115     /**
116      * This container stores the agreement resource properties document and the agreement execution properties
117      * in the database.
118      * 
119      * {@link AbstractAgreementType#getXMLObject()} {@link AbstractAgreementType#getExecutionContext()}
120      */
121     protected PersistentAgreementContainer persistentAgreementContainer;
122 
123     /**
124      * The factory resource id. This id identifies the factory uniquely in the system.
125      */
126     protected String agreementFactoryId;
127 
128     private DatabasePersistentAgreement()
129     {
130         super();
131     }
132 
133     /**
134      * Uses an existing {@link PersistentAgreementContainer} instance to instantiate the persisted agreement.
135      * The initialization itself is delegated to the {@link #load()} method. Use this constructor to load an
136      * existing agreement instance.
137      * 
138      * @param persistentAgreementContainer
139      *            Instance storing all the agreement-related information.
140      * @param agreementFactoryId
141      *            ID of the agreement factory, used to build and deploy this agreement.
142      */
143     public DatabasePersistentAgreement( PersistentAgreementContainer persistentAgreementContainer,
144                                         String agreementFactoryId )
145     {
146         if ( LOG.isTraceEnabled() )
147         {
148             LOG.trace( "Create a new DatabasePersistentAgreement instance." );
149         }
150 
151         this.persistentAgreementContainer = persistentAgreementContainer;
152         this.agreementFactoryId = agreementFactoryId;
153     }
154 
155     /**
156      * Inserts an agreement in the database.
157      * 
158      * @param agreement
159      *            the agreement to persist
160      * @param agreementFactoryId
161      *            the id of the factory that created the agreement
162      * 
163      * @return an instance of the persisted agreement
164      * 
165      * @throws AgreementFactoryException
166      *             failed to insert agreement in the database
167      */
168     public static DatabasePersistentAgreement
169         insertAgreement( Agreement agreement, String agreementFactoryId ) throws AgreementFactoryException
170     {
171 
172         if ( LOG.isDebugEnabled() )
173         {
174             LOG.debug( LogMessage.getMessage(
175                 "Try to persist the PersistentAgreementContainer for agreement ''{0}''.",
176                 agreement.getAgreementId() ) );
177         }
178 
179         PersistentAgreementContainer container =
180             PersistentAgreementContainer.createContainer( agreement, agreementFactoryId );
181 
182         if ( LOG.isDebugEnabled() )
183         {
184             LOG.debug( LogMessage.getMessage(
185                 "PersistentAgreementContainer for agreement ''{0}'' persisted.", agreement.getAgreementId() ) );
186         }
187 
188         if ( LOG.isDebugEnabled() )
189         {
190             LOG.debug( "Insert new DatabasePersistentAgreement instance into database." );
191         }
192 
193         MonitorableAgreement monitorable =
194             initializeMonitorableAgreement( agreement, container.getPersistedAgreementContextType() );
195 
196         DatabasePersistentAgreement result = new DatabasePersistentAgreement();
197         result.persistentAgreementContainer = container;
198         result.agreementFactoryId = agreementFactoryId;
199         result.agreement = monitorable;
200 
201         monitorable.addObserver( result );
202         monitorable.notifyObservers();
203 
204         return result;
205     }
206 
207     // ////////////////////////////////////////
208     // //
209     // Agreement Delegation Methods //
210     // //
211     // ////////////////////////////////////////
212     /**
213      * {@inheritDoc}
214      * 
215      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getGuaranteeTermStates()
216      */
217     public GuaranteeTermStateType[] getGuaranteeTermStates()
218     {
219         return agreement.getGuaranteeTermStates();
220     }
221 
222     /**
223      * {@inheritDoc}
224      * 
225      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getAgreementId()
226      */
227     public String getAgreementId()
228     {
229         return agreement.getAgreementId();
230     }
231 
232     /**
233      * {@inheritDoc}
234      * 
235      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getContext()
236      */
237     public AgreementContextType getContext()
238     {
239         return agreement.getContext();
240     }
241 
242     /**
243      * {@inheritDoc}
244      * 
245      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getName()
246      */
247     public String getName()
248     {
249         return agreement.getName();
250     }
251 
252     /**
253      * {@inheritDoc}
254      * 
255      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getServiceTermStates()
256      */
257     public ServiceTermStateType[] getServiceTermStates()
258     {
259         return agreement.getServiceTermStates();
260     }
261 
262     /**
263      * {@inheritDoc}
264      * 
265      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getState()
266      */
267     public AgreementStateType getState()
268     {
269         return agreement.getState();
270     }
271 
272     /**
273      * {@inheritDoc}
274      * 
275      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getTerms()
276      */
277     public TermTreeType getTerms()
278     {
279         return agreement.getTerms();
280     }
281 
282     /**
283      * {@inheritDoc}
284      * 
285      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#getAgreementInstance()
286      */
287     public AbstractAgreementType getAgreementInstance()
288     {
289         return agreement.getAgreementInstance();
290     }
291 
292     /**
293      * {@inheritDoc}
294      * 
295      * @see org.ogf.graap.wsag.server.monitoring.MonitorableAgreement#terminate(org.ogf.schemas.graap.wsAgreement.TerminateInputType)
296      */
297     public void terminate( TerminateInputType reason )
298     {
299         agreement.terminate( reason );
300     }
301 
302     // ////////////////////////////////////////////////
303     // //
304     // Agreement Persistence Methods //
305     // //
306     // ////////////////////////////////////////////////
307     /**
308      * <p>
309      * Load an agreement based on the persisted information, the {@link PersistentAgreementContainer}
310      * instance. All information packages in the {@link PersistenceAgreementContextType} instance is loaded
311      * and used to re-build the original XML documents.
312      * </p>
313      * 
314      * <p>
315      * The result of this load-operation is a {@link MonitorableAgreement} instance stored in the
316      * {@link #agreement} variable.
317      * </p>
318      * 
319      * {@inheritDoc}
320      */
321     @SuppressWarnings( "unchecked" )
322     public synchronized void load() throws Exception
323     {
324 
325         //
326         // if we reload the agreement make sure that for the actual agreement instance
327         // monitoring is stopped in order to prevent memory leaks.
328         //
329         if ( agreement != null )
330         {
331             agreement.stopMonitoring();
332         }
333 
334         if ( LOG.isDebugEnabled() )
335         {
336             LOG.debug( "Load DatabasePersistentAgreement." );
337         }
338 
339         // re-build agreement context
340         PersistenceAgreementContextType persistenceAgreementContext =
341             persistentAgreementContainer.getPersistedAgreementContextType();
342 
343         // re-build agreement properties
344         AgreementPropertiesType agreementPropertiesType =
345             persistenceAgreementContext.getAgreementProperties();
346 
347         // load the agreement itself (dependent on the original class)
348         String className = persistentAgreementContainer.getAgreementClassName();
349 
350         LOG.debug( LogMessage.getMessage( "Re-load the original agreement using class ''{0}''.", className ) );
351 
352         Class<AbstractAgreementType> clazz;
353         try
354         {
355             clazz = (Class<AbstractAgreementType>) this.getClass().getClassLoader().loadClass( className );
356         }
357         catch ( ClassCastException e )
358         {
359             throw new Exception(
360                 "Load agreement failed. Agreement must inherit from AbstractAgreement class.", e );
361         }
362 
363         AbstractAgreementType abstractAgreementType = null;
364 
365         //
366         // First try to instantiate class using the default constructor
367         //
368         try
369         {
370             Constructor<AbstractAgreementType> constructor = clazz.getConstructor();
371             abstractAgreementType = constructor.newInstance();
372             abstractAgreementType.setXmlObject( agreementPropertiesType );
373         }
374         catch ( NoSuchMethodException e )
375         {
376             LOG.trace( LogMessage.getMessage( "Default constructor for class {0} not implemented.",
377                 clazz.getName() ) );
378         }
379 
380         //
381         // if default constructor is not implemented use constructor(AgreementPropertiesType)
382         //
383         if ( abstractAgreementType == null )
384         {
385             try
386             {
387                 Constructor<AbstractAgreementType> constructor =
388                     clazz.getConstructor( AgreementPropertiesType.class );
389                 abstractAgreementType = constructor.newInstance( agreementPropertiesType );
390             }
391             catch ( NoSuchMethodException e )
392             {
393                 LOG.trace( LogMessage.getMessage(
394                     "Constructor AgreementTypeImpl(AgreementPropertiesType) for class {0} not implemented.",
395                     clazz.getName() ) );
396             }
397         }
398 
399         //
400         // if still no success use the AgreementOffer constructor
401         //
402         if ( abstractAgreementType == null )
403         {
404             try
405             {
406                 Constructor<AbstractAgreementType> constructor = clazz.getConstructor( AgreementOffer.class );
407 
408                 //
409                 // First create an offer based on the stored agreement
410                 //
411                 AgreementTemplateType template = AgreementTemplateType.Factory.newInstance();
412                 template.addNewCreationConstraints();
413                 template.addNewContext().set( agreementPropertiesType.getContext() );
414                 template.addNewTerms().set( agreementPropertiesType.getTerms() );
415                 template.setName( agreementPropertiesType.getContext().getTemplateName() );
416                 template.setTemplateId( agreementPropertiesType.getContext().getTemplateId() );
417 
418                 //
419                 // instantiate the agreement object and set the properties document
420                 //
421                 abstractAgreementType = constructor.newInstance( new AgreementOfferType( template ) );
422                 abstractAgreementType.setXmlObject( agreementPropertiesType );
423             }
424             catch ( NoSuchMethodException e )
425             {
426                 LOG.trace( MessageFormat.format(
427                     "Constructor AgreementTypeImpl(AgreementOffer) for class {0} not implemented.",
428                     clazz.getName() ) );
429             }
430         }
431         //
432         // if agreement class was still not instantiated throw an exception
433         //
434         if ( abstractAgreementType == null )
435         {
436             String message =
437                 MessageFormat.format( "Could not instantiate agreement class ''{0}''.\n"
438                     + "Agreement class does neither implement default constructor "
439                     + "nor constructor Ageement(AgreementPropertiesType).", clazz.getName() );
440             LOG.error( message );
441             throw new Exception( message );
442         }
443 
444         Entry[] entries = persistenceAgreementContext.getEntryArray();
445         for ( int i = 0; i < entries.length; i++ )
446         {
447             abstractAgreementType.getExecutionContext().put( entries[i].getName(), entries[i].getValue() );
448         }
449 
450         agreement = initializeMonitorableAgreement( abstractAgreementType, persistenceAgreementContext );
451         agreement.addObserver( this );
452 
453         //
454         // notify the agreement instance of reload
455         //
456         agreement.notifyReload();
457 
458         if ( LOG.isDebugEnabled() )
459         {
460             LOG.debug( "Instantiated Monitorable Agreement instance for persisted agreement." );
461         }
462     }
463 
464     private static MonitorableAgreement
465         initializeMonitorableAgreement( Agreement agreement, PersistenceAgreementContextType pContext )
466     {
467         //
468         // TODO: this is kind of hackish, should be re-factored in version 2.0
469         //
470 
471         //
472         // if the agreement is already an instance of the MonitorableAgreement class simply return it
473         //
474         if ( agreement instanceof MonitorableAgreement )
475         {
476             return (MonitorableAgreement) agreement;
477         }
478 
479         //
480         // otherwise we wrap the implementation with the MonitorableAgreement
481         //
482         AbstractAgreementType instance = null;
483 
484         if ( agreement instanceof AbstractAgreementType )
485         {
486             instance = (AbstractAgreementType) agreement;
487         }
488         else
489         {
490             instance = agreement.getAgreementInstance();
491         }
492 
493         //
494         // Finally, create the monitorable agreement
495         //
496         MonitorableAgreement monitorable = new MonitorableAgreement( instance );
497 
498         return monitorable;
499     }
500 
501     /**
502      * <p>
503      * Handles the save-operation of an agreement. All required information (the current version/state of the
504      * information) are capsuled in the agreement-related {@link PersistentAgreementContainer} instance, which
505      * then is stored in the database.
506      * </p>
507      * 
508      * <p>
509      * If the agreement was already persisted, the existing persisted agreement is replaced with the new
510      * version. If not, the agreement is initially persisted. The decision is based on the existence of a
511      * database-record with the same agreement id.
512      * </p>
513      * 
514      * {@inheritDoc}
515      */
516     public synchronized void save() throws Exception
517     {
518         if ( LOG.isDebugEnabled() )
519         {
520             LOG.debug( LogMessage.getMessage( "Save DatabasePersistentAgreement ''{0}''.",
521                 agreement.getAgreementId() ) );
522         }
523 
524         //
525         // check if the agreement was already persisted
526         //
527 
528         EntityManager em = EmfRegistry.getEntityManager();
529         Query query = em.createNamedQuery( "PersistentAgreementContainer.findByAgreementId" );
530         query.setParameter( "agreementId", agreement.getAgreementId() );
531 
532         PersistentAgreementContainer container = null;
533         boolean persistentAgreementContainerExists = true;
534         try
535         {
536             container = (PersistentAgreementContainer) query.getSingleResult();
537         }
538         catch ( NonUniqueResultException ex )
539         {
540             LOG.error( LogMessage.getMessage(
541                 "Found more than one PersistentAgreementContainer for the agreement id ''{0}''.",
542                 agreement.getAgreementId() ), ex );
543 
544             throw new Exception( MessageFormat.format(
545                 "Found more than one PersistentAgreementContainer for the agreement id ''{0}''.",
546                 agreement.getAgreementId() ) );
547         }
548         catch ( NoResultException ex )
549         {
550             persistentAgreementContainerExists = false;
551         }
552 
553         // was already persisted, so just update it
554         if ( persistentAgreementContainerExists )
555         {
556             LOG.debug( LogMessage.getMessage( "Update PersistentAgreementContainer ''{0}''.",
557                 agreement.getAgreementId() ) );
558 
559             // build wrapper
560             PersistenceAgreementContextType persistenceAgreementContext =
561                 PersistenceAgreementContextType.Factory.newInstance();
562 
563             // update agreement properties
564             persistenceAgreementContext.addNewAgreementProperties();
565 
566             // update the agreement properties
567             AbstractAgreementType abstractAgreementType = agreement.getAgreementInstance();
568             persistenceAgreementContext.getAgreementProperties().set( abstractAgreementType.getXMLObject() );
569 
570             //
571             // update the agreement execution context
572             //
573             Map<String, XmlObject> executionProperties =
574                 agreement.getExecutionContext().getExecutionProperties();
575             persistenceAgreementContext.setEntryArray( new PersistenceAgreementContextType.Entry[0] );
576             Iterator<String> keys = executionProperties.keySet().iterator();
577             while ( keys.hasNext() )
578             {
579                 String key = keys.next();
580                 XmlObject value = executionProperties.get( key );
581                 PersistenceAgreementContextType.Entry entry = persistenceAgreementContext.addNewEntry();
582                 entry.setName( key );
583                 entry.addNewValue().set( value );
584                 entry.getValue().changeType( value.schemaType() );
585             }
586 
587             // PersistenceAgreementContextType.Entry entry = persistenceAgreementContext.addNewEntry();
588             // entry.set(agreement.getContext());
589             // entry.setName(AGREEMENT_CONTEXT_ENTRY);
590 
591             // set the new PersistenceAgreementContextType
592             container.setPersistedAgreementContextType( persistenceAgreementContext );
593 
594             // set the agreements' status and EPR
595             container.setState( agreement.getState() );
596 
597             // set agreement class name
598             container.setAgreementClassName( abstractAgreementType.getClass().getName() );
599 
600             // try to persist the wrapped agreement
601             em.getTransaction().begin();
602 
603             try
604             {
605                 // persist and commit
606                 em.merge( container );
607                 em.getTransaction().commit();
608             }
609             catch ( RollbackException ex )
610             {
611                 LOG.error( LogMessage.getMessage( "Could not update the wrapped agreement with id ''{0}''.",
612                     agreement.getAgreementId() ), ex );
613 
614                 // roll back the persistent
615                 em.getTransaction().rollback();
616 
617                 throw new Exception( MessageFormat.format(
618                     "Could not update the wrapped agreement with id ''{0}''.", agreement.getAgreementId() ) );
619             }
620             finally
621             {
622                 em.close();
623             }
624         }
625         // was not persisted, create a new PersistentAgreementContainer and persist it
626         else
627         {
628             String msgNoAgreementFound =
629                 "No agreement instance found in database. "
630                     + "Save operation can only be executed on exosting agreement instances.";
631             throw new AgreementFactoryException( msgNoAgreementFound );
632         }
633     }
634 
635     /**
636      * {@inheritDoc}
637      */
638     public Agreement getAgreement()
639     {
640         return agreement;
641     }
642 
643     /**
644      * {@inheritDoc}
645      * 
646      * @deprecated
647      */
648     @Deprecated
649     public EndpointReferenceType getAgreementEPR()
650     {
651         throw new UnsupportedOperationException( "Not implemented yet." );
652     }
653 
654     /**
655      * 
656      * @return the persistent agreement container of this instance
657      */
658     public PersistentAgreementContainer getPersistentAgreementContainer()
659     {
660         return persistentAgreementContainer;
661     }
662 
663     /**
664      * Retrieves state change notifications of the concrete agreement implementation. If a state notification
665      * was received the agreement is saved.
666      * 
667      * {@inheritDoc}
668      * 
669      * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
670      */
671     public void update( Observable o, Object arg )
672     {
673         //
674         // state changes to an agreement object are propagated to the MonitorableAgreement instance
675         // which in turn notifies the database agreement instance.
676         //
677         if ( o == agreement )
678         {
679             try
680             {
681                 save();
682             }
683             catch ( Exception e )
684             {
685                 LOG.error( "Failed to save agreement instance.", e );
686             }
687         }
688         else
689         {
690             LOG.error( "Received a state change notification for an greement instance different than the registered." );
691         }
692     }
693 }