From aca1633212fe261c04861cd48ab53b5081089fd2 Mon Sep 17 00:00:00 2001 From: lresende Date: Wed, 13 Jan 2010 01:41:50 +0000 Subject: Cleaning up sandbox git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@898604 13f79535-47bb-0310-9956-ffa450edef68 --- .../backup/organization-das/organization.sql | 33 ++++ sandbox/lresende/backup/organization-das/pom.xml | 203 +++++++++++++++++++++ .../organization/das/OrganizationDataService.java | 31 ++++ .../das/OrganizationDataServiceImpl.java | 89 +++++++++ .../organization/services/OrganizationService.java | 33 ++++ .../services/OrganizationServiceImpl.java | 53 ++++++ .../src/main/java/util/ConnectionHelper.java | 28 +++ .../main/resources/organization-spring-context.xml | 20 ++ .../src/main/resources/organization.composite | 22 +++ .../main/resources/organizationConfiguration.xml | 87 +++++++++ .../src/main/resources/sdo-types.xsd | 66 +++++++ .../java/organization/OrganizationTestCase.java | 141 ++++++++++++++ 12 files changed, 806 insertions(+) create mode 100644 sandbox/lresende/backup/organization-das/organization.sql create mode 100644 sandbox/lresende/backup/organization-das/pom.xml create mode 100644 sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataService.java create mode 100644 sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataServiceImpl.java create mode 100644 sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationService.java create mode 100644 sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationServiceImpl.java create mode 100644 sandbox/lresende/backup/organization-das/src/main/java/util/ConnectionHelper.java create mode 100644 sandbox/lresende/backup/organization-das/src/main/resources/organization-spring-context.xml create mode 100644 sandbox/lresende/backup/organization-das/src/main/resources/organization.composite create mode 100644 sandbox/lresende/backup/organization-das/src/main/resources/organizationConfiguration.xml create mode 100644 sandbox/lresende/backup/organization-das/src/main/resources/sdo-types.xsd create mode 100644 sandbox/lresende/backup/organization-das/src/test/java/organization/OrganizationTestCase.java (limited to 'sandbox/lresende/backup/organization-das') diff --git a/sandbox/lresende/backup/organization-das/organization.sql b/sandbox/lresende/backup/organization-das/organization.sql new file mode 100644 index 0000000000..3990fa3166 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/organization.sql @@ -0,0 +1,33 @@ +DROP TABLE ACCOUNT; +DROP TABLE ORGANIZATIONTYPELIST; + +CREATE TABLE ACCOUNT ( + ACCOUNT_ID INT PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY, + ACCOUNTTYPE_CD VARCHAR(30), + ACCOUNT_NAME VARCHAR(30), + WEB_SITE_ADDRESS VARCHAR(30), + STREET_ADDRESS_1 VARCHAR(30), + STREET_ADDRESS_2 VARCHAR(30), + STREET_ADDRESS_3 VARCHAR(30), + CITY VARCHAR(10), + STATE_CD VARCHAR(2), + POSTAL_CD VARCHAR(5), + COUNTRY_CD VARCHAR(3), + PHONE_NUMBER VARCHAR(15), + FAX_NUMBER VARCHAR(15), + NOTE VARCHAR(50)); + +CREATE TABLE ORGANIZATIONTYPELIST ( + ORGANIZATIONTYPEID INT PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY, + CODE VARCHAR(10), + DISPLAYVALUE VARCHAR(30), + DESCRIPTION VARCHAR(30), + ISEDITABLE INT); + + +INSERT INTO ACCOUNT (ACCOUNTTYPE_CD, ACCOUNT_NAME, WEB_SITE_ADDRESS, STREET_ADDRESS_1, STREET_ADDRESS_2, STREET_ADDRESS_3, CITY, STATE_CD, POSTAL_CD, COUNTRY_CD, PHONE_NUMBER, FAX_NUMBER, NOTE) VALUES ('ACC_TYPE_01', 'NAME_001', 'http://name_001.com', '1234 Street 1', '', '', 'San Jose', 'CA', '95134', 'USA', '', '', 'NAME 001 NOTES'); + + +INSERT INTO ORGANIZATIONTYPELIST (CODE, DISPLAYVALUE, DESCRIPTION, ISEDITABLE) VALUES('ACME', 'ACME Organization', 'ACME Description', 0 ); +INSERT INTO ORGANIZATIONTYPELIST (CODE, DISPLAYVALUE, DESCRIPTION, ISEDITABLE) VALUES('DO-RITE', 'DO-RITE Plumbing', 'DO-RITE Plumbing Description', 0 ); +INSERT INTO ORGANIZATIONTYPELIST (CODE, DISPLAYVALUE, DESCRIPTION, ISEDITABLE) VALUES('MEGACORP', 'MEGACORP Organization', 'MEGACORP Description', 0 ); diff --git a/sandbox/lresende/backup/organization-das/pom.xml b/sandbox/lresende/backup/organization-das/pom.xml new file mode 100644 index 0000000000..b39c125f25 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/pom.xml @@ -0,0 +1,203 @@ + + + + 4.0.0 + + org.apache.tuscany.sca + tuscany-sca + 1.4-SNAPSHOT + ../pom.xml + + + sample-organization-das + Apache Tuscany SCA Company Data Access Service Sample + jar + + + + + org.apache.tuscany.sca + tuscany-sca-api + 1.4-SNAPSHOT + + + + org.apache.tuscany.sca + tuscany-host-embedded + 1.4-SNAPSHOT + + + + org.apache.tuscany.sca + tuscany-implementation-java-runtime + 1.4-SNAPSHOT + runtime + + + + org.apache.tuscany.sca + tuscany-binding-ws-axis2 + 1.4-SNAPSHOT + runtime + + + + org.apache.tuscany.sca + tuscany-host-tomcat + 1.4-SNAPSHOT + runtime + + + + org.apache.tuscany.sca + tuscany-implementation-spring + 1.4-SNAPSHOT + runtime + + + + org.apache.tuscany.sca + tuscany-databinding-sdo + 1.4-SNAPSHOT + compile + + + + org.apache.tuscany.sdo + tuscany-sdo-impl + 1.1.1 + compile + + + + org.apache.tuscany.das + tuscany-das-rdb + 1.0-SNAPSHOT + compile + + + + org.apache.derby + derby + 10.1.2.1 + test + + + + junit + junit + 4.2 + test + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.0 + + + add-source + generate-sources + + add-test-source + + + + target/sdo-source + + + + + + + + org.apache.tuscany.sdo + tuscany-sdo-plugin + 1.1.1 + + + config + + ${basedir}/src/main/resources/sdo-types.xsd + true + + + generate + + + + + + + org.codehaus.mojo + sql-maven-plugin + + 1.1-SNAPSHOT + + + + org.apache.derby + derby + 10.1.2.1 + + + + + + create-db + generate-resources + + execute + + + org.apache.derby.jdbc.EmbeddedDriver + jdbc:derby:${pom.basedir}/target/organization_db;create=true + true + continue + skip + ; + + ${pom.basedir}/organization.sql + + + + + + + shutdown-database-sothat-test-can-run + process-test-resources + + execute + + + org.apache.derby.jdbc.EmbeddedDriver + jdbc:derby:${pom.basedir}/target/organization_db;shutdown=true + true + + + + + + + diff --git a/sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataService.java b/sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataService.java new file mode 100644 index 0000000000..af71558b14 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataService.java @@ -0,0 +1,31 @@ +/* + * 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 organization.das; + +import org.soa.types.SchemaInfoType; + +public interface OrganizationDataService { + + public commonj.sdo.DataObject findOrganizationBugTwo(java.lang.String id, SchemaInfoType schemaInfo); + + public commonj.sdo.DataObject findOrganizationBugOne(java.lang.String id, SchemaInfoType schemaInfo); + + public void updateOrganization(commonj.sdo.DataObject organization, SchemaInfoType schemaInfo); +} diff --git a/sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataServiceImpl.java b/sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataServiceImpl.java new file mode 100644 index 0000000000..2f99dc84f5 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/java/organization/das/OrganizationDataServiceImpl.java @@ -0,0 +1,89 @@ +/* + * 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 organization.das; + +import java.io.InputStream; + +import org.apache.tuscany.das.rdb.Command; +import org.apache.tuscany.das.rdb.DAS; +import org.soa.types.SchemaInfoType; + +import util.ConnectionHelper; + +import commonj.sdo.DataObject; + + +public class OrganizationDataServiceImpl implements OrganizationDataService { + + + public DataObject findOrganizationBugTwo(String id, SchemaInfoType schemaInfo) { + DataObject root = null; + InputStream in = getClass().getClassLoader().getResourceAsStream("organizationConfiguration.xml"); + + java.sql.Connection conn = ConnectionHelper.getConnection(schemaInfo); + + DAS das = DAS.FACTORY.createDAS(in, conn); + Command read = das.getCommand("getAccountByID"); + read.setParameter(1, Integer.valueOf(id)); + + root = read.executeQuery(); + + // Testing the root datagraph after returning to test runner. + + return root; + } + + public DataObject findOrganizationBugOne(String id, SchemaInfoType schemaInfo) { + DataObject root = null; + InputStream in = getClass().getClassLoader().getResourceAsStream("organizationConfiguration.xml"); + + java.sql.Connection conn = ConnectionHelper.getConnection(schemaInfo); + + DAS das = DAS.FACTORY.createDAS(in, conn); + Command read = das.getCommand("getAccountByID"); + read.setParameter(1, Integer.valueOf(id)); + + root = read.executeQuery(); + + // This is just testing to see if the call to das.applyChanges works + // This doesn't work + + DataObject account = root.getDataObject("ACCOUNT[1]"); + account.set("ACCOUNT_NAME", "TestNode2"); + System.out.println("findOrganization data graph -> " + root.getDataGraph()); + System.out.println("findOrganization data graph root -> " + root.getDataGraph().getRootObject()); + + das.applyChanges(root); + + return root; + } + + public void updateOrganization(DataObject organization, SchemaInfoType schemaInfo) { + InputStream in = getClass().getClassLoader().getResourceAsStream("organizationConfiguration.xml"); + + java.sql.Connection conn = ConnectionHelper.getConnection(schemaInfo); + + DAS das = DAS.FACTORY.createDAS(in, conn); + try { + das.applyChanges(organization); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationService.java b/sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationService.java new file mode 100644 index 0000000000..d821da47e5 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationService.java @@ -0,0 +1,33 @@ +/* + * 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 organization.services; + +import org.soa.types.SchemaInfoType; + +import commonj.sdo.DataObject; + +public interface OrganizationService { + + public DataObject findOrganizationBugTwo(String id, SchemaInfoType schemaInfo); + + public DataObject findOrganizationBugOne(String id, SchemaInfoType schemaInfo); + + public void updateOrganization(DataObject organization, SchemaInfoType schemaInfo); +} diff --git a/sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationServiceImpl.java b/sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationServiceImpl.java new file mode 100644 index 0000000000..fe977aff55 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/java/organization/services/OrganizationServiceImpl.java @@ -0,0 +1,53 @@ +/* + * 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 organization.services; + +import org.osoa.sca.annotations.Service; +import org.soa.types.SchemaInfoType; + +import organization.das.OrganizationDataService; + +import commonj.sdo.DataObject; + +@Service(OrganizationService.class) +public class OrganizationServiceImpl implements OrganizationService { + + private OrganizationDataService organizationDataService; + + public OrganizationDataService getOrganizationDataService() { + return organizationDataService; + } + + public void setOrganizationDataService(OrganizationDataService organizationDataService) { + this.organizationDataService = organizationDataService; + } + + public DataObject findOrganizationBugTwo(String id, SchemaInfoType schemaInfo) { + return organizationDataService.findOrganizationBugTwo(id, schemaInfo); + } + + public DataObject findOrganizationBugOne(String id, SchemaInfoType schemaInfo) { + return organizationDataService.findOrganizationBugOne(id, schemaInfo); + } + + public void updateOrganization(DataObject organization, SchemaInfoType schemaInfo) { + organizationDataService.updateOrganization(organization, schemaInfo); + } +} diff --git a/sandbox/lresende/backup/organization-das/src/main/java/util/ConnectionHelper.java b/sandbox/lresende/backup/organization-das/src/main/java/util/ConnectionHelper.java new file mode 100644 index 0000000000..43903b3a11 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/java/util/ConnectionHelper.java @@ -0,0 +1,28 @@ +package util; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.soa.types.SchemaInfoType; + +public class ConnectionHelper { + public static java.sql.Connection getConnection(SchemaInfoType schemaInfo) + { + Connection conn = null; + + // schemaInfo should be your SID. Something like xe (for Oracle Personal Edition) + String url = "jdbc:derby:target/organization_db; create = true"; + String driver = "org.apache.derby.jdbc.EmbeddedDriver"; + + try { + Class.forName(driver); + conn = DriverManager.getConnection(url, schemaInfo.getUSERNAME(), schemaInfo.getPASSWORD()); + conn.setAutoCommit(false); + } + catch (Exception e) { + e.printStackTrace(); + } + + return conn; + } +} diff --git a/sandbox/lresende/backup/organization-das/src/main/resources/organization-spring-context.xml b/sandbox/lresende/backup/organization-das/src/main/resources/organization-spring-context.xml new file mode 100644 index 0000000000..c814afe28b --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/resources/organization-spring-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/sandbox/lresende/backup/organization-das/src/main/resources/organization.composite b/sandbox/lresende/backup/organization-das/src/main/resources/organization.composite new file mode 100644 index 0000000000..0113ccf8a0 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/resources/organization.composite @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/lresende/backup/organization-das/src/main/resources/organizationConfiguration.xml b/sandbox/lresende/backup/organization-das/src/main/resources/organizationConfiguration.xml new file mode 100644 index 0000000000..2104c37404 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/resources/organizationConfiguration.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
\ No newline at end of file diff --git a/sandbox/lresende/backup/organization-das/src/main/resources/sdo-types.xsd b/sandbox/lresende/backup/organization-das/src/main/resources/sdo-types.xsd new file mode 100644 index 0000000000..0b287bda24 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/main/resources/sdo-types.xsd @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sandbox/lresende/backup/organization-das/src/test/java/organization/OrganizationTestCase.java b/sandbox/lresende/backup/organization-das/src/test/java/organization/OrganizationTestCase.java new file mode 100644 index 0000000000..33810f4325 --- /dev/null +++ b/sandbox/lresende/backup/organization-das/src/test/java/organization/OrganizationTestCase.java @@ -0,0 +1,141 @@ +/* + * 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 organization; + +import java.io.InputStream; +import java.util.Random; + +import org.apache.tuscany.das.rdb.Command; +import org.apache.tuscany.das.rdb.DAS; +import org.apache.tuscany.sca.host.embedded.SCADomain; +import org.junit.Test; +import org.soa.types.SchemaInfoType; +import org.soa.types.TypesFactory; +import organization.services.OrganizationService; + +import util.ConnectionHelper; + +import commonj.sdo.DataObject; + +public class OrganizationTestCase { + + private static final String id = "1"; + + /** + * This represents BUG_ONE from TUSCANY-2525 + */ + @Test + public void testRetrieveOrganizationDAS() { + DataObject root = null; + InputStream in = getClass().getClassLoader().getResourceAsStream("organizationConfiguration.xml"); + + // Need to put in valid values for your DB + SchemaInfoType schemaInfo = TypesFactory.INSTANCE.createSchemaInfoType(); + schemaInfo.setPASSWORD(""); + schemaInfo.setSCHEMA(""); + schemaInfo.setUSERNAME(""); + + java.sql.Connection conn = ConnectionHelper.getConnection(schemaInfo); + + DAS das = DAS.FACTORY.createDAS(in, conn); + Command read = das.getCommand("getAccountByID"); + read.setParameter(1, Integer.valueOf(id)); + + root = read.executeQuery(); + + // This is just testing to see if the call to das.applyChanges works + // This doesn't work + + Random generator = new Random(); + String accountName = "ACCOUNT_" + Integer.toString(generator.nextInt()); + + DataObject account = root.getDataObject("ACCOUNT[1]"); + account.set("ACCOUNT_NAME", accountName); + System.out.println("findOrganization data graph -> " + root.getDataGraph()); + System.out.println("findOrganization data graph root -> " + root.getDataGraph().getRootObject()); + + das.applyChanges(root); + + root = read.executeQuery(); + + DataObject updatedAccount = root.getDataObject("ACCOUNT[1]"); + org.junit.Assert.assertEquals(accountName, updatedAccount.get("ACCOUNT_NAME")); + + } + + @Test + public void testRetrieveOrganizationAndSaveDAS() { + DataObject root = null; + InputStream in = getClass().getClassLoader().getResourceAsStream("organizationConfiguration.xml"); + + // Need to put in valid values for your DB + SchemaInfoType schemaInfo = TypesFactory.INSTANCE.createSchemaInfoType(); + schemaInfo.setPASSWORD(""); + schemaInfo.setSCHEMA(""); + schemaInfo.setUSERNAME(""); + + java.sql.Connection conn = ConnectionHelper.getConnection(schemaInfo); + + DAS das = DAS.FACTORY.createDAS(in, conn); + Command read = das.getCommand("getAccountByID"); + read.setParameter(1, Integer.valueOf(id)); + + root = read.executeQuery(); + + das.applyChanges(root); + } + + /** + * This represents BUG_TWO from TUSCANY-2525 + */ + @Test + public void testRetrieveOrganizationSCA() { + + System.out.println("\n1. Organization-Service-Composite OrganizationClientRunner "); + + SCADomain scaDomain = SCADomain.newInstance("organization.composite"); + + OrganizationService organizationService = + scaDomain.getService(OrganizationService.class, "OrganizationServiceComponent"); + + // Need to put in valid values for your DB + SchemaInfoType schemaInfo = TypesFactory.INSTANCE.createSchemaInfoType(); + schemaInfo.setPASSWORD(""); + schemaInfo.setSCHEMA(""); + schemaInfo.setUSERNAME(""); + + // This doesn't work. Just retreive account, make a change, then apply changes + DataObject root = organizationService.findOrganizationBugOne(id, schemaInfo); + DataObject account = root.getDataObject("ACCOUNT[1]"); + + org.junit.Assert.assertEquals("TestNode2", account.get("ACCOUNT_NAME")); + + + // This doesn't work either. The root datagraph is null + // If you call das.applyChanges for a dataObject that has null as the dataGraph, + // it hurls. + DataObject rootOne = organizationService.findOrganizationBugTwo(id, schemaInfo); + ////account = root.getDataObject("ACCOUNT[1]"); + ////account.set("ACCOUNT_NAME", "something"); + System.out.println("rootOne dataGraph -> " + rootOne.getDataGraph()); + organizationService.updateOrganization(rootOne, schemaInfo); + + scaDomain.close(); + } +} -- cgit v1.2.3