blob: 333f1a98bfbc58b3cd9ebce7a4e312a4d0ad591f [file] [log] [blame]
/*
* Copyright (c) 2009, Rickard Öberg. All Rights Reserved.
*
* 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 org.apache.zest.migration;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import org.hamcrest.CoreMatchers;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.apache.zest.api.activation.ActivationException;
import org.apache.zest.api.service.importer.NewObjectImporter;
import org.apache.zest.api.unitofwork.UnitOfWork;
import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException;
import org.apache.zest.bootstrap.AssemblyException;
import org.apache.zest.bootstrap.ModuleAssembly;
import org.apache.zest.bootstrap.SingletonAssembler;
import org.apache.zest.io.Input;
import org.apache.zest.io.Output;
import org.apache.zest.io.Receiver;
import org.apache.zest.io.Sender;
import org.apache.zest.migration.assembly.EntityMigrationOperation;
import org.apache.zest.migration.assembly.MigrationBuilder;
import org.apache.zest.migration.assembly.MigrationOperation;
import org.apache.zest.spi.entitystore.BackupRestore;
import org.apache.zest.spi.entitystore.helpers.JSONKeys;
import org.apache.zest.spi.entitystore.helpers.StateStore;
import org.apache.zest.test.AbstractQi4jTest;
import org.apache.zest.test.EntityTestAssembler;
import static org.junit.Assert.assertThat;
/**
* JAVADOC
*/
public class MigrationTest
extends AbstractQi4jTest
{
@Override
public void assemble( ModuleAssembly module )
throws AssemblyException
{
new EntityTestAssembler().assemble( module );
module.objects( MigrationEventLogger.class );
module.importedServices( MigrationEventLogger.class ).importedBy( NewObjectImporter.class );
module.entities( TestEntity1_0.class,
TestEntity1_1.class,
TestEntity2_0.class,
org.apache.zest.migration.moved.TestEntity2_0.class );
MigrationBuilder migration = new MigrationBuilder( "1.0" );
migration.
toVersion( "1.1" ).
renameEntity( TestEntity1_0.class.getName(), TestEntity1_1.class.getName() ).
atStartup( new CustomFixOperation( "Fix for 1.1" ) ).
forEntities( TestEntity1_1.class.getName() ).
renameProperty( "foo", "newFoo" ).
renameManyAssociation( "fooManyAssoc", "newFooManyAssoc" ).
renameAssociation( "fooAssoc", "newFooAssoc" ).
end().
toVersion( "2.0" ).
renameEntity( TestEntity1_1.class.getName(), TestEntity2_0.class.getName() ).
atStartup( new CustomFixOperation( "Fix for 2.0, 1" ) ).
atStartup( new CustomFixOperation( "Fix for 2.0, 2" ) ).
forEntities( TestEntity2_0.class.getName() ).
addProperty( "bar", "Some value" ).
removeProperty( "newFoo", "Some value" ).
custom( new CustomBarOperation() ).
end().
toVersion( "3.0" ).
renamePackage( "org.qi4j.migration", "org.qi4j.migration.moved" ).
withEntities( "TestEntity2_0" ).
end();
module.services( MigrationService.class ).setMetaInfo( migration );
module.entities( MigrationConfiguration.class );
module.forMixin( MigrationConfiguration.class ).declareDefaults().lastStartupVersion().set( "1.0" );
}
@Test
public void testMigration()
throws UnitOfWorkCompletionException, IOException, ActivationException, AssemblyException
{
// Set up version 1
String id;
StringInputOutput data_v1 = new StringInputOutput();
{
SingletonAssembler v1 = new SingletonAssembler()
{
@Override
public void assemble( ModuleAssembly module )
throws AssemblyException
{
MigrationTest.this.assemble( module );
module.layer().application().setVersion( "1.0" );
}
};
UnitOfWork uow = v1.module().newUnitOfWork();
TestEntity1_0 entity = uow.newEntity( TestEntity1_0.class );
entity.foo().set( "Some value" );
entity.fooManyAssoc().add( entity );
entity.fooAssoc().set( entity );
id = entity.identity().get();
uow.complete();
BackupRestore backupRestore = v1.module()
.findService( BackupRestore.class )
.get();
backupRestore.backup().transferTo( data_v1 );
}
// Set up version 1.1
StringInputOutput data_v1_1 = new StringInputOutput();
{
SingletonAssembler v1_1 = new SingletonAssembler()
{
@Override
public void assemble( ModuleAssembly module )
throws AssemblyException
{
MigrationTest.this.assemble( module );
module.layer().application().setVersion( "1.1" );
}
};
BackupRestore testData = v1_1.module().findService( BackupRestore.class ).get();
data_v1.transferTo( testData.restore() );
UnitOfWork uow = v1_1.module().newUnitOfWork();
TestEntity1_1 entity = uow.get( TestEntity1_1.class, id );
assertThat( "Property has been renamed", entity.newFoo().get(), CoreMatchers.equalTo( "Some value" ) );
assertThat( "ManyAssociation has been renamed", entity.newFooManyAssoc().count(), CoreMatchers.equalTo( 1 ) );
assertThat( "Association has been renamed", entity.newFooAssoc().get(), CoreMatchers.equalTo( entity ) );
uow.complete();
testData.backup().transferTo( data_v1_1 );
}
// Set up version 2.0
{
SingletonAssembler v2_0 = new SingletonAssembler()
{
@Override
public void assemble( ModuleAssembly module )
throws AssemblyException
{
MigrationTest.this.assemble( module );
module.layer().application().setVersion( "2.0" );
}
};
BackupRestore testData = v2_0.module().findService( BackupRestore.class ).get();
// Test migration from 1.0 -> 2.0
{
data_v1.transferTo( testData.restore() );
UnitOfWork uow = v2_0.module().newUnitOfWork();
TestEntity2_0 entity = uow.get( TestEntity2_0.class, id );
assertThat( "Property has been created", entity.bar().get(), CoreMatchers.equalTo( "Some value" ) );
assertThat( "Custom Property has been created", entity.customBar().get(), CoreMatchers.equalTo( "Hello Some value" ) );
assertThat( "ManyAssociation has been renamed", entity.newFooManyAssoc().count(), CoreMatchers.equalTo( 1 ) );
assertThat( "Association has been renamed", entity.newFooAssoc().get(), CoreMatchers.equalTo( entity ) );
uow.complete();
}
}
// Set up version 3.0
{
SingletonAssembler v3_0 = new SingletonAssembler()
{
@Override
public void assemble( ModuleAssembly module )
throws AssemblyException
{
MigrationTest.this.assemble( module );
module.layer().application().setVersion( "3.0" );
}
};
BackupRestore testData = v3_0.module().findService( BackupRestore.class ).get();
data_v1_1.transferTo( testData.restore() );
// Test migration from 1.0 -> 3.0
{
data_v1.transferTo( testData.restore() );
UnitOfWork uow = v3_0.module().newUnitOfWork();
org.apache.zest.migration.moved.TestEntity2_0 entity = uow.get( org.apache.zest.migration.moved.TestEntity2_0.class, id );
uow.complete();
}
}
}
private static class CustomBarOperation
implements EntityMigrationOperation
{
@Override
public boolean upgrade( JSONObject state, StateStore stateStore, Migrator migrator )
throws JSONException
{
JSONObject properties = (JSONObject) state.get( JSONKeys.PROPERTIES );
return migrator.addProperty( state, "customBar", "Hello " + properties.getString( "bar" ) );
}
@Override
public boolean downgrade( JSONObject state, StateStore stateStore, Migrator migrator )
throws JSONException
{
return migrator.removeProperty( state, "customBar" );
}
}
private static class CustomFixOperation
implements MigrationOperation
{
String msg;
private CustomFixOperation( String msg )
{
this.msg = msg;
}
@Override
public void upgrade( StateStore stateStore, Migrator migrator )
throws IOException
{
System.out.println( msg );
}
@Override
public void downgrade( StateStore stateStore, Migrator migrator )
throws IOException
{
System.out.println( msg );
}
}
private static class StringInputOutput
implements Output<String, IOException>, Input<String, IOException>
{
final StringBuilder builder = new StringBuilder();
@Override
public <SenderThrowableType extends Throwable> void receiveFrom( Sender<? extends String, SenderThrowableType> sender )
throws IOException, SenderThrowableType
{
sender.sendTo( new Receiver<String, IOException>()
{
@Override
public void receive( String item )
throws IOException
{
builder.append( item ).append( "\n" );
}
} );
}
@Override
public <ReceiverThrowableType extends Throwable> void transferTo( Output<? super String, ReceiverThrowableType> output )
throws IOException, ReceiverThrowableType
{
output.receiveFrom( new Sender<String, IOException>()
{
@Override
public <ReceiverThrowableType extends Throwable> void sendTo( Receiver<? super String, ReceiverThrowableType> receiver )
throws ReceiverThrowableType, IOException
{
BufferedReader reader = new BufferedReader( new StringReader( builder.toString() ) );
String line;
try
{
while( ( line = reader.readLine() ) != null )
{
receiver.receive( line );
}
}
finally
{
reader.close();
}
}
} );
}
@Override
public String toString()
{
return builder.toString();
}
}
}