001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (C) 2012  Paul Ferraro
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package net.sf.hajdbc.sql;
019
020import java.beans.Introspector;
021import java.beans.PropertyDescriptor;
022import java.beans.PropertyEditor;
023import java.beans.PropertyEditorManager;
024import java.util.AbstractMap;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.HashMap;
028import java.util.LinkedList;
029import java.util.List;
030import java.util.Map;
031import java.util.concurrent.ConcurrentHashMap;
032import java.util.concurrent.ConcurrentMap;
033import java.util.concurrent.Executors;
034import java.util.concurrent.ThreadFactory;
035
036import javax.xml.bind.annotation.XmlAttribute;
037import javax.xml.bind.annotation.XmlElement;
038import javax.xml.bind.annotation.XmlID;
039import javax.xml.bind.annotation.XmlIDREF;
040import javax.xml.bind.annotation.XmlType;
041import javax.xml.bind.annotation.XmlValue;
042import javax.xml.bind.annotation.adapters.XmlAdapter;
043import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
044
045import net.sf.hajdbc.Database;
046import net.sf.hajdbc.DatabaseClusterConfiguration;
047import net.sf.hajdbc.DatabaseFactory;
048import net.sf.hajdbc.ExecutorServiceProvider;
049import net.sf.hajdbc.Identifiable;
050import net.sf.hajdbc.IdentifiableMatcher;
051import net.sf.hajdbc.Messages;
052import net.sf.hajdbc.SynchronizationStrategy;
053import net.sf.hajdbc.TransactionMode;
054import net.sf.hajdbc.balancer.BalancerFactory;
055import net.sf.hajdbc.cache.DatabaseMetaDataCacheFactory;
056import net.sf.hajdbc.codec.DecoderFactory;
057import net.sf.hajdbc.codec.MultiplexingDecoderFactory;
058import net.sf.hajdbc.dialect.DialectFactory;
059import net.sf.hajdbc.distributed.CommandDispatcherFactory;
060import net.sf.hajdbc.durability.DurabilityFactory;
061import net.sf.hajdbc.io.InputSinkProvider;
062import net.sf.hajdbc.lock.LockManagerFactory;
063import net.sf.hajdbc.management.DefaultMBeanRegistrar;
064import net.sf.hajdbc.management.MBeanRegistrar;
065import net.sf.hajdbc.state.StateManagerFactory;
066import net.sf.hajdbc.tx.SimpleTransactionIdentifierFactory;
067import net.sf.hajdbc.tx.TransactionIdentifierFactory;
068import net.sf.hajdbc.tx.UUIDTransactionIdentifierFactory;
069import net.sf.hajdbc.util.ServiceLoaders;
070import net.sf.hajdbc.util.concurrent.cron.CronExpression;
071
072/**
073 * @author Paul Ferraro
074 */
075@XmlType(propOrder = { "commandDispatcherFactoryDescriptor", "synchronizationStrategyDescriptors", "stateManagerFactoryDescriptor", "lockManagerFactoryDescriptor" })
076public abstract class AbstractDatabaseClusterConfiguration<Z, D extends Database<Z>> implements DatabaseClusterConfiguration<Z, D>
077{
078        private static final long serialVersionUID = -2808296483725374829L;
079
080        private CommandDispatcherFactory dispatcherFactory;     
081        private Map<String, SynchronizationStrategy> synchronizationStrategies = new HashMap<String, SynchronizationStrategy>();
082        private StateManagerFactory stateManagerFactory = ServiceLoaders.findRequiredService(StateManagerFactory.class);
083        private LockManagerFactory lockManagerFactory = ServiceLoaders.findRequiredService(LockManagerFactory.class);
084
085        protected abstract NestedConfiguration<Z, D> getNestedConfiguration();
086
087        @Override
088        public DatabaseFactory<Z, D> getDatabaseFactory()
089        {
090                return this.getNestedConfiguration().getDatabaseFactory();
091        }
092
093        @XmlElement(name = "distributable")
094        private CommandDispatcherFactoryDescriptor getCommandDispatcherFactoryDescriptor() throws Exception
095        {
096                return (this.dispatcherFactory != null) ? new CommandDispatcherFactoryDescriptorAdapter().marshal(this.dispatcherFactory) : null;
097        }
098
099        @SuppressWarnings("unused")
100        private void setCommandDispatcherFactoryDescriptor(CommandDispatcherFactoryDescriptor descriptor) throws Exception
101        {
102                this.dispatcherFactory = (descriptor != null) ? new CommandDispatcherFactoryDescriptorAdapter().unmarshal(descriptor) : null;
103        }
104        
105        @XmlElement(name = "sync")
106        private SynchronizationStrategyDescriptor[] getSynchronizationStrategyDescriptors() throws Exception
107        {
108                List<SynchronizationStrategyDescriptor> results = new ArrayList<SynchronizationStrategyDescriptor>(this.synchronizationStrategies.size());
109                SynchronizationStrategyDescriptorAdapter adapter = new SynchronizationStrategyDescriptorAdapter();
110
111                for (Map.Entry<String, SynchronizationStrategy> entry: this.synchronizationStrategies.entrySet())
112                {
113                        SynchronizationStrategyDescriptor result = adapter.marshal(entry.getValue());
114                        
115                        result.setId(entry.getKey());
116                        
117                        results.add(result);
118                }
119                
120                return results.toArray(new SynchronizationStrategyDescriptor[results.size()]);
121        }
122        
123        @SuppressWarnings("unused")
124        private void setSynchronizationStrategyDescriptors(SynchronizationStrategyDescriptor[] entries) throws Exception
125        {
126                SynchronizationStrategyDescriptorAdapter adapter = new SynchronizationStrategyDescriptorAdapter();
127                
128                for (SynchronizationStrategyDescriptor entry: entries)
129                {
130                        SynchronizationStrategy strategy = adapter.unmarshal(entry);
131                        
132                        this.synchronizationStrategies.put(entry.getId(), strategy);
133                }
134        }
135        
136        @XmlElement(name = "state")
137        private StateManagerFactoryDescriptor getStateManagerFactoryDescriptor() throws Exception
138        {
139                return new StateManagerFactoryDescriptorAdapter().marshal(this.stateManagerFactory);
140        }
141        
142        @SuppressWarnings("unused")
143        private void setStateManagerFactoryDescriptor(StateManagerFactoryDescriptor descriptor) throws Exception
144        {
145                this.stateManagerFactory = new StateManagerFactoryDescriptorAdapter().unmarshal(descriptor);
146        }
147        
148        @XmlElement(name = "lock")
149        private LockManagerFactoryDescriptor getLockManagerFactoryDescriptor() throws Exception
150        {
151                return new LockManagerFactoryDescriptorAdapter().marshal(this.lockManagerFactory);
152        }
153        
154        @SuppressWarnings("unused")
155        private void setLockManagerFactoryDescriptor(LockManagerFactoryDescriptor descriptor) throws Exception
156        {
157                this.lockManagerFactory = new LockManagerFactoryDescriptorAdapter().unmarshal(descriptor);
158        }
159        
160        /**
161         * {@inheritDoc}
162         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getAutoActivationExpression()
163         */
164        @Override
165        public CronExpression getAutoActivationExpression()
166        {
167                return this.getNestedConfiguration().getAutoActivationExpression();
168        }
169
170        public void setAutoActivationExpression(CronExpression expression)
171        {
172                this.getNestedConfiguration().setAutoActivationExpression(expression);
173        }
174        
175        /**
176         * {@inheritDoc}
177         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getBalancerFactory()
178         */
179        @Override
180        public BalancerFactory getBalancerFactory()
181        {
182                return this.getNestedConfiguration().getBalancerFactory();
183        }
184
185        public void setBalancerFactory(BalancerFactory factory)
186        {
187                this.getNestedConfiguration().setBalancerFactory(factory);
188        }
189        
190        /**
191         * {@inheritDoc}
192         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getDispatcherFactory()
193         */
194        @Override
195        public CommandDispatcherFactory getDispatcherFactory()
196        {
197                return this.dispatcherFactory;
198        }
199
200        public void setDispatcherFactory(CommandDispatcherFactory factory)
201        {
202                this.dispatcherFactory = factory;
203        }
204        
205        /**
206         * {@inheritDoc}
207         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getDatabaseMap()
208         */
209        @Override
210        public ConcurrentMap<String, D> getDatabaseMap()
211        {
212                return this.getNestedConfiguration().getDatabaseMap();
213        }
214
215        public void setDatabases(Collection<D> databases)
216        {
217                Map<String, D> map = this.getDatabaseMap();
218                
219                for (D database: databases)
220                {
221                        map.put(database.getId(), database);
222                }
223        }
224        
225        /**
226         * {@inheritDoc}
227         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getDatabaseMetaDataCacheFactory()
228         */
229        @Override
230        public DatabaseMetaDataCacheFactory getDatabaseMetaDataCacheFactory()
231        {
232                return this.getNestedConfiguration().getDatabaseMetaDataCacheFactory();
233        }
234
235        public void setDatabaseMetaDataCacheFactory(DatabaseMetaDataCacheFactory factory)
236        {
237                this.getNestedConfiguration().setDatabaseMetaDataCacheFactory(factory);
238        }
239        
240        /**
241         * {@inheritDoc}
242         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getDefaultSynchronizationStrategy()
243         */
244        @Override
245        public String getDefaultSynchronizationStrategy()
246        {
247                return this.getNestedConfiguration().getDefaultSynchronizationStrategy();
248        }
249
250        public void setDefaultSynchronizationStrategy(String strategy)
251        {
252                this.getNestedConfiguration().setDefaultSynchronizationStrategy(strategy);
253        }
254        
255        /**
256         * {@inheritDoc}
257         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getDialectFactory()
258         */
259        @Override
260        public DialectFactory getDialectFactory()
261        {
262                return this.getNestedConfiguration().getDialectFactory();
263        }
264
265        public void setDialectFactory(DialectFactory factory)
266        {
267                this.getNestedConfiguration().setDialectFactory(factory);
268        }
269        
270        /**
271         * {@inheritDoc}
272         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getDurabilityFactory()
273         */
274        @Override
275        public DurabilityFactory getDurabilityFactory()
276        {
277                return this.getNestedConfiguration().getDurabilityFactory();
278        }
279
280        public void setDurabilityFactory(DurabilityFactory factory)
281        {
282                this.getNestedConfiguration().setDurabilityFactory(factory);
283        }
284        
285        /**
286         * {@inheritDoc}
287         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getExecutorProvider()
288         */
289        @Override
290        public ExecutorServiceProvider getExecutorProvider()
291        {
292                return this.getNestedConfiguration().getExecutorProvider();
293        }
294        
295        public void setExecutorProvider(ExecutorServiceProvider provider)
296        {
297                this.getNestedConfiguration().setExecutorProvider(provider);
298        }
299        
300        /**
301         * {@inheritDoc}
302         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getThreadFactory()
303         */
304        @Override
305        public ThreadFactory getThreadFactory()
306        {
307                return this.getNestedConfiguration().getThreadFactory();
308        }
309
310        public void setThreadFactory(ThreadFactory factory)
311        {
312                this.getNestedConfiguration().setThreadFactory(factory);
313        }
314        
315        /**
316         * {@inheritDoc}
317         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getDecoderFactory()
318         */
319        @Override
320        public DecoderFactory getDecoderFactory()
321        {
322                return this.getNestedConfiguration().getDecoderFactory();
323        }
324
325        public void setCodecFactory(DecoderFactory factory)
326        {
327                this.getNestedConfiguration().setDecoderFactory(factory);
328        }
329        
330        /**
331         * {@inheritDoc}
332         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getFailureDetectionExpression()
333         */
334        @Override
335        public CronExpression getFailureDetectionExpression()
336        {
337                return this.getNestedConfiguration().getFailureDetectionExpression();
338        }
339
340        public void setFailureDetectionExpression(CronExpression expression)
341        {
342                this.getNestedConfiguration().setFailureDetectionExpression(expression);
343        }
344        
345        /**
346         * {@inheritDoc}
347         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getStateManagerFactory()
348         */
349        @Override
350        public StateManagerFactory getStateManagerFactory()
351        {
352                return this.stateManagerFactory;
353        }
354
355        public void setStateManagerFactory(StateManagerFactory factory)
356        {
357                this.stateManagerFactory = factory;
358        }
359        
360        @Override
361        public LockManagerFactory getLockManagerFactory()
362        {
363                return this.lockManagerFactory;
364        }
365
366        public void setLockManagerFactory(LockManagerFactory factory)
367        {
368                this.lockManagerFactory = factory;
369        }
370        
371        /**
372         * {@inheritDoc}
373         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getSynchronizationStrategyMap()
374         */
375        @Override
376        public Map<String, SynchronizationStrategy> getSynchronizationStrategyMap()
377        {
378                return this.synchronizationStrategies;
379        }
380
381        public void setSynchronizationStrategyMap(Map<String, SynchronizationStrategy> strategies)
382        {
383                this.synchronizationStrategies = strategies;
384        }
385        
386        /**
387         * {@inheritDoc}
388         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getTransactionMode()
389         */
390        @Override
391        public TransactionMode getTransactionMode()
392        {
393                return this.getNestedConfiguration().getTransactionMode();
394        }
395
396        public void setTransactionMode(TransactionMode mode)
397        {
398                this.getNestedConfiguration().setTransactionMode(mode);
399        }
400        
401        @Override
402        public InputSinkProvider getInputSinkProvider()
403        {
404                return this.getNestedConfiguration().getInputSinkProvider();
405        }
406
407        public void setInputSinkFactoryProvider(InputSinkProvider provider)
408        {
409                this.getNestedConfiguration().setSinkSourceProvider(provider);
410        }
411        
412        /**
413         * {@inheritDoc}
414         * @see net.sf.hajdbc.DatabaseClusterConfiguration#isCurrentDateEvaluationEnabled()
415         */
416        @Override
417        public boolean isCurrentDateEvaluationEnabled()
418        {
419                return this.getNestedConfiguration().isCurrentDateEvaluationEnabled();
420        }
421
422        public void setCurrentDateEvaluationEnabled(boolean enabled)
423        {
424                this.getNestedConfiguration().setCurrentDateEvaluationEnabled(enabled);
425        }
426        
427        /**
428         * {@inheritDoc}
429         * @see net.sf.hajdbc.DatabaseClusterConfiguration#isCurrentTimeEvaluationEnabled()
430         */
431        @Override
432        public boolean isCurrentTimeEvaluationEnabled()
433        {
434                return this.getNestedConfiguration().isCurrentTimeEvaluationEnabled();
435        }
436
437        public void setCurrentTimeEvaluationEnabled(boolean enabled)
438        {
439                this.getNestedConfiguration().setCurrentTimeEvaluationEnabled(enabled);
440        }
441        
442        /**
443         * {@inheritDoc}
444         * @see net.sf.hajdbc.DatabaseClusterConfiguration#isCurrentTimestampEvaluationEnabled()
445         */
446        @Override
447        public boolean isCurrentTimestampEvaluationEnabled()
448        {
449                return this.getNestedConfiguration().isCurrentTimestampEvaluationEnabled();
450        }
451
452        public void setCurrentTimestampEvaluationEnabled(boolean enabled)
453        {
454                this.getNestedConfiguration().setCurrentTimestampEvaluationEnabled(enabled);
455        }
456        
457        /**
458         * {@inheritDoc}
459         * @see net.sf.hajdbc.DatabaseClusterConfiguration#isIdentityColumnDetectionEnabled()
460         */
461        @Override
462        public boolean isIdentityColumnDetectionEnabled()
463        {
464                return this.getNestedConfiguration().isIdentityColumnDetectionEnabled();
465        }
466
467        public void setIdentityColumnDetectionEnabled(boolean enabled)
468        {
469                this.getNestedConfiguration().setIdentityColumnDetectionEnabled(enabled);
470        }
471        
472        /**
473         * {@inheritDoc}
474         * @see net.sf.hajdbc.DatabaseClusterConfiguration#isRandEvaluationEnabled()
475         */
476        @Override
477        public boolean isRandEvaluationEnabled()
478        {
479                return this.getNestedConfiguration().isRandEvaluationEnabled();
480        }
481
482        public void setRandEvaluationEnabled(boolean enabled)
483        {
484                this.getNestedConfiguration().setRandEvaluationEnabled(enabled);
485        }
486        
487        /**
488         * {@inheritDoc}
489         * @see net.sf.hajdbc.DatabaseClusterConfiguration#isSequenceDetectionEnabled()
490         */
491        @Override
492        public boolean isSequenceDetectionEnabled()
493        {
494                return this.getNestedConfiguration().isSequenceDetectionEnabled();
495        }
496        
497        public void setSequenceDetectionEnabled(boolean enabled)
498        {
499                this.getNestedConfiguration().setSequenceDetectionEnabled(enabled);
500        }
501        
502        static Map<String, Map.Entry<PropertyDescriptor, PropertyEditor>> findDescriptors(Class<?> targetClass) throws Exception
503        {
504                Map<String, Map.Entry<PropertyDescriptor, PropertyEditor>> map = new HashMap<String, Map.Entry<PropertyDescriptor, PropertyEditor>>();
505                
506                for (PropertyDescriptor descriptor: Introspector.getBeanInfo(targetClass).getPropertyDescriptors())
507                {
508                        if ((descriptor.getReadMethod() != null) && (descriptor.getWriteMethod() != null))
509                        {
510                                PropertyEditor editor = PropertyEditorManager.findEditor(descriptor.getPropertyType());
511                                
512                                if (editor != null)
513                                {
514                                        map.put(descriptor.getName(), new AbstractMap.SimpleImmutableEntry<PropertyDescriptor, PropertyEditor>(descriptor, editor));
515                                }
516                        }
517                }
518                
519                return map;
520        }
521        
522        /**
523         * {@inheritDoc}
524         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getMBeanRegistrar()
525         */
526        @Override
527        public MBeanRegistrar<Z, D> getMBeanRegistrar()
528        {
529                return this.getNestedConfiguration().getMBeanRegistrar();
530        }
531
532        public void setMBeanRegistrar(MBeanRegistrar<Z, D> registrar)
533        {
534                this.getNestedConfiguration().setMBeanRegistrar(registrar);
535        }
536        
537        /**
538         * {@inheritDoc}
539         * @see net.sf.hajdbc.DatabaseClusterConfiguration#isEmptyClusterAllowed()
540         */
541        @Override
542        public boolean isEmptyClusterAllowed()
543        {
544                return this.getNestedConfiguration().isEmptyClusterAllowed();
545        }
546
547        public void setEmptyClusterAllowed(boolean emptyClusterAllowed)
548        {
549                this.getNestedConfiguration().setEmptyClusterAllowed(emptyClusterAllowed);
550        }
551        
552        /**
553         * {@inheritDoc}
554         * @see net.sf.hajdbc.DatabaseClusterConfiguration#getTransactionIdentifierFactory()
555         */
556        @Override
557        public TransactionIdentifierFactory<? extends Object> getTransactionIdentifierFactory()
558        {
559                return (this.dispatcherFactory != null) ? new UUIDTransactionIdentifierFactory() : new SimpleTransactionIdentifierFactory();
560        }
561
562        @XmlType(name = "abstractNestedConfiguration")
563        protected static abstract class NestedConfiguration<Z, D extends Database<Z>> implements DatabaseClusterConfiguration<Z, D>, DatabaseFactory<Z, D>
564        {
565                private static final long serialVersionUID = -5674156614205147546L;
566
567                @XmlJavaTypeAdapter(BalancerFactoryAdapter.class)
568                @XmlAttribute(name = "balancer")
569                private BalancerFactory balancerFactory = ServiceLoaders.findService(BalancerFactory.class);
570                
571                @XmlJavaTypeAdapter(DatabaseMetaDataCacheFactoryAdapter.class)
572                @XmlAttribute(name = "meta-data-cache")
573                private DatabaseMetaDataCacheFactory databaseMetaDataCacheFactory = ServiceLoaders.findService(DatabaseMetaDataCacheFactory.class);
574                
575                @XmlJavaTypeAdapter(DialectFactoryAdapter.class)
576                @XmlAttribute(name = "dialect")
577                private DialectFactory dialectFactory = ServiceLoaders.findService(DialectFactory.class);
578                
579                @XmlJavaTypeAdapter(DurabilityFactoryAdapter.class)
580                @XmlAttribute(name = "durability")
581                private DurabilityFactory durabilityFactory = ServiceLoaders.findService(DurabilityFactory.class);
582
583                @XmlJavaTypeAdapter(SinkSourceProviderAdapter.class)
584                @XmlAttribute(name = "input-sink")
585                private InputSinkProvider sinkSourceProvider = ServiceLoaders.findService(InputSinkProvider.class);
586                
587                private ExecutorServiceProvider executorProvider = new DefaultExecutorServiceProvider();
588                private ThreadFactory threadFactory = Executors.defaultThreadFactory();
589                private DecoderFactory decoderFactory = new MultiplexingDecoderFactory();
590                private MBeanRegistrar<Z, D> registrar = new DefaultMBeanRegistrar<Z, D>();
591                
592                @XmlJavaTypeAdapter(TransactionModeAdapter.class)
593                @XmlAttribute(name = "transaction-mode")
594                private TransactionMode transactionMode = TransactionModeEnum.SERIAL;
595
596                @XmlJavaTypeAdapter(CronExpressionAdapter.class)
597                @XmlAttribute(name = "auto-activate-schedule")
598                private CronExpression autoActivationExpression;
599                @XmlJavaTypeAdapter(CronExpressionAdapter.class)
600                @XmlAttribute(name = "failure-detect-schedule")
601                private CronExpression failureDetectionExpression;
602                
603                @XmlAttribute(name = "eval-current-date")
604                private Boolean currentDateEvaluationEnabled = false;
605                @XmlAttribute(name = "eval-current-time")
606                private Boolean currentTimeEvaluationEnabled = false;
607                @XmlAttribute(name = "eval-current-timestamp")
608                private Boolean currentTimestampEvaluationEnabled = false;
609                @XmlAttribute(name = "eval-rand")
610                private Boolean randEvaluationEnabled = false;
611                
612                @XmlAttribute(name = "detect-identity-columns")
613                private Boolean identityColumnDetectionEnabled = false;
614                @XmlAttribute(name = "detect-sequences")
615                private Boolean sequenceDetectionEnabled = false;
616
617                @XmlAttribute(name = "allow-empty-cluster")
618                private Boolean emptyClusterAllowed = false;
619                
620                private String defaultSynchronizationStrategy;
621                
622                private ConcurrentMap<String, D> databases = new ConcurrentHashMap<String, D>();
623                
624                @Override
625                public DatabaseFactory<Z, D> getDatabaseFactory()
626                {
627                        return this;
628                }
629
630                @XmlIDREF
631                @XmlAttribute(name = "default-sync", required = true)
632                private SynchronizationStrategyDescriptor getDefaultSynchronizationStrategyDescriptor()
633                {
634                        SynchronizationStrategyDescriptor descriptor = new SynchronizationStrategyDescriptor();
635                        descriptor.setId(this.defaultSynchronizationStrategy);
636                        return descriptor;
637                }
638                
639                @SuppressWarnings("unused")
640                private void setDefaultSynchronizationStrategyDescriptor(SynchronizationStrategyDescriptor descriptor)
641                {
642                        this.defaultSynchronizationStrategy = descriptor.getId();
643                }
644                
645                @Override
646                public ConcurrentMap<String, D> getDatabaseMap()
647                {
648                        return this.databases;
649                }
650                
651                @Override
652                public CronExpression getAutoActivationExpression()
653                {
654                        return this.autoActivationExpression;
655                }
656
657                void setAutoActivationExpression(CronExpression expression)
658                {
659                        this.autoActivationExpression = expression;
660                }
661                
662                @Override
663                public BalancerFactory getBalancerFactory()
664                {
665                        return this.balancerFactory;
666                }
667
668                void setBalancerFactory(BalancerFactory factory)
669                {
670                        this.balancerFactory = factory;
671                }
672                
673                @Override
674                public CommandDispatcherFactory getDispatcherFactory()
675                {
676                        throw new IllegalStateException();
677                }
678
679                @Override
680                public DatabaseMetaDataCacheFactory getDatabaseMetaDataCacheFactory()
681                {
682                        return this.databaseMetaDataCacheFactory;
683                }
684
685                void setDatabaseMetaDataCacheFactory(DatabaseMetaDataCacheFactory factory)
686                {
687                        this.databaseMetaDataCacheFactory = factory;
688                }
689                
690                @Override
691                public String getDefaultSynchronizationStrategy()
692                {
693                        return this.defaultSynchronizationStrategy;
694                }
695
696                void setDefaultSynchronizationStrategy(String strategy)
697                {
698                        this.defaultSynchronizationStrategy = strategy;
699                }
700                
701                @Override
702                public DialectFactory getDialectFactory()
703                {
704                        return this.dialectFactory;
705                }
706
707                void setDialectFactory(DialectFactory factory)
708                {
709                        this.dialectFactory = factory;
710                }
711                
712                @Override
713                public DurabilityFactory getDurabilityFactory()
714                {
715                        return this.durabilityFactory;
716                }
717
718                void setDurabilityFactory(DurabilityFactory factory)
719                {
720                        this.durabilityFactory = factory;
721                }
722                
723                @Override
724                public ExecutorServiceProvider getExecutorProvider()
725                {
726                        return this.executorProvider;
727                }
728
729                void setExecutorProvider(ExecutorServiceProvider provider)
730                {
731                        this.executorProvider = provider;
732                }
733                
734                @Override
735                public ThreadFactory getThreadFactory()
736                {
737                        return this.threadFactory;
738                }
739
740                void setThreadFactory(ThreadFactory factory)
741                {
742                        this.threadFactory = factory;
743                }
744                
745                @Override
746                public DecoderFactory getDecoderFactory()
747                {
748                        return this.decoderFactory;
749                }
750
751                void setDecoderFactory(DecoderFactory factory)
752                {
753                        this.decoderFactory = factory;
754                }
755                
756                @Override
757                public MBeanRegistrar<Z, D> getMBeanRegistrar()
758                {
759                        return this.registrar;
760                }
761                
762                void setMBeanRegistrar(MBeanRegistrar<Z, D> registrar)
763                {
764                        this.registrar = registrar;
765                }
766                
767                @Override
768                public CronExpression getFailureDetectionExpression()
769                {
770                        return this.failureDetectionExpression;
771                }
772
773                void setFailureDetectionExpression(CronExpression expression)
774                {
775                        this.failureDetectionExpression = expression;
776                }
777                
778                @Override
779                public StateManagerFactory getStateManagerFactory()
780                {
781                        throw new IllegalStateException();
782                }
783                
784                @Override
785                public LockManagerFactory getLockManagerFactory()
786                {
787                        throw new IllegalStateException();
788                }
789
790                @Override
791                public Map<String, SynchronizationStrategy> getSynchronizationStrategyMap()
792                {
793                        throw new IllegalStateException();
794                }
795                
796                @Override
797                public TransactionMode getTransactionMode()
798                {
799                        return this.transactionMode;
800                }
801
802                void setTransactionMode(TransactionMode mode)
803                {
804                        this.transactionMode = mode;
805                }
806                
807                @Override
808                public InputSinkProvider getInputSinkProvider()
809                {
810                        return this.sinkSourceProvider;
811                }
812
813                void setSinkSourceProvider(InputSinkProvider provider)
814                {
815                        this.sinkSourceProvider = provider;
816                }
817                
818                @Override
819                public boolean isCurrentDateEvaluationEnabled()
820                {
821                        return this.currentDateEvaluationEnabled;
822                }
823                
824                void setCurrentDateEvaluationEnabled(boolean enabled)
825                {
826                        this.currentDateEvaluationEnabled = enabled;
827                }
828                
829                @Override
830                public boolean isCurrentTimeEvaluationEnabled()
831                {
832                        return this.currentTimeEvaluationEnabled;
833                }
834                
835                void setCurrentTimeEvaluationEnabled(boolean enabled)
836                {
837                        this.currentTimeEvaluationEnabled = enabled;
838                }
839                
840                @Override
841                public boolean isCurrentTimestampEvaluationEnabled()
842                {
843                        return this.currentTimestampEvaluationEnabled;
844                }
845                
846                void setCurrentTimestampEvaluationEnabled(boolean enabled)
847                {
848                        this.currentTimestampEvaluationEnabled = enabled;
849                }
850                
851                @Override
852                public boolean isIdentityColumnDetectionEnabled()
853                {
854                        return this.identityColumnDetectionEnabled;
855                }
856                
857                void setIdentityColumnDetectionEnabled(boolean enabled)
858                {
859                        this.identityColumnDetectionEnabled = enabled;
860                }
861                
862                @Override
863                public boolean isRandEvaluationEnabled()
864                {
865                        return this.randEvaluationEnabled;
866                }
867
868                void setRandEvaluationEnabled(boolean enabled)
869                {
870                        this.randEvaluationEnabled = enabled;
871                }
872                
873                @Override
874                public boolean isSequenceDetectionEnabled()
875                {
876                        return this.sequenceDetectionEnabled;
877                }
878                
879                void setSequenceDetectionEnabled(boolean enabled)
880                {
881                        this.sequenceDetectionEnabled = enabled;
882                }
883
884                @Override
885                public TransactionIdentifierFactory<? extends Object> getTransactionIdentifierFactory()
886                {
887                        throw new IllegalStateException();
888                }
889
890                /* (non-Javadoc)
891                 * @see net.sf.hajdbc.DatabaseClusterConfiguration#isEmptyClusterAllowed()
892                 */
893                @Override
894                public boolean isEmptyClusterAllowed()
895                {
896                        return this.emptyClusterAllowed;
897                }
898                
899                void setEmptyClusterAllowed(boolean emptyClusterAllowed)
900                {
901                        this.emptyClusterAllowed = emptyClusterAllowed;
902                }
903        }
904
905        static class IdentifiableServiceAdapter<T extends Identifiable> extends XmlAdapter<String, T>
906        {
907                private final Class<T> serviceClass;
908                
909                IdentifiableServiceAdapter(Class<T> serviceClass)
910                {
911                        this.serviceClass = serviceClass;
912                }
913
914                @Override
915                public T unmarshal(final String value)
916                {
917                        return ServiceLoaders.findRequiredService(new IdentifiableMatcher<T>(value), this.serviceClass);
918                }
919
920                @Override
921                public String marshal(T service)
922                {
923                        return service.getId();
924                }
925        }
926
927        static class BalancerFactoryAdapter extends IdentifiableServiceAdapter<BalancerFactory>
928        {
929                BalancerFactoryAdapter()
930                {
931                        super(BalancerFactory.class);
932                }
933        }
934
935        static class DatabaseMetaDataCacheFactoryAdapter extends IdentifiableServiceAdapter<DatabaseMetaDataCacheFactory>
936        {
937                DatabaseMetaDataCacheFactoryAdapter()
938                {
939                        super(DatabaseMetaDataCacheFactory.class);
940                }
941        }
942
943        static class DurabilityFactoryAdapter extends IdentifiableServiceAdapter<DurabilityFactory>
944        {
945                DurabilityFactoryAdapter()
946                {
947                        super(DurabilityFactory.class);
948                }
949        }
950
951        static class TransactionModeAdapter extends EnumAdapter<TransactionMode, TransactionModeEnum>
952        {
953                @Override
954                protected Class<TransactionModeEnum> getTargetClass()
955                {
956                        return TransactionModeEnum.class;
957                }
958        }
959
960        static abstract class EnumAdapter<I, E extends I> extends XmlAdapter<E, I>
961        {
962                @Override
963                public I unmarshal(E enumerated)
964                {
965                        return enumerated;
966                }
967                
968                @Override
969                public E marshal(I object)
970                {
971                        return this.getTargetClass().cast(object);
972                }
973
974                protected abstract Class<E> getTargetClass();
975        }
976
977        static class DialectFactoryAdapter extends IdentifiableServiceAdapter<DialectFactory>
978        {
979                DialectFactoryAdapter()
980                {
981                        super(DialectFactory.class);
982                }
983        }
984
985        static class SinkSourceProviderAdapter extends IdentifiableServiceAdapter<InputSinkProvider>
986        {
987                SinkSourceProviderAdapter()
988                {
989                        super(InputSinkProvider.class);
990                }
991        }
992        
993        static class CronExpressionAdapter extends XmlAdapter<String, CronExpression>
994        {
995                @Override
996                public String marshal(CronExpression expression)
997                {
998                        return (expression != null) ? expression.getCronExpression() : null;
999                }
1000
1001                @Override
1002                public CronExpression unmarshal(String value) throws Exception
1003                {
1004                        return (value != null) ? new CronExpression(value) : null;
1005                }
1006        }
1007        
1008        @XmlType
1009        static class CommandDispatcherFactoryDescriptor extends IdentifiableServiceDescriptor
1010        {
1011                @XmlAttribute(name = "id", required = false)
1012                private String id = "jgroups";
1013                
1014                @Override
1015                public String getId()
1016                {
1017                        return this.id;
1018                }
1019                
1020                @Override
1021                public void setId(String id)
1022                {
1023                        this.id = id;
1024                }
1025        }
1026
1027        static class CommandDispatcherFactoryDescriptorAdapter extends IdentifiableServiceDescriptorAdapter<CommandDispatcherFactory, CommandDispatcherFactoryDescriptor>
1028        {
1029                CommandDispatcherFactoryDescriptorAdapter()
1030                {
1031                        super(CommandDispatcherFactory.class, CommandDispatcherFactoryDescriptor.class);
1032                }
1033        }
1034        
1035        @XmlType
1036        static class SynchronizationStrategyDescriptor extends IdentifiableServiceDescriptor
1037        {
1038                @XmlID
1039                @XmlAttribute(name = "id", required = true)
1040                private String id;
1041                
1042                @Override
1043                public String getId()
1044                {
1045                        return this.id;
1046                }
1047                
1048                @Override
1049                public void setId(String id)
1050                {
1051                        this.id = id;
1052                }
1053        }
1054        
1055        static class SynchronizationStrategyDescriptorAdapter extends IdentifiableServiceDescriptorAdapter<SynchronizationStrategy, SynchronizationStrategyDescriptor>
1056        {
1057                SynchronizationStrategyDescriptorAdapter()
1058                {
1059                        super(SynchronizationStrategy.class, SynchronizationStrategyDescriptor.class);
1060                }
1061        }
1062        
1063        @XmlType
1064        static class StateManagerFactoryDescriptor extends IdentifiableServiceDescriptor
1065        {
1066                @XmlAttribute(name = "id", required = true)
1067                private String id;
1068                
1069                @Override
1070                public String getId()
1071                {
1072                        return this.id;
1073                }
1074                
1075                @Override
1076                public void setId(String id)
1077                {
1078                        this.id = id;
1079                }
1080        }
1081
1082        static class StateManagerFactoryDescriptorAdapter extends IdentifiableServiceDescriptorAdapter<StateManagerFactory, StateManagerFactoryDescriptor>
1083        {
1084                StateManagerFactoryDescriptorAdapter()
1085                {
1086                        super(StateManagerFactory.class, StateManagerFactoryDescriptor.class);
1087                }
1088        }
1089        
1090        @XmlType
1091        static class LockManagerFactoryDescriptor extends IdentifiableServiceDescriptor
1092        {
1093                @XmlAttribute(name = "id", required = true)
1094                private String id;
1095                
1096                @Override
1097                public String getId()
1098                {
1099                        return this.id;
1100                }
1101                
1102                @Override
1103                public void setId(String id)
1104                {
1105                        this.id = id;
1106                }
1107        }
1108
1109        static class LockManagerFactoryDescriptorAdapter extends IdentifiableServiceDescriptorAdapter<LockManagerFactory, LockManagerFactoryDescriptor>
1110        {
1111                LockManagerFactoryDescriptorAdapter()
1112                {
1113                        super(LockManagerFactory.class, LockManagerFactoryDescriptor.class);
1114                }
1115        }
1116
1117        static abstract class IdentifiableServiceDescriptor implements Identifiable
1118        {
1119                @XmlElement(name = "property")
1120                private List<Property> properties;
1121                
1122                public List<Property> getProperties()
1123                {
1124                        return this.properties;
1125                }
1126
1127                public void setProperties(List<Property> properties)
1128                {
1129                        this.properties = properties;
1130                }
1131                
1132                public abstract void setId(String id);
1133        }
1134
1135        static class IdentifiableServiceDescriptorAdapter<T extends Identifiable, D extends IdentifiableServiceDescriptor> extends XmlAdapter<D, T>
1136        {
1137                private final Class<T> serviceClass;
1138                private final Class<D> descriptorClass;
1139                
1140                IdentifiableServiceDescriptorAdapter(Class<T> serviceClass, Class<D> descriptorClass)
1141                {
1142                        this.serviceClass = serviceClass;
1143                        this.descriptorClass = descriptorClass;
1144                }
1145                
1146                @Override
1147                public D marshal(T object) throws Exception
1148                {
1149                        D result = this.descriptorClass.newInstance();
1150                        List<Property> properties = new LinkedList<Property>();
1151                        
1152                        result.setId(object.getId());
1153                        result.setProperties(properties);
1154                        
1155                        for (Map.Entry<PropertyDescriptor, PropertyEditor> entry: findDescriptors(object.getClass()).values())
1156                        {
1157                                PropertyDescriptor descriptor = entry.getKey();
1158                                PropertyEditor editor = entry.getValue();
1159                                
1160                                Object value = descriptor.getReadMethod().invoke(object);
1161                                if (value != null)
1162                                {
1163                                        editor.setValue(value);
1164                                        
1165                                        Property property = new Property();
1166                                        property.setName(descriptor.getName());
1167                                        property.setValue(editor.getAsText());
1168                                        
1169                                        properties.add(property);
1170                                }
1171                        }
1172                        
1173                        return result;
1174                }
1175
1176                @Override
1177                public T unmarshal(D target) throws Exception
1178                {
1179                        T result = ServiceLoaders.findRequiredService(new IdentifiableMatcher<T>(target.getId()), this.serviceClass);
1180                        List<Property> properties = target.getProperties();
1181                        
1182                        if (properties != null)
1183                        {
1184                                Map<String, Map.Entry<PropertyDescriptor, PropertyEditor>> descriptors = findDescriptors(result.getClass());
1185                                
1186                                for (Property property: properties)
1187                                {
1188                                        String name = property.getName();
1189                                        Map.Entry<PropertyDescriptor, PropertyEditor> entry = descriptors.get(name);
1190                                        
1191                                        if (entry == null)
1192                                        {
1193                                                throw new IllegalArgumentException(Messages.INVALID_PROPERTY.getMessage(name, result.getClass().getName()));
1194                                        }
1195                                        
1196                                        PropertyDescriptor descriptor = entry.getKey();
1197                                        PropertyEditor editor = entry.getValue();
1198
1199                                        String textValue = property.getValue();
1200                                        
1201                                        try
1202                                        {
1203                                                editor.setAsText(textValue);
1204                                        }
1205                                        catch (Exception e)
1206                                        {
1207                                                throw new IllegalArgumentException(Messages.INVALID_PROPERTY_VALUE.getMessage(textValue, name, result.getClass().getName()));
1208                                        }
1209                                        descriptor.getWriteMethod().invoke(result, editor.getValue());
1210                                }
1211                        }
1212                        return result;
1213                }
1214        }
1215
1216        @XmlType
1217        protected static class Property
1218        {
1219                @XmlAttribute(required = true)
1220                private String name;
1221                @XmlValue
1222                private String value;
1223                
1224                public String getName()
1225                {
1226                        return this.name;
1227                }
1228                
1229                public void setName(String name)
1230                {
1231                        this.name = name;
1232                }
1233                
1234                public String getValue()
1235                {
1236                        return this.value;
1237                }
1238                
1239                public void setValue(String value)
1240                {
1241                        this.value = value;
1242                }
1243        }
1244}