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