View Javadoc

1   /* 
2    * Copyright (c) 2007, 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.engine;
36  
37  import java.io.InputStream;
38  import java.text.MessageFormat;
39  import java.util.Properties;
40  import java.util.Vector;
41  
42  import javax.security.auth.login.LoginContext;
43  
44  import org.apache.log4j.Logger;
45  import org.apache.xml.resolver.tools.CatalogResolver;
46  import org.apache.xmlbeans.XmlObject;
47  import org.ogf.graap.wsag.api.WsagConstants;
48  import org.ogf.graap.wsag.api.configuration.WSAG4JConfiguration;
49  import org.ogf.graap.wsag.api.logging.LogMessage;
50  import org.ogf.graap.wsag.server.api.WsagMessageContext;
51  import org.ogf.graap.wsag.server.persistence.EmfRegistry;
52  import org.ogf.graap.wsag.server.persistence.IAgreementFactoryHome;
53  import org.ogf.graap.wsag.server.persistence.IAgreementHome;
54  import org.ogf.graap.wsag.server.persistence.PersistentAgreementFactory;
55  import org.ogf.graap.wsag.server.persistence.impl.DatabaseAgreementHome;
56  import org.ogf.graap.wsag.server.persistence.impl.WSAG4JPersistenceFacade;
57  import org.ogf.graap.wsag4j.types.configuration.ConfigurationType;
58  import org.ogf.graap.wsag4j.types.configuration.WSAG4JEngineConfigurationDocument;
59  import org.ogf.graap.wsag4j.types.configuration.WSAG4JEngineConfigurationType;
60  import org.ogf.graap.wsag4j.types.configuration.WSAG4JEngineInstanceType;
61  import org.ogf.graap.wsag4j.types.configuration.WSRFEngineConfigurationType;
62  import org.quartz.Scheduler;
63  import org.quartz.SchedulerException;
64  import org.quartz.SchedulerFactory;
65  import org.quartz.impl.StdSchedulerFactory;
66  
67  /**
68   * The WSAGEngine provides access to all agreement factories configured for this engine. The available
69   * factories can be retrieved via the agreement factory home interface. The agreement factory home is
70   * retrieved by the {@link #getAgreementFactoryHome()} method. Before the engine can be used it must be
71   * initialized. Engine initialization is triggered by the {@link #initializeEngine(String)} method.
72   * 
73   * @author Oliver Waeldrich
74   * 
75   */
76  public class WsagEngine
77  {
78  
79      private static final Logger LOG = Logger.getLogger( WsagEngine.class );
80  
81      private static ThreadLocal<WsagMessageContext> messageContext = null;
82  
83      private static WsagEngine engine = null;
84  
85      private ConfigurationType wsag4jConfiguration = null;
86  
87      private WSRFEngineConfigurationType wsrfConfiguration = null;
88  
89      private WSAG4JEngineConfigurationType[] engineConfigurations = null;
90  
91      private String deploymentURI = null;
92  
93      private LoginContext serverLoginContext;
94  
95      private IAgreementFactoryHome agreementFactoryHome;
96  
97      private IAgreementHome agreementHome;
98  
99      private boolean allowAnonymousAccess = false;
100 
101     /**
102      * Allows to specify an alternate filename for the wsrf-engine configuration via the system properties.
103      * 
104      * @see System#setProperty(String, String)
105      */
106     public static final String WSAG4J_WSRF_ENGINE_FILE_NAME = "wsag4j.wsrf-engine.config.filename";
107 
108     /**
109      * Returns the message context for the current invocation (thread).
110      * 
111      * @return the message context
112      */
113     public static WsagMessageContext getWsagMessageContext()
114     {
115 
116         synchronized ( messageContext )
117         {
118             WsagMessageContext wsagcontext = messageContext.get();
119 
120             if ( wsagcontext == null )
121             {
122                 wsagcontext = new WsagMessageContext();
123                 messageContext.set( wsagcontext );
124             }
125 
126             return wsagcontext;
127         }
128     }
129 
130     /**
131      * Sets the message context for the current invocation (thread).
132      * 
133      * @param context
134      *            the message context
135      */
136     public static void setWsagMessageContext( WsagMessageContext context )
137     {
138         WsagEngine.messageContext.set( context );
139     }
140 
141     private static synchronized WsagEngine getInstance()
142     {
143         if ( engine == null )
144         {
145             engine = new WsagEngine();
146         }
147 
148         return engine;
149     }
150 
151     /**
152      * Initializes the WSAG4J Engine. During the initialization process the WSRF engine configuration (system
153      * configuration) is parsed and the referenced WSAG4J engine configurations (factory configurations) are
154      * loaded. The WSAG4J engine must be initialized before making any calls to the system. By default the
155      * WSRF engine configuration location is specified by the
156      * {@link WsagConstants#WSAG4J_WSRF_ENGINE_CONFIG_FILE} constant. This value can be overwritten by the
157      * system property {@value #WSAG4J_WSRF_ENGINE_FILE_NAME}.
158      * 
159      * @param defaultGatewayURL
160      *            the default gateway URL
161      * 
162      * @throws Exception
163      *             indicates an error during engine initialization
164      */
165     public static void initializeEngine( String defaultGatewayURL ) throws Exception
166     {
167         getInstance().initialize( defaultGatewayURL );
168     }
169 
170     private void initialize( String defaultGatewayURL ) throws Exception
171     {
172 
173         if ( LOG.isTraceEnabled() )
174         {
175             LOG.trace( "WsagEngine -> initializeServer" );
176         }
177 
178         //
179         // set the CatalogResolver as default entity resolver for XmlBeans
180         //
181         System.setProperty( "xmlbean.entityResolver", CatalogResolver.class.getName() );
182 
183         messageContext = new ThreadLocal<WsagMessageContext>();
184 
185         loadWSAG4JConfiguration();
186 
187         if ( LOG.isTraceEnabled() )
188         {
189             LOG.trace( "AFTER: WsagEngine -> loadWSAG4JConfiguration" );
190         }
191 
192         initializeGatewayURI( defaultGatewayURL );
193 
194         if ( LOG.isTraceEnabled() )
195         {
196             LOG.trace( "AFTER: WsagEngine -> initializeGatewayURI" );
197         }
198 
199         loadEngineConfigurations();
200 
201         initializePersistenceLayer();
202 
203         if ( LOG.isTraceEnabled() )
204         {
205             LOG.trace( "AFTER: WsagEngine -> initializePersistenceLayer" );
206         }
207     }
208 
209     /**
210      * Shutdown of the WSAG4J engine instance.
211      * 
212      * @throws Exception
213      *             indicates an error during engine shutdown
214      */
215     public static void shutdownEngine() throws Exception
216     {
217 
218         try
219         {
220             SchedulerFactory factory = new StdSchedulerFactory();
221             Scheduler scheduler = factory.getScheduler();
222 
223             if ( scheduler.isStarted() )
224             {
225                 scheduler.shutdown();
226             }
227         }
228         catch ( SchedulerException e )
229         {
230             LOG.error( "Failed to shutdown quartz scheduler.", e );
231         }
232 
233         getInstance().shutdownPersistenceLayer();
234 
235         //
236         // prevent memory leak
237         //
238         if ( messageContext != null )
239         {
240             messageContext.set( null );
241             messageContext = null;
242         }
243         engine = null;
244     }
245 
246     /**
247      * Loads the WSAG4J configuration.
248      * 
249      * 
250      * @throws Exception
251      */
252     private void loadWSAG4JConfiguration() throws Exception
253     {
254 
255         if ( LOG.isTraceEnabled() )
256         {
257             LOG.trace( "WsagEngine -> loadWSAG4JConfiguration" );
258         }
259 
260         //
261         // resolve the WSAG4J configuration file name
262         //
263         String filename =
264             System.getProperty( WSAG4J_WSRF_ENGINE_FILE_NAME, WsagConstants.WSAG4J_WSRF_ENGINE_CONFIG_FILE );
265 
266         //
267         // load the WSAG4J configuration and set it in the WSAG4JEngine
268         //
269         wsag4jConfiguration = WSAG4JConfiguration.findWSAG4JConfiguration( filename );
270         if ( wsag4jConfiguration != null )
271         {
272 
273             wsrfConfiguration = wsag4jConfiguration.getWSRFEngineConfiguration();
274 
275             //
276             // If the WSRF configuration was not found, throw an exception
277             //
278             if ( WsagEngine.getWSRFConfiguration() == null )
279             {
280                 String message = "WSAG4J WSRF configuration was not found.";
281                 throw new Exception( message );
282             }
283 
284         }
285         else
286         {
287 
288             //
289             // If the WSAG4J configuration was not found, throw an exception
290             //
291             String message = "WSAG4J configuration was not found.";
292             throw new Exception( message );
293 
294         }
295     }
296 
297     /**
298      * Loads the WSAG4J engine configurations specified in the wsrf-engine.config file and initializes the
299      * {link {@link #engineConfigurations} property.
300      */
301     private void loadEngineConfigurations()
302     {
303         Vector<WSAG4JEngineConfigurationType> engineConfig = new Vector<WSAG4JEngineConfigurationType>();
304 
305         WSAG4JEngineInstanceType[] instances = new WSAG4JEngineInstanceType[0];
306         if ( ( wsrfConfiguration != null ) && wsrfConfiguration.isSetWSAG4JEngineInstances() )
307         {
308             instances = wsrfConfiguration.getWSAG4JEngineInstances().getWSAG4JEngineArray();
309         }
310 
311         if ( instances == null )
312         {
313             String message =
314                 "Missing section WSAG4JEngineInstances in wsag4j engine configuration. "
315                     + "No Agreement Factories were instantiated.";
316             LOG.warn( message );
317         }
318         else
319         {
320             for ( int i = 0; i < instances.length; i++ )
321             {
322                 String configFile = instances[i].getEngineConfigurationFile();
323 
324                 try
325                 {
326                     InputStream in = getClass().getResourceAsStream( configFile );
327 
328                     if ( in == null )
329                     {
330                         LOG.warn( LogMessage.getMessage(
331                             "The wsag4j engine configuration file [{0}] was not found. Skipping this entry.",
332                             configFile ) );
333 
334                         continue;
335                     }
336 
337                     WSAG4JEngineConfigurationDocument engineConfiguration =
338                         (WSAG4JEngineConfigurationDocument) XmlObject.Factory.parse( in );
339                     engineConfig.add( engineConfiguration.getWSAG4JEngineConfiguration() );
340                 }
341                 catch ( Exception e )
342                 {
343                     LOG.error( LogMessage.getMessage(
344                         "Could not load WSAG4J engine configuration {0}. Ignoring this instance. Error: {1}",
345                         configFile, e.getMessage() ) );
346                 }
347             }
348         }
349 
350         this.engineConfigurations =
351             engineConfig.toArray( new WSAG4JEngineConfigurationType[engineConfig.size()] );
352     }
353 
354     /**
355      * Initializes the WSAG4J Gateway URL.
356      * 
357      * @param defaultDeploymentURI
358      * @throws Exception
359      */
360     private void initializeGatewayURI( String defaultDeploymentURI ) throws Exception
361     {
362 
363         LOG.info( "WsagEngine -> initializeGatewayURI" );
364 
365         // first we try to load the deployment uri via the system properties
366         String spConfiguredURI = loadViaSystemProperties();
367         if ( spConfiguredURI != null )
368         {
369 
370             LOG.info( "Gateway address for this service is configured via System Properties." );
371             LOG.info( LogMessage.getMessage( "WS-Resources will be deployed at URI: {0}", spConfiguredURI ) );
372 
373             deploymentURI = spConfiguredURI;
374             return;
375         }
376 
377         // Read the gateway URI from the configuration file.
378         // This value is provided by the user/administrator.
379         String gatewayAddress = wsrfConfiguration.getGatewayAddress();
380         if ( ( gatewayAddress != null ) && ( !gatewayAddress.equals( "" ) ) )
381         {
382             Object[] filler = new Object[] { gatewayAddress };
383             String message =
384                 MessageFormat.format(
385                     "Loaded gateway url from configuration file. Resources will be deployed at [{0}]",
386                     filler );
387             LOG.info( message );
388 
389             deploymentURI = gatewayAddress;
390             return;
391         }
392 
393         // Since no gateway URI was configured in the configuration
394         // file, we try to generate one.
395         // We use a standard way to obtain the context path name
396         // which should work across all servers. We remove the
397         // trailing slash, then take what's left over, which
398         // should be the context path less the preceding
399         // slash such as "wsag4j"
400         String noDeploymentURI = "No gateway address is configured for this service";
401         LOG.warn( noDeploymentURI );
402 
403         String msgGeneratedWarning = "Try to generate gateway address for service";
404         LOG.warn( msgGeneratedWarning );
405 
406         deploymentURI = defaultDeploymentURI;
407     }
408 
409     private String loadViaSystemProperties()
410     {
411         try
412         {
413             InputStream in = WSAG4JConfiguration.findResource( WsagConstants.WSAG4J_CONFIG_FILE );
414             Properties properties = new Properties();
415             properties.load( in );
416 
417             String key =
418                 properties.getProperty( "org.ogf.graap.wsag.gateway.key",
419                     WsagConstants.WSAG4J_GATEWAY_PROPERTY );
420 
421             return System.getProperty( key );
422         }
423         catch ( Exception ex )
424         {
425             return System.getProperty( WsagConstants.WSAG4J_GATEWAY_PROPERTY );
426         }
427     }
428 
429     /**
430      * @return the wsag4jConfiguration
431      */
432     public static ConfigurationType getWSAG4JConfiguration()
433     {
434         return getInstance().wsag4jConfiguration;
435     }
436 
437     /**
438      * @return the configuration
439      */
440     public static WSRFEngineConfigurationType getWSRFConfiguration()
441     {
442         return getInstance().wsrfConfiguration;
443     }
444 
445     /**
446      * @return the deploymentAddress
447      */
448     public static String getGatewayURL()
449     {
450         return getInstance().deploymentURI;
451     }
452 
453     /**
454      * Sets the WSAG4J engine login context. This login context is used to identify a WSAG4J server instance,
455      * e.g. action implementations can use this login context to sign outgoing messages.
456      * 
457      * @param context
458      *            the login context
459      */
460     public static void setLoginContext( LoginContext context )
461     {
462         getInstance().serverLoginContext = context;
463     }
464 
465     /**
466      * Returns the WSAG4J engine login context. This login context is used to identify a WSAG4J server
467      * instance, e.g. action implementations can use this login context to sign outgoing messages.
468      * 
469      * @return the login context
470      */
471     public static LoginContext getLoginContext()
472     {
473         return getInstance().serverLoginContext;
474     }
475 
476     /**
477      * Returns the agreement factory home
478      * 
479      * @return the agreement factory home
480      */
481     public static IAgreementFactoryHome getAgreementFactoryHome()
482     {
483         return getInstance().agreementFactoryHome;
484     }
485 
486     /**
487      * Overwrites the instance created during initialization. The WS-Layer uses this to deploy the extended WS
488      * version.
489      * 
490      * @param agreementFactoryHome
491      *            the home interface to look up the agreement factories
492      */
493     public static void setAgreementFactoryHome( IAgreementFactoryHome agreementFactoryHome )
494     {
495         getInstance().agreementFactoryHome = agreementFactoryHome;
496     }
497 
498     /**
499      * @return the agreement home
500      * 
501      * @deprecated
502      */
503     @Deprecated
504     public static IAgreementHome getAgreementHome()
505     {
506         return getInstance().agreementHome;
507     }
508 
509     /**
510      * Overwrites the instance created during initialization. The WS-Layer uses this to deploy the extended WS
511      * version.
512      * 
513      * @param agreementHome
514      *            the home interface to look up the existing agreements
515      */
516     public static void setAgreementHome( IAgreementHome agreementHome )
517     {
518         getInstance().agreementHome = agreementHome;
519     }
520 
521     /**
522      * Initializes the WSAG4J persistence layer.
523      */
524     private void initializePersistenceLayer() throws Exception
525     {
526 
527         try
528         {
529             LOG.info( "WsagEngine -> initialize PersistenceLayer" );
530 
531             // build and initialise agreement factory home
532             agreementFactoryHome = new WSAG4JPersistenceFacade( engineConfigurations );
533             agreementFactoryHome.initialize();
534 
535             // build and initialise agreement home
536             agreementHome = new DatabaseAgreementHome( agreementFactoryHome );
537 
538             LOG.info( "WsagEngine -> Persistence Layer initialized" );
539         }
540         catch ( Exception e )
541         {
542             LOG.error( "WsagEngine -> failed to initialize Persistence Layer", e );
543             throw new Exception( "Failed to initialize persistence layer.", e );
544         }
545     }
546 
547     /**
548      * Shutdown the persistence layer and persist all agreements that are currently managed by the
549      * implementation.
550      * 
551      * TODO: Save current agreement states.
552      */
553     private void shutdownPersistenceLayer() throws Exception
554     {
555         // load all agreements
556         PersistentAgreementFactory[] factories = getAgreementFactoryHome().list();
557         for ( int i = 0; i < factories.length; i++ )
558         {
559             try
560             {
561                 final String msgDoSave = "Save agreement factory ''{0}''.";
562                 LOG.debug( LogMessage.getMessage( msgDoSave, factories[i].getResourceId() ) );
563 
564                 factories[i].save();
565 
566                 final String msgSaved = "Agreement factory ''{0}'' saved.";
567                 LOG.debug( LogMessage.getMessage( msgSaved, factories[i].getResourceId() ) );
568             }
569             catch ( Exception e )
570             {
571                 String message = "Failed to save agreement factory ''{0}''.";
572                 LOG.error( MessageFormat.format( message, new Object[] { factories[i].getResourceId() } ), e );
573             }
574         }
575 
576         // remove references to agreement (factory) home
577         agreementFactoryHome = null;
578         agreementHome = null;
579 
580         // close the entity manager factory
581         EmfRegistry.finalizeEmfRegistry();
582     }
583 
584     /**
585      * Sets if anonymous access to the server is allowed.
586      * 
587      * @param allowAnonymousAccess
588      *            the allowAnonymousAccess to set
589      */
590     public static void setAllowAnonymousAccess( boolean allowAnonymousAccess )
591     {
592         getInstance().allowAnonymousAccess = allowAnonymousAccess;
593     }
594 
595     /**
596      * Sets if anonymous access to the server is allowed.
597      * 
598      * @return the allowAnonymousAccess
599      */
600     public static boolean isAllowAnonymousAccess()
601     {
602         return getInstance().allowAnonymousAccess;
603     }
604 }