Java tutorial
/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.upgrade.test; import com.liferay.arquillian.extension.junit.bridge.junit.Arquillian; import com.liferay.petra.string.StringPool; import com.liferay.portal.kernel.dao.jdbc.DataAccess; import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream; import com.liferay.portal.kernel.model.Release; import com.liferay.portal.kernel.service.ReleaseLocalServiceUtil; import com.liferay.portal.kernel.service.persistence.ServiceComponentUtil; import com.liferay.portal.kernel.test.rule.AggregateTestRule; import com.liferay.portal.kernel.test.util.DBAssertionUtil; import com.liferay.portal.kernel.transaction.TransactionConfig; import com.liferay.portal.kernel.transaction.TransactionInvokerUtil; import com.liferay.portal.kernel.util.StreamUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.model.impl.BuildAutoUpgradeTestEntityModelImpl; import com.liferay.portal.test.log.CaptureAppender; import com.liferay.portal.test.log.Log4JLoggerTestUtil; import com.liferay.portal.test.rule.LiferayIntegrationTestRule; import com.liferay.portal.util.PropsValues; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.List; import java.util.Properties; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; /** * @author Preston Crary */ @RunWith(Arquillian.class) public abstract class BaseBuildAutoUpgradeTestCase { @ClassRule @Rule public static final AggregateTestRule aggregateTestRule = new LiferayIntegrationTestRule(); @Before public void setUp() throws Exception { try (Connection con = DataAccess.getUpgradeOptimizedConnection(); PreparedStatement ps = con.prepareStatement("drop table BuildAutoUpgradeTestEntity")) { ps.executeUpdate(); } catch (SQLException sqle) { } _previousSchemaModuleBuildAutoUpgrade = PropsValues.SCHEMA_MODULE_BUILD_AUTO_UPGRADE; PropsValues.SCHEMA_MODULE_BUILD_AUTO_UPGRADE = true; } @After public void tearDown() throws Throwable { PropsValues.SCHEMA_MODULE_BUILD_AUTO_UPGRADE = _previousSchemaModuleBuildAutoUpgrade; if (_bundle != null) { _bundle.uninstall(); } try (Connection con = DataAccess.getUpgradeOptimizedConnection(); PreparedStatement ps = con.prepareStatement("drop table BuildAutoUpgradeTestEntity")) { ps.executeUpdate(); } catch (SQLException sqle) { } Release release = ReleaseLocalServiceUtil.fetchRelease(BUNDLE_SYMBOLICNAME); if (release != null) { ReleaseLocalServiceUtil.deleteRelease(release); } TransactionConfig.Builder builder = new TransactionConfig.Builder(); TransactionInvokerUtil.invoke(builder.build(), () -> { ServiceComponentUtil.removeByBuildNamespace("BuildAutoUpgradeTest"); return null; }); } @Test public void testBuildAutoUpgrade() throws Exception { Bundle testBundle = FrameworkUtil.getBundle(BaseBuildAutoUpgradeTestCase.class); BundleContext bundleContext = testBundle.getBundleContext(); _bundle = bundleContext.installBundle(BaseBuildAutoUpgradeTestCase.class.getName(), getBundleInputStream(1)); _bundle.start(); try (Connection con = DataAccess.getUpgradeOptimizedConnection(); PreparedStatement ps = con .prepareStatement("insert into BuildAutoUpgradeTestEntity values (1, 'data')")) { Assert.assertEquals(1, ps.executeUpdate()); } // Initial columns DBAssertionUtil.assertColumns("BuildAutoUpgradeTestEntity", "id_", "data_"); // Add "data2" column _updateBundle(getBundleInputStream(2)); DBAssertionUtil.assertColumns("BuildAutoUpgradeTestEntity", "id_", "data_", "data2"); try (Connection con = DataAccess.getUpgradeOptimizedConnection(); PreparedStatement ps = con .prepareStatement("select id_, data_, data2 from BuildAutoUpgradeTestEntity"); ResultSet rs = ps.executeQuery()) { Assert.assertTrue(rs.next()); Assert.assertEquals(1, rs.getLong("id_")); Assert.assertEquals("data", rs.getString("data_")); String data2 = rs.getString("data2"); Assert.assertTrue(data2, Validator.isNull(data2)); Assert.assertFalse(rs.next()); } try (Connection con = DataAccess.getUpgradeOptimizedConnection(); PreparedStatement ps = con.prepareStatement( "update BuildAutoUpgradeTestEntity set data2 = 'data2' where " + "id_ = 1")) { Assert.assertEquals(1, ps.executeUpdate()); } // Remove "data_" column _updateBundle(getBundleInputStream(3)); DBAssertionUtil.assertColumns("BuildAutoUpgradeTestEntity", "id_", "data2"); try (Connection con = DataAccess.getUpgradeOptimizedConnection(); PreparedStatement ps = con.prepareStatement("select id_, data2 from BuildAutoUpgradeTestEntity"); ResultSet rs = ps.executeQuery()) { Assert.assertTrue(rs.next()); Assert.assertEquals(1, rs.getLong("id_")); Assert.assertEquals("data2", rs.getString("data2")); Assert.assertFalse(rs.next()); } // Remove "data2" column and add "data_" column _updateBundle(getBundleInputStream(4)); DBAssertionUtil.assertColumns("BuildAutoUpgradeTestEntity", "id_", "data_"); try (Connection con = DataAccess.getUpgradeOptimizedConnection(); PreparedStatement ps = con.prepareStatement("select id_, data_ from BuildAutoUpgradeTestEntity"); ResultSet rs = ps.executeQuery()) { Assert.assertTrue(rs.next()); Assert.assertEquals(1, rs.getLong("id_")); String data = rs.getString("data_"); Assert.assertTrue(data, Validator.isNull(data)); Assert.assertFalse(rs.next()); } } protected void addClass(String path, JarOutputStream jarOutputStream, Object[][] tableColumns, String createSQL) throws IOException { jarOutputStream.putNextEntry(new JarEntry(path)); ClassLoader classLoader = BaseBuildAutoUpgradeTestCase.class.getClassLoader(); try (InputStream inputStream = classLoader.getResourceAsStream(ENTITY_PATH)) { ClassReader classReader = new ClassReader(inputStream); ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5, classWriter) { @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { if ((createSQL != null) && name.equals("TABLE_SQL_CREATE")) { value = createSQL; } return super.visitField(access, name, desc, signature, value); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions); if (name.equals("<clinit>")) { _initTableColumns(methodVisitor, tableColumns); return null; } return methodVisitor; } }; classReader.accept(classVisitor, 0); jarOutputStream.write(classWriter.toByteArray()); } jarOutputStream.closeEntry(); } protected void addEmptyResource(String path, JarOutputStream jarOutputStream) throws IOException { jarOutputStream.putNextEntry(new JarEntry(path)); jarOutputStream.closeEntry(); } protected void addResource(String path, byte[] data, JarOutputStream jarOutputStream) throws IOException { jarOutputStream.putNextEntry(new JarEntry(path)); jarOutputStream.write(data); jarOutputStream.closeEntry(); } protected void addResource(String path, JarOutputStream jarOutputStream) throws IOException { addResource("dependencies/service/" + path, path, jarOutputStream); } protected void addResource(String resourcePath, String path, JarOutputStream jarOutputStream) throws IOException { jarOutputStream.putNextEntry(new JarEntry(path)); try (InputStream inputStream = BaseBuildAutoUpgradeTestCase.class.getResourceAsStream(resourcePath)) { StreamUtil.transfer(inputStream, jarOutputStream, false); } jarOutputStream.closeEntry(); } protected void addServiceProperties(int version, String path, JarOutputStream jarOutputStream) throws IOException { long time = System.currentTimeMillis(); Properties serviceProperties = new Properties(); serviceProperties.setProperty("build.namespace", "BuildAutoUpgradeTest"); serviceProperties.setProperty("build.number", String.valueOf(version)); serviceProperties.setProperty("build.date", String.valueOf(time + version)); try (UnsyncByteArrayOutputStream unsyncByteArrayOutputStream = new UnsyncByteArrayOutputStream()) { serviceProperties.store(unsyncByteArrayOutputStream, null); addResource(path, unsyncByteArrayOutputStream.toByteArray(), jarOutputStream); } } protected abstract InputStream getBundleInputStream(int version) throws IOException; protected String toCreateSQL(Object[][] tableColumns) { StringBundler sb = new StringBundler(tableColumns.length * 5 + 1); sb.append("create table BuildAutoUpgradeTestEntity ("); boolean first = true; for (Object[] tableColumn : tableColumns) { sb.append(tableColumn[0]); sb.append(StringPool.SPACE); int type = (Integer) tableColumn[1]; if (Types.BIGINT == type) { sb.append("LONG"); } else if (Types.VARCHAR == type) { sb.append("VARCHAR(75)"); } else { throw new IllegalArgumentException("Unknown data type " + type); } if (first) { first = false; sb.append(" not null primary key"); } else { sb.append(" null"); } sb.append(StringPool.COMMA); } sb.setStringAt(");", sb.index() - 1); return sb.toString(); } protected static final String BUNDLE_SYMBOLICNAME = "build.auto.upgrade.test"; protected static final String ENTITY_PATH; static { String path = BuildAutoUpgradeTestEntityModelImpl.class.getName(); path = path.replace('.', '/'); ENTITY_PATH = path.concat(".class"); } private String _assertAndGetFirstLogRecordMessage(CaptureAppender captureAppender) { List<LoggingEvent> loggingEvents = captureAppender.getLoggingEvents(); Assert.assertEquals(loggingEvents.toString(), 1, loggingEvents.size()); LoggingEvent loggingEvent = loggingEvents.get(0); return loggingEvent.getRenderedMessage(); } private void _initTableColumns(MethodVisitor methodVisitor, Object[][] tableColumns) { methodVisitor.visitCode(); methodVisitor.visitInsn(Opcodes.ICONST_0 + tableColumns.length); methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, Type.getDescriptor(Object[].class)); methodVisitor.visitInsn(Opcodes.DUP); for (int i = 0; i < tableColumns.length; i++) { Object[] tableColumn = tableColumns[i]; methodVisitor.visitInsn(Opcodes.ICONST_0 + i); methodVisitor.visitInsn(Opcodes.ICONST_2); methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class)); methodVisitor.visitInsn(Opcodes.DUP); methodVisitor.visitInsn(Opcodes.ICONST_0); methodVisitor.visitLdcInsn(tableColumn[0]); methodVisitor.visitInsn(Opcodes.AASTORE); methodVisitor.visitInsn(Opcodes.DUP); methodVisitor.visitInsn(Opcodes.ICONST_1); methodVisitor.visitIntInsn(Opcodes.BIPUSH, (Integer) tableColumn[1]); methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Integer.class), "valueOf", Type.getMethodDescriptor(Type.getType(Integer.class), Type.INT_TYPE), false); methodVisitor.visitInsn(Opcodes.AASTORE); methodVisitor.visitInsn(Opcodes.AASTORE); if (i < (tableColumns.length - 1)) { methodVisitor.visitInsn(Opcodes.DUP); } } methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, Type.getInternalName(BuildAutoUpgradeTestEntityModelImpl.class), "TABLE_COLUMNS", Type.getDescriptor(Object[][].class)); methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitMaxs(0, 0); methodVisitor.visitEnd(); } private void _updateBundle(InputStream inputStream) throws Exception { try (CaptureAppender serviceComponentCaptureHandler = Log4JLoggerTestUtil.configureLog4JLogger( "com.liferay.portal.service.impl." + "ServiceComponentLocalServiceImpl", Level.WARN); CaptureAppender baseDBCaptureHandler = Log4JLoggerTestUtil .configureLog4JLogger("com.liferay.portal.dao.db.BaseDB", Level.WARN)) { _bundle.update(inputStream); String message = _assertAndGetFirstLogRecordMessage(serviceComponentCaptureHandler); Assert.assertTrue(message, message.startsWith("Auto upgrading BuildAutoUpgradeTest")); message = _assertAndGetFirstLogRecordMessage(baseDBCaptureHandler); Assert.assertTrue(message, message.contains("create table BuildAutoUpgradeTestEntity")); } } private Bundle _bundle; private boolean _previousSchemaModuleBuildAutoUpgrade; }