// WriteConcern.java
/**
* Copyright (C) 2008-2011 10gen Inc.
*
* Licensed 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 com.mongodb;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
/**
* <p>WriteConcern control the write behavior for with various options, as well as exception raising on error conditions.</p>
*
* <p>
* <b>w</b>
* <ul>
* <li>-1 = don't even report network errors </li>
* <li> 0 = default, don't call getLastError by default </li>
* <li> 1 = basic, call getLastError, but don't wait for slaves</li>
* <li> 2+= wait for slaves </li>
* </ul>
* <b>wtimeout</b> how long to wait for slaves before failing
* <ul>
* <li>0 = indefinite </li>
* <li>> 0 = ms to wait </li>
* </ul>
* </p>
* <p><b>fsync</b> force fsync to disk </p>
*
* @dochub databases
*/
public class WriteConcern implements Serializable {
private static final long serialVersionUID = 1884671104750417011L;
/** No exceptions are raised, even for network issues */
public final static WriteConcern NONE = new WriteConcern(-1);
/** Exceptions are raised for network issues, but not server errors */
public final static WriteConcern NORMAL = new WriteConcern(0);
/** Exceptions are raised for network issues, and server errors; waits on a server for the write operation */
public final static WriteConcern SAFE = new WriteConcern(1);
/** Exceptions are raised for network issues, and server errors; waits on a majority of servers for the write operation */
public final static WriteConcern MAJORITY = new Majority();
/** Exceptions are raised for network issues, and server errors; the write operation waits for the server to flush the data to disk*/
public final static WriteConcern FSYNC_SAFE = new WriteConcern(true);
/** Exceptions are raised for network issues, and server errors; the write operation waits for the server to group commit to the journal file on disk*/
public final static WriteConcern JOURNAL_SAFE = new WriteConcern( 1, 0, false, true );
/** Exceptions are raised for network issues, and server errors; waits for at least 2 servers for the write operation*/
public final static WriteConcern REPLICAS_SAFE = new WriteConcern(2);
// map of the constants from above for use by fromString
private static Map<String, WriteConcern> _namedConcerns = null;
/**
* Default constructor keeping all options as default
*/
public WriteConcern(){
this(0);
}
/**
* Calls {@link WriteConcern#WriteConcern(int, int, boolean)} with wtimeout=0 and fsync=false
* @param w number of writes
*/
public WriteConcern( int w ){
this( w , 0 , false );
}
/**
* Tag based Write Concern with wtimeout=0, fsync=false, and j=false
* @param w Write Concern tag
*/
public WriteConcern( String w ){
this( w , 0 , false, false );
}
/**
* Calls {@link WriteConcern#WriteConcern(int, int, boolean)} with fsync=false
* @param w number of writes
* @param wtimeout timeout for write operation
*/
public WriteConcern( int w , int wtimeout ){
this( w , wtimeout , false );
}
/**
* Calls {@link WriteConcern#WriteConcern(int, int, boolean)} with w=1 and wtimeout=0
* @param fsync whether or not to fsync
*/
public WriteConcern( boolean fsync ){
this( 1 , 0 , fsync);
}
/**
* Creates a WriteConcern object.
* <p>Specifies the number of servers to wait for on the write operation, and exception raising behavior </p>
* <p> w represents the number of servers:
* <ul>
* <li>{@code w=-1} None, no checking is done</li>
* <li>{@code w=0} None, network socket errors raised</li>
* <li>{@code w=1} Checks server for errors as well as network socket errors raised</li>
* <li>{@code w>1} Checks servers (w) for errors as well as network socket errors raised</li>
* </ul>
* </p>
* @param w number of writes
* @param wtimeout timeout for write operation
* @param fsync whether or not to fsync
*/
public WriteConcern( int w , int wtimeout , boolean fsync ){
this(w, wtimeout, fsync, false);
}
/**
* Creates a WriteConcern object.
* <p>Specifies the number of servers to wait for on the write operation, and exception raising behavior </p>
* <p> w represents the number of servers:
* <ul>
* <li>{@code w=-1} None, no checking is done</li>
* <li>{@code w=0} None, network socket errors raised</li>
* <li>{@code w=1} Checks server for errors as well as network socket errors raised</li>
* <li>{@code w>1} Checks servers (w) for errors as well as network socket errors raised</li>
* </ul>
* </p>
* @param w number of writes
* @param wtimeout timeout for write operation
* @param fsync whether or not to fsync
* @param j whether writes should wait for a journaling group commit
*/
public WriteConcern( int w , int wtimeout , boolean fsync , boolean j ){
this( w, wtimeout, fsync, j, false);
}
/**
* Creates a WriteConcern object.
* <p>Specifies the number of servers to wait for on the write operation, and exception raising behavior </p>
* <p> w represents the number of servers:
* <ul>
* <li>{@code w=-1} None, no checking is done</li>
* <li>{@code w=0} None, network socket errors raised</li>
* <li>{@code w=1} Checks server for errors as well as network socket errors raised</li>
* <li>{@code w>1} Checks servers (w) for errors as well as network socket errors raised</li>
* </ul>
* </p>
* @param w number of writes
* @param wtimeout timeout for write operation
* @param fsync whether or not to fsync
* @param j whether writes should wait for a journaling group commit
* @param continueOnInsertError if batch inserts should continue after the first error
*/
public WriteConcern( int w , int wtimeout , boolean fsync , boolean j, boolean continueOnInsertError) {
_w = w;
_wtimeout = wtimeout;
_fsync = fsync;
_j = j;
_continueOnErrorForInsert = continueOnInsertError;
}
/**
* Creates a WriteConcern object.
* <p>Specifies the number of servers to wait for on the write operation, and exception raising behavior </p>
* <p> w represents the number of servers:
* <ul>
* <li>{@code w=-1} None, no checking is done</li>
* <li>{@code w=0} None, network socket errors raised</li>
* <li>{@code w=1} Checks server for errors as well as network socket errors raised</li>
* <li>{@code w>1} Checks servers (w) for errors as well as network socket errors raised</li>
* </ul>
* </p>
* @param w number of writes
* @param wtimeout timeout for write operation
* @param fsync whether or not to fsync
* @param j whether writes should wait for a journaling group commit
*/
public WriteConcern( String w , int wtimeout , boolean fsync, boolean j ){
this( w, wtimeout, fsync, j, false);
}
/**
* Creates a WriteConcern object.
* <p>Specifies the number of servers to wait for on the write operation, and exception raising behavior </p>
* <p> w represents the number of servers:
* <ul>
* <li>{@code w=-1} None, no checking is done</li>
* <li>{@code w=0} None, network socket errors raised</li>
* <li>{@code w=1} Checks server for errors as well as network socket errors raised</li>
* <li>{@code w>1} Checks servers (w) for errors as well as network socket errors raised</li>
* </ul>
* </p>
* @param w number of writes
* @param wtimeout timeout for write operation
* @param fsync whether or not to fsync
* @param j whether writes should wait for a journaling group commit
* @param continueOnInsertError if batch inserts should continue after the first error
* @return
*/
public WriteConcern( String w , int wtimeout , boolean fsync, boolean j, boolean continueOnInsertError ){
if (w == null) {
throw new IllegalArgumentException("w can not be null");
}
_w = w;
_wtimeout = wtimeout;
_fsync = fsync;
_j = j;
_continueOnErrorForInsert = continueOnInsertError;
}
public BasicDBObject getCommand(){
BasicDBObject _command = new BasicDBObject( "getlasterror" , 1 );
if ( _w instanceof Integer && ( (Integer) _w > 0) ||
( _w instanceof String && _w != null ) ){
_command.put( "w" , _w );
_command.put( "wtimeout" , _wtimeout );
}
if ( _fsync )
_command.put( "fsync" , true );
if ( _j )
_command.put( "j", true );
return _command;
}
/**
* Sets the w value (the write strategy)
* @param w
*/
public void setWObject(Object w) {
if ( ! (w instanceof Integer) && ! (w instanceof String) )
throw new IllegalArgumentException("The w parameter must be an int or a String");
this._w = w;
}
/**
* Gets the w value (the write strategy)
* @return
*/
public Object getWObject(){
return _w;
}
/**
* Gets the w parameter (the write strategy)
* @return
*/
public int getW(){
return (Integer) _w;
}
/**
* Gets the w parameter (the write strategy) in String format
* @return
*/
public String getWString(){
return _w.toString();
}
/**
* Gets the write timeout (in milliseconds)
* @return
*/
public int getWtimeout(){
return _wtimeout;
}
/**
* Gets the fsync flag (fsync to disk on the server)
* @return
*/
public boolean getFsync(){
return _fsync;
}
/**
* Gets the fsync flag (fsync to disk on the server)
* @return
*/
public boolean fsync(){
return _fsync;
}
/**
* Returns whether network error may be raised (w >= 0)
* @return
*/
public boolean raiseNetworkErrors(){
if (_w instanceof Integer)
return (Integer) _w >= 0;
return _w != null;
}
/**
* Returns whether "getlasterror" should be called (w > 0)
* @return
*/
public boolean callGetLastError(){
if (_w instanceof Integer)
return (Integer) _w > 0;
return _w != null;
}
/**
* Gets the WriteConcern constants by name: NONE, NORMAL, SAFE, FSYNC_SAFE,
* REPLICA_SAFE. (matching is done case insensitively)
* @param name
* @return
*/
public static WriteConcern valueOf(String name) {
if (_namedConcerns == null) {
HashMap<String, WriteConcern> newMap = new HashMap<String, WriteConcern>( 8 , 1 );
for (Field f : WriteConcern.class.getFields())
if (Modifier.isStatic( f.getModifiers() ) && f.getType().equals( WriteConcern.class )) {
try {
newMap.put( f.getName().toLowerCase(), (WriteConcern) f.get( null ) );
} catch (Exception e) {
throw new RuntimeException( e );
}
}
// Thought about doing a synchronize but this seems just as safe and
// I don't care about race conditions.
_namedConcerns = newMap;
}
return _namedConcerns.get( name.toLowerCase() );
}
@Override
public String toString(){
return "WriteConcern " + getCommand() + " / (Continue Inserting on Errors? " + getContinueOnErrorForInsert() + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WriteConcern that = (WriteConcern) o;
if (_continueOnErrorForInsert != that._continueOnErrorForInsert) return false;
if (_fsync != that._fsync) return false;
if (_j != that._j) return false;
if (_wtimeout != that._wtimeout) return false;
if (!_w.equals(that._w)) return false;
return true;
}
@Override
public int hashCode() {
int result = _w.hashCode();
result = 31 * result + _wtimeout;
result = 31 * result + (_fsync ? 1 : 0);
result = 31 * result + (_j ? 1 : 0);
result = 31 * result + (_continueOnErrorForInsert ? 1 : 0);
return result;
}
/**
* Gets the j parameter (journal syncing)
* @return
*/
public boolean getJ() {
return _j;
}
/**
* Toggles the "continue inserts on error" mode. This only applies to server side errors.
* If there is a document which does not validate in the client, an exception will still
* be thrown in the client.
* This will return a *NEW INSTANCE* of WriteConcern with your preferred continueOnInsert value
*
* @param continueOnErrorForInsert
*/
public WriteConcern continueOnErrorForInsert(boolean continueOnErrorForInsert) {
if ( _w instanceof Integer )
return new WriteConcern((Integer) _w, _wtimeout, _fsync, _j, continueOnErrorForInsert);
else if ( _w instanceof String )
return new WriteConcern((String) _w, _wtimeout, _fsync, _j, continueOnErrorForInsert);
else
throw new IllegalStateException("The w parameter must be an int or a String");
}
/**
* Gets the "continue inserts on error" mode
* @return
*/
public boolean getContinueOnErrorForInsert() {
return _continueOnErrorForInsert;
}
/**
* Create a Majority Write Concern that requires a majority of
* servers to acknowledge the write.
*
* @param wtimeout timeout for write operation
* @param fsync whether or not to fsync
* @param j whether writes should wait for a journaling group commit
*/
public static Majority majorityWriteConcern( int wtimeout, boolean fsync, boolean j ) {
return new Majority( wtimeout, fsync, j );
}
Object _w = 0;
int _wtimeout = 0;
boolean _fsync = false;
boolean _j = false;
boolean _continueOnErrorForInsert = false;
public static class Majority extends WriteConcern {
private static final long serialVersionUID = -4128295115883875212L;
public Majority( ) {
super( "majority", 0, false, false );
}
public Majority( int wtimeout, boolean fsync, boolean j ){
super( "majority", wtimeout, fsync, j );
}
@Override
public String toString(){
return "[Majority] WriteConcern " + getCommand();
}
}
}