[jOOQ/jOOQ#9586] Add MigrationListener SPI

This commit is contained in:
Lukas Eder 2019-11-21 23:27:50 +01:00
parent e51acdedc7
commit 8d01407974
13 changed files with 1008 additions and 54 deletions

View File

@ -55,6 +55,7 @@ import org.jooq.impl.DefaultConnectionProvider;
import org.jooq.impl.DefaultDiagnosticsListenerProvider;
import org.jooq.impl.DefaultExecuteListenerProvider;
import org.jooq.impl.DefaultExecutorProvider;
import org.jooq.impl.DefaultMigrationListenerProvider;
import org.jooq.impl.DefaultRecordListenerProvider;
import org.jooq.impl.DefaultRecordMapper;
import org.jooq.impl.DefaultRecordMapperProvider;
@ -405,6 +406,31 @@ public interface Configuration extends Serializable {
*/
ExecuteListenerProvider[] executeListenerProviders();
/**
* Get the configured <code>MigrationListenerProvider</code>s from this
* configuration.
* <p>
* This method allows for retrieving the configured
* <code>MigrationListenerProvider</code> from this configuration. The
* providers will provide jOOQ with {@link MigrationListener} instances.
* These instances receive migration lifecycle notification events every
* time jOOQ executes migrations. jOOQ makes no assumptions about the
* internal state of these listeners, i.e. listener instances may
* <ul>
* <li>share this <code>Configuration</code>'s lifecycle (i.e. that of a
* JDBC <code>Connection</code>, or that of a transaction)</li>
* <li>share the lifecycle of an <code>MigrationContext</code> (i.e. that of a
* single query execution)</li>
* <li>follow an entirely different lifecycle.</li>
* </ul>
*
* @return The configured set of migration listeners.
* @see MigrationListenerProvider
* @see MigrationListener
* @see MigrationContext
*/
MigrationListenerProvider[] migrationListenerProviders();
/**
* Get the configured <code>VisitListenerProvider</code> instances from this
* configuration.
@ -689,6 +715,33 @@ public interface Configuration extends Serializable {
*/
Configuration set(ExecuteListenerProvider... newExecuteListenerProviders);
/**
* Change this configuration to hold a new migration listeners.
* <p>
* This will wrap the argument {@link MigrationListener} in a
* {@link DefaultMigrationListenerProvider} for convenience.
* <p>
* This method is not thread-safe and should not be used in globally
* available <code>Configuration</code> objects.
*
* @param newMigrationListeners The new migration listeners to be contained
* in the changed configuration.
* @return The changed configuration.
*/
Configuration set(MigrationListener... newMigrationListeners);
/**
* Change this configuration to hold a new migration listener providers.
* <p>
* This method is not thread-safe and should not be used in globally
* available <code>Configuration</code> objects.
*
* @param newMigrationListenerProviders The new migration listener providers to
* be contained in the changed configuration.
* @return The changed configuration.
*/
Configuration set(MigrationListenerProvider... newMigrationListenerProviders);
/**
* Change this configuration to hold a new visit listeners.
* <p>
@ -1023,6 +1076,28 @@ public interface Configuration extends Serializable {
*/
Configuration derive(ExecuteListenerProvider... newExecuteListenerProviders);
/**
* Create a derived configuration from this one, with new migration listeners.
* <p>
* This will wrap the argument {@link MigrationListener} in a
* {@link DefaultMigrationListenerProvider} for convenience.
*
* @param newMigrationListeners The new migration listener to be contained in
* the derived configuration.
* @return The derived configuration.
*/
Configuration derive(MigrationListener... newMigrationListeners);
/**
* Create a derived configuration from this one, with new migration listener
* providers.
*
* @param newMigrationListenerProviders The new migration listener providers to
* be contained in the derived configuration.
* @return The derived configuration.
*/
Configuration derive(MigrationListenerProvider... newMigrationListenerProviders);
/**
* Create a derived configuration from this one, with new visit listeners.
* <p>

View File

@ -0,0 +1,129 @@
/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
/**
* The context in which a {@link Migration} is executed.
*
* @see MigrationListener
* @author Lukas Eder
*/
public interface MigrationContext extends Scope {
/**
* The {@link Version} from which a {@link Migration} has started.
* <p>
* {@link #migrationFrom()} and {@link #migrationTo()} versions need not be
* consecutive versions for any given migration. If a migration jumps a few
* versions, these two methods will only return the endpoints.
* <p>
* This is available on all {@link MigrationListener} events.
*/
Version migrationFrom();
/**
* The {@link Version} to which a {@link Migration} is headed.
* <p>
* {@link #migrationFrom()} and {@link #migrationTo()} versions need not be
* consecutive versions for any given migration. If a migration jumps a few
* versions, these two methods will only return the endpoints.
* <p>
* This is available on all {@link MigrationListener} events.
*/
Version migrationTo();
/**
* The complete set of {@link Queries} that are executed between
* {@link #migrationFrom()} and {@link #migrationTo()}.
* <p>
* This is available on all {@link MigrationListener} events.
*/
Queries migrationQueries();
/**
* The {@link Version} from which an individual set of {@link Queries} has
* started.
* <p>
* {@link #queriesFrom()} and {@link #queriesTo()} versions are consecutive
* versions in a migration. If a migration jumps a few versions, these two
* methods might return those intermediate versions on these events:
* <p>
* <ul>
* <li>{@link MigrationListener#queriesStart(MigrationContext)}</li>
* <li>{@link MigrationListener#queriesEnd(MigrationContext)}</li>
* <li>{@link MigrationListener#queryStart(MigrationContext)}</li>
* <li>{@link MigrationListener#queryEnd(MigrationContext)}</li>
* </ul>
*/
Version queriesFrom();
/**
* The {@link Version} to which an individual set of {@link Queries} is
* headed.
* <p>
* {@link #queriesFrom()} and {@link #queriesTo()} versions are consecutive
* versions in a migration. If a migration jumps a few versions, these two
* methods might return those intermediate versions on these events:
* <p>
* <ul>
* <li>{@link MigrationListener#queriesStart(MigrationContext)}</li>
* <li>{@link MigrationListener#queriesEnd(MigrationContext)}</li>
* <li>{@link MigrationListener#queryStart(MigrationContext)}</li>
* <li>{@link MigrationListener#queryEnd(MigrationContext)}</li>
* </ul>
*/
Version queriesTo();
/**
* The complete set of {@link Queries} that are executed between
* {@link #queriesFrom()} and {@link #queriesTo()}.
* <p>
* This is available on the same {@link MigrationListener} events as
* {@link #queriesFrom()} and {@link #queriesTo()}.
*/
Queries queries();
/**
* The current {@link Query} that is being executed.
* <p>
* This is available on
* {@link MigrationListener#queryStart(MigrationContext)} and
* {@link MigrationListener#queryEnd(MigrationContext)}.
*/
Query query();
}

View File

@ -0,0 +1,78 @@
/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
import java.util.EventListener;
/**
* A listener for {@link Migration} lifecycles.
*
* @author Lukas Eder
*/
public interface MigrationListener extends EventListener {
/**
* Invoked at the start of a {@link Migration}.
*/
void migrationStart(MigrationContext ctx);
/**
* Invoked at the end of a {@link Migration}.
*/
void migrationEnd(MigrationContext ctx);
/**
* Invoked at the start of a set of {@link Queries} that describe a single version increment.
*/
void queriesStart(MigrationContext ctx);
/**
* Invoked at the end of a set of {@link Queries} that describe a single version increment.
*/
void queriesEnd(MigrationContext ctx);
/**
* Invoked at the start of an individual {@link Query}.
*/
void queryStart(MigrationContext ctx);
/**
* Invoked at the start of an individual {@link Query}.
*/
void queryEnd(MigrationContext ctx);
}

View File

@ -0,0 +1,74 @@
/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
/**
* A provider for {@link MigrationListener} instances.
* <p>
* In order to facilitate the lifecycle management of
* <code>MigrationListener</code> instances that are provided to a jOOQ
* {@link Configuration}, clients can implement this API. To jOOQ, it is thus
* irrelevant, if migration listeners are stateful or stateless, local to an
* execution, or global to an application.
*
* @author Lukas Eder
* @see MigrationListener
* @see Configuration
*/
@FunctionalInterface
public interface MigrationListenerProvider {
/**
* Provide an <code>MigrationListener</code> instance.
* <p>
* Implementations are free to choose whether this method returns new
* instances at every call or whether the same instance is returned
* repetitively.
* <p>
* An <code>MigrationListener</code> shall be provided exactly once per {@link Migration}
* execution lifecycle, i.e. per <code>MigrationContext</code>.
*
* @return An <code>MigrationListener</code> instance.
* @see MigrationListener
* @see MigrationContext
* @see DefaultMigrationListenerProvider
*/
MigrationListener provide();
}

View File

@ -103,6 +103,12 @@ public class Settings
protected InvocationOrder transactionListenerEndInvocationOrder = InvocationOrder.DEFAULT;
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected InvocationOrder migrationListenerStartInvocationOrder = InvocationOrder.DEFAULT;
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected InvocationOrder migrationListenerEndInvocationOrder = InvocationOrder.DEFAULT;
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected InvocationOrder visitListenerStartInvocationOrder = InvocationOrder.DEFAULT;
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
@ -822,6 +828,38 @@ public class Settings
this.transactionListenerEndInvocationOrder = value;
}
/**
* The order of invocation for [action]start() methods registered {@link org.jooq.MigrationListener}s.
*
*/
public InvocationOrder getMigrationListenerStartInvocationOrder() {
return migrationListenerStartInvocationOrder;
}
/**
* The order of invocation for [action]start() methods registered {@link org.jooq.MigrationListener}s.
*
*/
public void setMigrationListenerStartInvocationOrder(InvocationOrder value) {
this.migrationListenerStartInvocationOrder = value;
}
/**
* The order of invocation for [action]end() methods registered {@link org.jooq.MigrationListener}s.
*
*/
public InvocationOrder getMigrationListenerEndInvocationOrder() {
return migrationListenerEndInvocationOrder;
}
/**
* The order of invocation for [action]end() methods registered {@link org.jooq.MigrationListener}s.
*
*/
public void setMigrationListenerEndInvocationOrder(InvocationOrder value) {
this.migrationListenerEndInvocationOrder = value;
}
/**
* The order of invocation for [action]start() methods registered {@link org.jooq.VisitListener}s.
*
@ -1946,6 +1984,24 @@ public class Settings
return this;
}
/**
* The order of invocation for [action]start() methods registered {@link org.jooq.MigrationListener}s.
*
*/
public Settings withMigrationListenerStartInvocationOrder(InvocationOrder value) {
setMigrationListenerStartInvocationOrder(value);
return this;
}
/**
* The order of invocation for [action]end() methods registered {@link org.jooq.MigrationListener}s.
*
*/
public Settings withMigrationListenerEndInvocationOrder(InvocationOrder value) {
setMigrationListenerEndInvocationOrder(value);
return this;
}
/**
* The order of invocation for [action]start() methods registered {@link org.jooq.VisitListener}s.
*
@ -2306,6 +2362,8 @@ public class Settings
builder.append("inlineThreshold", inlineThreshold);
builder.append("transactionListenerStartInvocationOrder", transactionListenerStartInvocationOrder);
builder.append("transactionListenerEndInvocationOrder", transactionListenerEndInvocationOrder);
builder.append("migrationListenerStartInvocationOrder", migrationListenerStartInvocationOrder);
builder.append("migrationListenerEndInvocationOrder", migrationListenerEndInvocationOrder);
builder.append("visitListenerStartInvocationOrder", visitListenerStartInvocationOrder);
builder.append("visitListenerEndInvocationOrder", visitListenerEndInvocationOrder);
builder.append("recordListenerStartInvocationOrder", recordListenerStartInvocationOrder);
@ -2605,6 +2663,24 @@ public class Settings
return false;
}
}
if (migrationListenerStartInvocationOrder == null) {
if (other.migrationListenerStartInvocationOrder!= null) {
return false;
}
} else {
if (!migrationListenerStartInvocationOrder.equals(other.migrationListenerStartInvocationOrder)) {
return false;
}
}
if (migrationListenerEndInvocationOrder == null) {
if (other.migrationListenerEndInvocationOrder!= null) {
return false;
}
} else {
if (!migrationListenerEndInvocationOrder.equals(other.migrationListenerEndInvocationOrder)) {
return false;
}
}
if (visitListenerStartInvocationOrder == null) {
if (other.visitListenerStartInvocationOrder!= null) {
return false;
@ -3034,6 +3110,8 @@ public class Settings
result = ((prime*result)+((inlineThreshold == null)? 0 :inlineThreshold.hashCode()));
result = ((prime*result)+((transactionListenerStartInvocationOrder == null)? 0 :transactionListenerStartInvocationOrder.hashCode()));
result = ((prime*result)+((transactionListenerEndInvocationOrder == null)? 0 :transactionListenerEndInvocationOrder.hashCode()));
result = ((prime*result)+((migrationListenerStartInvocationOrder == null)? 0 :migrationListenerStartInvocationOrder.hashCode()));
result = ((prime*result)+((migrationListenerEndInvocationOrder == null)? 0 :migrationListenerEndInvocationOrder.hashCode()));
result = ((prime*result)+((visitListenerStartInvocationOrder == null)? 0 :visitListenerStartInvocationOrder.hashCode()));
result = ((prime*result)+((visitListenerEndInvocationOrder == null)? 0 :visitListenerEndInvocationOrder.hashCode()));
result = ((prime*result)+((recordListenerStartInvocationOrder == null)? 0 :recordListenerStartInvocationOrder.hashCode()));

View File

@ -63,6 +63,8 @@ import org.jooq.ExecuteListener;
import org.jooq.ExecuteListenerProvider;
import org.jooq.ExecutorProvider;
import org.jooq.MetaProvider;
import org.jooq.MigrationListener;
import org.jooq.MigrationListenerProvider;
import org.jooq.Record;
import org.jooq.RecordListener;
import org.jooq.RecordListenerProvider;
@ -120,6 +122,7 @@ public class DefaultConfiguration implements Configuration {
private transient RecordUnmapperProvider recordUnmapperProvider;
private transient RecordListenerProvider[] recordListenerProviders;
private transient ExecuteListenerProvider[] executeListenerProviders;
private transient MigrationListenerProvider[] migrationListenerProviders;
private transient VisitListenerProvider[] visitListenerProviders;
private transient TransactionListenerProvider[] transactionListenerProviders;
private transient DiagnosticsListenerProvider[] diagnosticsListenerProviders;
@ -177,6 +180,7 @@ public class DefaultConfiguration implements Configuration {
null,
null,
null,
null,
null,
@ -207,6 +211,7 @@ public class DefaultConfiguration implements Configuration {
configuration.recordUnmapperProvider,
configuration.recordListenerProviders,
configuration.executeListenerProviders,
configuration.migrationListenerProviders,
configuration.visitListenerProviders,
configuration.transactionListenerProviders,
configuration.diagnosticsListenerProviders,
@ -241,6 +246,7 @@ public class DefaultConfiguration implements Configuration {
RecordUnmapperProvider recordUnmapperProvider,
RecordListenerProvider[] recordListenerProviders,
ExecuteListenerProvider[] executeListenerProviders,
MigrationListenerProvider[] migrationListenerProviders,
VisitListenerProvider[] visitListenerProviders,
TransactionListenerProvider[] transactionListenerProviders,
DiagnosticsListenerProvider[] diagnosticsListenerProviders,
@ -264,6 +270,7 @@ public class DefaultConfiguration implements Configuration {
set(recordUnmapperProvider);
set(recordListenerProviders);
set(executeListenerProviders);
set(migrationListenerProviders);
set(visitListenerProviders);
set(transactionListenerProviders);
set(diagnosticsListenerProviders);
@ -322,6 +329,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -350,6 +358,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -378,6 +387,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -411,6 +421,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -439,6 +450,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -472,6 +484,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -505,6 +518,7 @@ public class DefaultConfiguration implements Configuration {
newRecordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -538,6 +552,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
newRecordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -571,6 +586,41 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
newExecuteListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
unwrapperProvider,
converterProvider,
clock,
dialect,
settings,
data
);
}
@Override
public final Configuration derive(MigrationListener... newMigrationListeners) {
return derive(DefaultMigrationListenerProvider.providers(newMigrationListeners));
}
@Override
public final Configuration derive(MigrationListenerProvider... newMigrationListenerProviders) {
return new DefaultConfiguration(
connectionProvider,
interpreterConnectionProvider,
systemConnectionProvider,
metaProvider,
versionProvider,
executorProvider,
transactionProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
newMigrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -604,6 +654,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
newVisitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -637,6 +688,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
newTransactionListenerProviders,
diagnosticsListenerProviders,
@ -670,6 +722,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
newDiagnosticsListenerProviders,
@ -703,6 +756,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -731,6 +785,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -760,6 +815,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -787,6 +843,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -815,6 +872,7 @@ public class DefaultConfiguration implements Configuration {
recordUnmapperProvider,
recordListenerProviders,
executeListenerProviders,
migrationListenerProviders,
visitListenerProviders,
transactionListenerProviders,
diagnosticsListenerProviders,
@ -949,6 +1007,20 @@ public class DefaultConfiguration implements Configuration {
return this;
}
@Override
public final Configuration set(MigrationListener... newMigrationListeners) {
return set(DefaultMigrationListenerProvider.providers(newMigrationListeners));
}
@Override
public final Configuration set(MigrationListenerProvider... newMigrationListenerProviders) {
this.migrationListenerProviders = newMigrationListenerProviders != null
? newMigrationListenerProviders
: new MigrationListenerProvider[0];
return this;
}
@Override
public final Configuration set(VisitListener... newVisitListeners) {
return set(DefaultVisitListenerProvider.providers(newVisitListeners));
@ -1348,6 +1420,11 @@ public class DefaultConfiguration implements Configuration {
return executeListenerProviders;
}
@Override
public final MigrationListenerProvider[] migrationListenerProviders() {
return migrationListenerProviders;
}
@Override
public final VisitListenerProvider[] visitListenerProviders() {
return visitListenerProviders;

View File

@ -0,0 +1,105 @@
/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import org.jooq.Configuration;
import org.jooq.MigrationContext;
import org.jooq.Queries;
import org.jooq.Query;
import org.jooq.Version;
/**
* A default implementation for {@link MigrationContext}.
*
* @author Lukas Eder
*/
final class DefaultMigrationContext extends AbstractScope implements MigrationContext {
private final Version migrationFrom;
private final Version migrationTo;
private final Queries migrationQueries;
private Query query;
DefaultMigrationContext(Configuration configuration, Version migrationFrom, Version migrationTo, Queries migrationQueries) {
super(configuration);
this.migrationFrom = migrationFrom;
this.migrationTo = migrationTo;
this.migrationQueries = migrationQueries;
}
@Override
public Version migrationFrom() {
return migrationFrom;
}
@Override
public Version migrationTo() {
return migrationTo;
}
@Override
public Queries migrationQueries() {
return migrationQueries;
}
@Override
public Version queriesFrom() {
return migrationFrom;
}
@Override
public Version queriesTo() {
return migrationTo;
}
@Override
public Queries queries() {
return migrationQueries;
}
@Override
public Query query() {
return query;
}
void query(Query q) {
this.query = q;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import org.jooq.MigrationContext;
import org.jooq.MigrationListener;
/**
* A publicly available default implementation of {@link MigrationListener}.
* <p>
* Use this to stay compatible with future API changes (i.e. added methods to
* <code>MigrationListener</code>)
*
* @author Lukas Eder
*/
public class DefaultMigrationListener implements MigrationListener {
@Override
public void migrationStart(MigrationContext ctx) {}
@Override
public void migrationEnd(MigrationContext ctx) {}
@Override
public void queriesStart(MigrationContext ctx) {}
@Override
public void queriesEnd(MigrationContext ctx) { }
@Override
public void queryStart(MigrationContext ctx) {}
@Override
public void queryEnd(MigrationContext ctx) {}
}

View File

@ -0,0 +1,97 @@
/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import java.io.Serializable;
import org.jooq.MigrationListener;
import org.jooq.MigrationListenerProvider;
/**
* A default implementation for {@link MigrationListenerProvider}.
* <p>
* This implementation just wraps an instance of {@link MigrationListener}, always
* providing the same.
*
* @author Lukas Eder
*/
public class DefaultMigrationListenerProvider implements MigrationListenerProvider, Serializable {
/**
* Generated UID.
*/
private static final long serialVersionUID = -2122007794302549679L;
/**
* The delegate listener.
*/
private final MigrationListener listener;
/**
* Convenience method to construct an array of
* <code>DefaultMigrationListenerProvider</code> from an array of
* <code>MigrationListener</code> instances.
*/
public static MigrationListenerProvider[] providers(MigrationListener... listeners) {
MigrationListenerProvider[] result = new MigrationListenerProvider[listeners.length];
for (int i = 0; i < listeners.length; i++)
result[i] = new DefaultMigrationListenerProvider(listeners[i]);
return result;
}
/**
* Create a new provider instance from an argument listener.
*
* @param listener The argument listener.
*/
public DefaultMigrationListenerProvider(MigrationListener listener) {
this.listener = listener;
}
@Override
public final MigrationListener provide() {
return listener;
}
@Override
public String toString() {
return listener.toString();
}
}

View File

@ -49,6 +49,7 @@ import org.jooq.ContextTransactionalCallable;
import org.jooq.Field;
import org.jooq.Identity;
import org.jooq.Migration;
import org.jooq.MigrationListener;
import org.jooq.MigrationResult;
import org.jooq.Name;
import org.jooq.Queries;
@ -135,63 +136,86 @@ final class MigrationImpl extends AbstractScope implements Migration {
return run(new ContextTransactionalCallable<MigrationResult>() {
@Override
public MigrationResult run() {
if (from().equals(to())) {
log.info("jOOQ Migrations", "Version " + to().id() + " is already installed as the current version.");
DefaultMigrationContext ctx = new DefaultMigrationContext(configuration(), from(), to(), queries());
MigrationListener listener = new MigrationListeners(configuration);
try {
listener.migrationStart(ctx);
if (from().equals(to())) {
log.info("jOOQ Migrations", "Version " + to().id() + " is already installed as the current version.");
return MIGRATION_RESULT;
}
// TODO: Implement preconditions
// TODO: Implement a listener with a variety of pro / oss features
// TODO: Implement additional out-of-the-box sanity checks
// TODO: Allow undo migrations only if enabled explicitly
// TODO: Add some migration settings, e.g. whether CHANGELOG.SQL should be filled
// TODO: Migrate the CHANGELOG table with the Migration API
// TODO: Create an Enum for CHANGELOG.STATUS
log.info("jOOQ Migrations", "Version " + from().id() + " is migrated to " + to().id());
StopWatch watch = new StopWatch();
// TODO: Make logging configurable
if (log.isDebugEnabled())
for (Query query : queries())
log.debug("jOOQ Migrations", dsl().renderInlined(query));
JooqMigrationsChangelogRecord newRecord = dsl().newRecord(CHANGELOG);
newRecord
.setJooqVersion(Constants.VERSION)
.setMigratedAt(new Timestamp(System.currentTimeMillis()))
.setMigratedFrom(from().id())
.setMigratedTo(to().id())
.setMigrationTime(0L)
.setSql(queries().toString())
.setSqlCount(queries().queries().length)
.setStatus("PENDING")
.insert();
try {
// TODO: Can we access the individual Queries from Version, if applicable?
// TODO: Set the ctx.queriesFrom(), ctx.queriesTo(), and ctx.queries() values
listener.queriesStart(ctx);
// TODO: Make batching an option: queries().executeBatch();
for (Query query : queries().queries()) {
ctx.query(query);
listener.queryStart(ctx);
query.execute();
listener.queryEnd(ctx);
ctx.query(null);
}
listener.queriesEnd(ctx);
newRecord
.setMigrationTime(watch.split() / 1000000L)
.setStatus("SUCCESS")
.update();
}
catch (DataAccessException e) {
// TODO: Make sure this is committed, given that we're re-throwing the exception.
// TODO: How can we recover from failure?
newRecord
.setMigrationTime(watch.split() / 1000000L)
.setStatus("FAILURE")
.update();
throw e;
}
return MIGRATION_RESULT;
}
// TODO: Implement preconditions
// TODO: Implement a listener with a variety of pro / oss features
// TODO: Implement additional out-of-the-box sanity checks
// TODO: Allow undo migrations only if enabled explicitly
// TODO: Add some migration settings, e.g. whether CHANGELOG.SQL should be filled
// TODO: Migrate the CHANGELOG table with the Migration API
// TODO: Create an Enum for CHANGELOG.STATUS
log.info("jOOQ Migrations", "Version " + from().id() + " is migrated to " + to().id());
StopWatch watch = new StopWatch();
// TODO: Make logging configurable
if (log.isDebugEnabled())
for (Query query : queries())
log.debug("jOOQ Migrations", dsl().renderInlined(query));
JooqMigrationsChangelogRecord newRecord = dsl().newRecord(CHANGELOG);
newRecord
.setJooqVersion(Constants.VERSION)
.setMigratedAt(new Timestamp(System.currentTimeMillis()))
.setMigratedFrom(from().id())
.setMigratedTo(to().id())
.setMigrationTime(0L)
.setSql(queries().toString())
.setSqlCount(queries().queries().length)
.setStatus("PENDING")
.insert();
// TODO: Make batching an option
try {
queries().executeBatch();
newRecord
.setMigrationTime(watch.split() / 1000000L)
.setStatus("SUCCESS")
.update();
finally {
listener.migrationEnd(ctx);
}
catch (DataAccessException e) {
// TODO: Make sure this is committed, given that we're re-throwing the exception.
// TODO: How can we recover from failure?
newRecord
.setMigrationTime(watch.split() / 1000000L)
.setStatus("FAILURE")
.update();
throw e;
}
return MIGRATION_RESULT;
}
});
}

View File

@ -0,0 +1,112 @@
/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import static org.jooq.conf.InvocationOrder.REVERSE;
import java.util.Arrays;
import org.jooq.Configuration;
import org.jooq.MigrationContext;
import org.jooq.MigrationListener;
import org.jooq.MigrationListenerProvider;
/**
* @author Lukas Eder
*/
class MigrationListeners implements MigrationListener {
private final MigrationListener[] listeners;
MigrationListeners(Configuration configuration) {
MigrationListenerProvider[] providers = configuration.migrationListenerProviders();
listeners = new MigrationListener[providers.length];
for (int i = 0; i < providers.length; i++)
listeners[i] = providers[i].provide();
}
@Override
public final void migrationStart(MigrationContext ctx) {
for (MigrationListener listener : ctx.settings().getMigrationListenerStartInvocationOrder() != REVERSE
? Arrays.asList(listeners)
: Tools.reverseIterable(listeners))
listener.migrationStart(ctx);
}
@Override
public final void migrationEnd(MigrationContext ctx) {
for (MigrationListener listener : ctx.settings().getMigrationListenerEndInvocationOrder() != REVERSE
? Arrays.asList(listeners)
: Tools.reverseIterable(listeners))
listener.migrationEnd(ctx);
}
@Override
public final void queriesStart(MigrationContext ctx) {
for (MigrationListener listener : ctx.settings().getMigrationListenerStartInvocationOrder() != REVERSE
? Arrays.asList(listeners)
: Tools.reverseIterable(listeners))
listener.queriesStart(ctx);
}
@Override
public final void queriesEnd(MigrationContext ctx) {
for (MigrationListener listener : ctx.settings().getMigrationListenerEndInvocationOrder() != REVERSE
? Arrays.asList(listeners)
: Tools.reverseIterable(listeners))
listener.queriesEnd(ctx);
}
@Override
public final void queryStart(MigrationContext ctx) {
for (MigrationListener listener : ctx.settings().getMigrationListenerStartInvocationOrder() != REVERSE
? Arrays.asList(listeners)
: Tools.reverseIterable(listeners))
listener.queryStart(ctx);
}
@Override
public final void queryEnd(MigrationContext ctx) {
for (MigrationListener listener : ctx.settings().getMigrationListenerEndInvocationOrder() != REVERSE
? Arrays.asList(listeners)
: Tools.reverseIterable(listeners))
listener.queryEnd(ctx);
}
}

View File

@ -54,6 +54,8 @@ import org.jooq.ExecuteListener;
import org.jooq.ExecuteListenerProvider;
import org.jooq.ExecutorProvider;
import org.jooq.MetaProvider;
import org.jooq.MigrationListener;
import org.jooq.MigrationListenerProvider;
import org.jooq.RecordListener;
import org.jooq.RecordListenerProvider;
import org.jooq.RecordMapper;
@ -178,6 +180,11 @@ public class MockConfiguration implements Configuration {
return delegate.executeListenerProviders();
}
@Override
public MigrationListenerProvider[] migrationListenerProviders() {
return delegate.migrationListenerProviders();
}
@Override
public VisitListenerProvider[] visitListenerProviders() {
return delegate.visitListenerProviders();
@ -310,6 +317,16 @@ public class MockConfiguration implements Configuration {
return delegate.set(newExecuteListenerProviders);
}
@Override
public Configuration set(MigrationListener... newMigrationListeners) {
return delegate.set(newMigrationListeners);
}
@Override
public Configuration set(MigrationListenerProvider... newMigrationListenerProviders) {
return delegate.set(newMigrationListenerProviders);
}
@Override
public Configuration set(VisitListener... newVisitListeners) {
return delegate.set(newVisitListeners);
@ -457,6 +474,16 @@ public class MockConfiguration implements Configuration {
return delegate.derive(newExecuteListenerProviders);
}
@Override
public Configuration derive(MigrationListener... newMigrationListeners) {
return delegate.derive(newMigrationListeners);
}
@Override
public Configuration derive(MigrationListenerProvider... newMigrationListenerProviders) {
return delegate.derive(newMigrationListenerProviders);
}
@Override
public Configuration derive(VisitListener... newVisitListeners) {
return delegate.derive(newVisitListeners);

View File

@ -224,6 +224,14 @@ case of which, this defaults to INLINED]]></jxb:javadoc></jxb:property></appinfo
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The order of invocation for [action]end() methods registered {@link org.jooq.TransactionListener}s.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="migrationListenerStartInvocationOrder" type="jooq-runtime:InvocationOrder" minOccurs="0" maxOccurs="1" default="DEFAULT">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The order of invocation for [action]start() methods registered {@link org.jooq.MigrationListener}s.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="migrationListenerEndInvocationOrder" type="jooq-runtime:InvocationOrder" minOccurs="0" maxOccurs="1" default="DEFAULT">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The order of invocation for [action]end() methods registered {@link org.jooq.MigrationListener}s.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="visitListenerStartInvocationOrder" type="jooq-runtime:InvocationOrder" minOccurs="0" maxOccurs="1" default="DEFAULT">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The order of invocation for [action]start() methods registered {@link org.jooq.VisitListener}s.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>