blob: 248c2ac36b913a870ae46613d81db44992ef416f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.mitosis.configuration;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.directory.mitosis.common.CSN;
import org.apache.directory.mitosis.common.CSNFactory;
import org.apache.directory.mitosis.common.Replica;
import org.apache.directory.mitosis.common.ReplicaId;
import org.apache.directory.mitosis.common.DefaultCSNFactory;
import org.apache.directory.mitosis.common.DefaultUUIDFactory;
import org.apache.directory.mitosis.common.UUID;
import org.apache.directory.mitosis.common.UUIDFactory;
import org.apache.directory.mitosis.service.ReplicationInterceptor;
import org.apache.directory.mitosis.store.ReplicationStore;
import org.apache.directory.mitosis.store.derby.DerbyReplicationStore;
import org.apache.directory.shared.ldap.util.StringTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A configuration for {@link ReplicationInterceptor}. This configuration can be
* used by calling {@link ReplicationInterceptor#setConfiguration(ReplicationConfiguration)}.
*
* @org.apache.xbean.XBean
*
* @author The Apache Directory Project Team
*/
public class ReplicationConfiguration
{
public static final int DEFAULT_LOG_MAX_AGE = 7;
public static final int DEFAULT_REPLICATION_INTERVAL = 5;
public static final int DEFAULT_RESPONSE_TIMEOUT = 60;
public static final int DEFAULT_SERVER_PORT = 7846;
/** The logger */
private static Logger log = LoggerFactory.getLogger( ReplicationConfiguration.class );
/** The server identifier */
private ReplicaId replicaId;
/** Default values for the communication part */
private int serverPort = DEFAULT_SERVER_PORT;
private int responseTimeout = DEFAULT_RESPONSE_TIMEOUT;
private int replicationInterval = DEFAULT_REPLICATION_INTERVAL;
/** List of connected replicas */
private final Set<Replica> peerReplicas = new HashSet<Replica>();
/** Factories */
private UUIDFactory uuidFactory = new DefaultUUIDFactory();
private CSNFactory csnFactory = new DefaultCSNFactory();
private ReplicationStore store = new DerbyReplicationStore();
/** The longest period of time before a stored entry is removed from storage */
private int logMaxAge = DEFAULT_LOG_MAX_AGE; // a week (days)
/**
* Creates a new instance with default properties except for
* the {@link ReplicaId} of the service and the the list of peer
* {@link Replica}s. You can set these properties by calling
* {@link #setReplicaId(ReplicaId)} and {@link #setPeerReplicas(Set)}
* respectively.
*/
public ReplicationConfiguration()
{
}
/**
* Returns the TCP/IP port number that a {@link ReplicationInterceptor}
* listens to. The default value is {@link #DEFAULT_SERVER_PORT}.
*/
public int getServerPort()
{
return serverPort;
}
/**
* Sets the TCP/IP port number that a {@link ReplicationInterceptor}
* listens to. The default value is {@link #DEFAULT_SERVER_PORT}.
*/
public void setServerPort( int serverPort )
{
this.serverPort = serverPort;
}
/**
* Returns the response timeout value (seconds) for each sent message
* during the communication between replicas. If any response message
* is not received within this timeout, the connection is closed and
* reestablished. The default value is {@link #DEFAULT_RESPONSE_TIMEOUT}.
*/
public int getResponseTimeout()
{
return responseTimeout;
}
/**
* Sets the response timeout value (seconds) for each sent message
* during the communication between replicas. If any response message
* is not received within this timeout, the connection is closed and
* reestablished. The default value is {@link #DEFAULT_RESPONSE_TIMEOUT}.
*/
public void setResponseTimeout( int responseTimeout )
{
this.responseTimeout = responseTimeout;
}
/**
* Returns the replication data exchange interval (seconds) between two
* replicas. The default value is {@link #DEFAULT_REPLICATION_INTERVAL}.
*
* @return <tt>0</tt> if automatic replication is disabled
*/
public int getReplicationInterval() {
return replicationInterval;
}
/**
* Sets the replication data exchange interval (seconds) between two
* replicas. The default value is {@link #DEFAULT_REPLICATION_INTERVAL}.
*
* @param replicationInterval <tt>0</tt> or below to disable automatic replication.
*/
public void setReplicationInterval( int replicationInterval )
{
if( replicationInterval < 0 )
{
replicationInterval = 0;
}
this.replicationInterval = replicationInterval;
}
/**
* Returns the {@link CSNFactory} for generating {@link CSN}s.
* The default factory is {@link DefaultCSNFactory}.
*/
public CSNFactory getCsnFactory()
{
return csnFactory;
}
/**
* Sets the {@link CSNFactory} for generating {@link CSN}s.
* The default factory is {@link DefaultCSNFactory}.
*/
public void setCsnFactory( CSNFactory csnFactory )
{
this.csnFactory = csnFactory;
}
/**
* Adds the specified {@link Replica} to the remote peer replica list.
*/
public void addPeerReplica( Replica peer )
{
assert peer != null;
peerReplicas.add( peer );
}
/**
* Removed the specified {@link Replica} from the remote peer replica list.
*/
public void removePeerReplica( Replica peer )
{
assert peer != null;
peerReplicas.remove( peer );
}
/**
* Clears the remote peer replica list.
*/
public void removeAllPeerReplicas()
{
peerReplicas.clear();
}
/**
* Returns the remote peer replica list.
*/
public Set<Replica> getPeerReplicas()
{
Set<Replica> result = new HashSet<Replica>();
result.addAll( peerReplicas );
return result;
}
/**
* Sets the remote peer replica list.
*/
public void setPeerReplicas( Set replicas )
{
assert replicas != null;
Set<Replica> normalizedReplicas = new HashSet<Replica>();
Iterator i = replicas.iterator();
while ( i.hasNext() )
{
Object o = i.next();
if ( o instanceof Replica )
{
normalizedReplicas.add( ( Replica ) o );
}
else
{
normalizedReplicas.add( new Replica( o.toString() ) );
}
}
peerReplicas.clear();
peerReplicas.addAll( normalizedReplicas );
}
/**
* Returns the ID of the replica this configuration is configuring.
*/
public ReplicaId getReplicaId()
{
return replicaId;
}
/**
* Sets the ID of the replica this configuration is configuring.
*/
public void setReplicaId( ReplicaId replicaId )
{
assert replicaId != null;
this.replicaId = replicaId;
}
/**
* Returns the {@link ReplicationStore} which stores the change log
* of the replica this configuration is configuring. The default
* implementation is {@link DerbyReplicationStore}.
*/
public ReplicationStore getStore()
{
return store;
}
/**
* Sets the {@link ReplicationStore} which stores the change log
* of the replica this configuration is configuring. The default
* implementation is {@link DerbyReplicationStore}.
*/
public void setStore( ReplicationStore store )
{
this.store = store;
}
/**
* Returns the {@link UUIDFactory} which generates {@link UUID}s for
* new directory entries. The default implementation is
* {@link DefaultUUIDFactory}.
*/
public UUIDFactory getUuidFactory()
{
return uuidFactory;
}
/**
* Sets the {@link UUIDFactory} which generates {@link UUID}s for
* new directory entries. The default implementation is
* {@link DefaultUUIDFactory}.
*/
public void setUuidFactory( UUIDFactory uuidFactory )
{
this.uuidFactory = uuidFactory;
}
/**
* Returns the maximum age (days) of change logs stored in
* {@link ReplicationStore}. Any change logs and deleted entries
* older than this value will be purged periodically. The default value
* is {@link #DEFAULT_LOG_MAX_AGE}.
*/
public int getLogMaxAge()
{
return logMaxAge;
}
/**
* Sets the maximum age (days) of change logs stored in
* {@link ReplicationStore}. Any change logs and deleted entries
* older than this value will be purged periodically. The default value
* is {@link #DEFAULT_LOG_MAX_AGE}.
*/
public void setLogMaxAge( int logMaxAge )
{
if ( logMaxAge <= 0 )
{
throw new ReplicationConfigurationException( "logMaxAge: " + logMaxAge );
}
this.logMaxAge = logMaxAge;
}
/**
* Validate Mitosis configuration.
*
* We check that the configuration file contains valid
* parameters :<br/>
* - a replicaId<br/>
* - a valid server port (between 0 and 65535)<br/>
* - a valid response timeout ( > 0 )<br/>
* - a uuidFactory<br/>
* - a CSN factory<br/>
* - a store (derby)<br/>
* - a list of valid replica, none of them being equal
* to the replicaId
*
* @throws ReplicationConfigurationException If the configuration file is invalid
*/
public void validate() throws ReplicationConfigurationException
{
if ( replicaId == null )
{
log.error( "The replicaId is missing" );
throw new ReplicationConfigurationException( "Replica ID is not specified." );
}
if ( ( serverPort < 0 ) || ( serverPort > 65535 ) )
{
log.error( "The replica port is not between 0 and 65535" );
throw new ReplicationConfigurationException( "Server port is invalid: " + serverPort );
}
if ( responseTimeout <= 0 )
{
log.error( "The replica responsetimeout is negative" );
throw new ReplicationConfigurationException( "Invalid response timeout: " + responseTimeout );
}
if ( uuidFactory == null )
{
log.error( "The UUID factory has not been declared" );
throw new ReplicationConfigurationException( "UUID factory is not specified." );
}
if ( csnFactory == null )
{
log.error( "The CSN factory has not been declared" );
throw new ReplicationConfigurationException( "CSN factory is not specified." );
}
if ( store == null )
{
log.error( "The store has not been declared" );
throw new ReplicationConfigurationException( "Replication store is not specified." );
}
if ( peerReplicas.size() == 0 )
{
log.error( "The replicas peer list is empty" );
throw new ReplicationConfigurationException( "No peer replicas" );
}
// Check the peer replicas.
// We should check that no replica has the same Id, and that we don't
// have two replicas on the same server with the same port
Set<String> ids = new TreeSet<String>();
Map<String, Integer> servers = new HashMap<String, Integer>();
// Initialize the set with this server replicaId
ids.add( replicaId.getId() );
// And store the local inetadress
servers.put( "localhost", serverPort );
servers.put( "127.0.0.1", serverPort );
try
{
servers.put( StringTools.lowerCase( InetAddress.getByName( "127.0.0.1" ).getHostName() ), serverPort );
}
catch ( UnknownHostException uhe )
{
// Should never occurs with 127.0.0.1
throw new ReplicationConfigurationException( "Unknown host name" );
}
for ( Replica peer:peerReplicas )
{
if ( ids.contains( peer.getId().getId() ) )
{
log.error( "Peer replica ID '{}' has already been declared.", peer.getId() );
throw new ReplicationConfigurationException( "Peer replica ID '" + peer.getId()
+ "' has already been declared." );
}
// Now check that we don't already have a replica on a server with the same port
String replicaServer = StringTools.lowerCase( peer.getAddress().getHostName() );
int replicaPort = peer.getAddress().getPort();
if ( servers.containsKey( replicaServer ) )
{
int peerPort = servers.get( replicaServer );
if ( replicaPort == peerPort )
{
log.error(
"The replica in the peer list has already been declared on the server {} with the port {}",
replicaServer, peerPort );
throw new ReplicationConfigurationException( "Replication store is not specified." );
}
}
servers.put( replicaServer, replicaPort );
}
}
}