package com.cedarsoftware.ncube;
import com.cedarsoftware.util.StringUtilities;
import com.cedarsoftware.util.UniqueIdGenerator;
import com.cedarsoftware.util.io.JsonObject;
import com.cedarsoftware.util.io.JsonReader;
import com.cedarsoftware.util.io.JsonWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class manages a list of NCubes. This class is referenced
* by NCube in one place - when it joins to other cubes, it consults
* the NCubeManager to find the joined NCube.
*
* This class takes care of creating, loading, updating, releasing,
* and deleting NCubes. It also allows you to get a list of NCubes
* matching a wildcard (SQL Like) string.
*
* @author John DeRegnaucourt (jdereg@gmail.com)
* <br/>
* Copyright (c) Cedar Software LLC
* <br/><br/>
* 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
* <br/><br/>
* http://www.apache.org/licenses/LICENSE-2.0
* <br/><br/>
* 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.
*/
public class NCubeManager
{
private final Map<String, NCube> cubeList = new ConcurrentHashMap<String, NCube>();
private static final Log LOG = LogFactory.getLog(NCubeManager.class);
private static final Pattern validCubeName = Pattern.compile("[0-9a-zA-Z:.|#_-]+");
private static final Pattern validVersion = Pattern.compile("^(\\d+\\.)(\\d+\\.)(\\*|\\d+)$");
public static NCubeManager getInstance()
{
return new NCubeManager();
}
/**
* @param name String name of an NCube.
* @return NCube instance with the given name. Please note
* that the cube must be loaded first before calling this.
*/
public NCube getCube(String name)
{
return cubeList.get(name);
}
/**
* Add a cube to the internal map of available cubes.
* @param ncube NCube to add to the list.
*/
public void addCube(NCube ncube)
{
synchronized(cubeList)
{
cubeList.put(ncube.getName(), ncube);
ncube.setManager(this);
}
}
/**
* @return Map<String, NCube> of all NCubes that are currently
* loaded (cached) in memory. A copy of the internal cache
* is returned.
*/
public Map<String, NCube> getCachedNCubes()
{
synchronized (cubeList)
{ return new TreeMap<String, NCube>(cubeList); }
}
/**
* Used for testing.
*/
public void clearCubeList()
{
synchronized(cubeList)
{
cubeList.clear();
}
}
private static void jdbcCleanup(PreparedStatement stmt)
{
if (stmt != null)
{
try
{
stmt.close();
}
catch (SQLException e) { LOG.error("Error closing JDBC Statement", e); }
}
}
private static void validateConnection(Connection c)
{
if (c == null)
{
throw new IllegalArgumentException("Connection cannot be null");
}
}
public static void validateApp(String app)
{
if (StringUtilities.isEmpty(app))
{
throw new IllegalArgumentException("App cannot be null or empty");
}
}
public static void validateCubeName(String cubeName)
{
if (StringUtilities.isEmpty(cubeName))
{
throw new IllegalArgumentException("n-cube name cannot be null or empty");
}
Matcher m = validCubeName.matcher(cubeName);
if (m.find())
{
if (cubeName.equals(m.group(0)))
{
return;
}
}
throw new IllegalArgumentException("n-cube name can only contain a-z, A-Z, 0-9, :, ., _, -, #, and |");
}
public static void validateVersion(String version)
{
if (StringUtilities.isEmpty(version))
{
throw new IllegalArgumentException("n-cube version cannot be null or empty");
}
Matcher m = validVersion.matcher(version);
if (m.find())
{
return;
}
throw new IllegalArgumentException("n-cube version must follow the form n.n.n where n is a number 0 or greater. The numbers stand for major.minor.revision");
}
public static void validateStatus(String status)
{
if (StringUtilities.isEmpty(status))
{
throw new IllegalArgumentException("n-cube status cannot be null or empty");
}
if (status.equals("RELEASE") || status.equals("SNAPSHOT"))
{
return;
}
throw new IllegalArgumentException("n-cube status must be RELEASE or SNAPSHOT");
}
/**
* Load an NCube from the database (any joined sub-cubes will also be loaded).
* @return NCube that matches, or null if not found.
*/
public NCube loadCube(Connection connection, String app, String name, String version, String status, Date sysDate)
{
validateConnection(connection);
validateApp(app);
validateCubeName(name);
validateStatus(status);
if (sysDate == null)
{
sysDate = new Date();
}
synchronized(cubeList)
{
PreparedStatement stmt = null;
try
{
java.sql.Date systemDate = new java.sql.Date(sysDate.getTime());
stmt = connection.prepareStatement("SELECT cube_value_bin FROM n_cube WHERE n_cube_nm = ? AND app_cd = ? AND sys_effective_dt <= ? AND (sys_expiration_dt IS NULL OR sys_expiration_dt >= ?) AND version_no_cd = ? AND status_cd = ?");
stmt.setString(1, name);
stmt.setString(2, app);
stmt.setDate(3, systemDate);
stmt.setDate(4, systemDate);
stmt.setString(5, version);
stmt.setString(6, status);
ResultSet rs = stmt.executeQuery();
if (rs.next())
{
byte[] jsonBytes = rs.getBytes("cube_value_bin");
String json = new String(jsonBytes, "UTF-8");
NCube<Object> ncube = (NCube) JsonReader.jsonToJava(json);
for (Axis axis : ncube.getAxes())
{
axis.buildScaffolding();
}
if (rs.next())
{
throw new IllegalStateException("More than one NCube matching name: " + ncube.getName() + ", app: " + app + ", version: " + version + ", status: " + status + ", sysDate: " + sysDate);
}
cubeList.put(ncube.getName(), ncube);
Set<String> subCubeList = ncube.getReferencedCubeNames();
for (String cubeName : subCubeList)
{
if (!cubeList.containsKey(cubeName))
{
loadCube(connection, app, cubeName, version, status, sysDate);
}
}
ncube.setManager(this);
return ncube;
}
return null; // Indicates not found
}
catch (IllegalStateException e) { throw e; }
catch (Exception e)
{
String s = "Unable to load nNCube: " + name + ", app: " + app + ", version: " + version + ", status: " + status + ", sysDate: " + sysDate + " from database";
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
}
/**
* Retrieve all cube names that are deeply referenced by the named app, cube (name), version, and status.
*/
public void getReferencedCubeNames(Connection connection, String app, String name, String version, String status, Date sysDate, Set<String> refs)
{
validateConnection(connection);
validateApp(app);
validateCubeName(name);
validateVersion(version);
validateStatus(status);
if (sysDate == null)
{
sysDate = new Date();
}
if (refs == null)
{
throw new IllegalArgumentException("null passed in for Set to hold referenced n-cube names");
}
PreparedStatement stmt = null;
try
{
java.sql.Date systemDate = new java.sql.Date(sysDate.getTime());
stmt = connection.prepareStatement("SELECT cube_value_bin FROM n_cube WHERE n_cube_nm = ? AND app_cd = ? AND sys_effective_dt <= ? AND (sys_expiration_dt IS NULL OR sys_expiration_dt >= ?) AND version_no_cd = ? AND status_cd = ?");
stmt.setString(1, name);
stmt.setString(2, app);
stmt.setDate(3, systemDate);
stmt.setDate(4, systemDate);
stmt.setString(5, version);
stmt.setString(6, status);
ResultSet rs = stmt.executeQuery();
if (rs.next())
{
byte[] jsonBytes = rs.getBytes("cube_value_bin");
String json = new String(jsonBytes, "UTF-8");
NCube<Object> ncube = (NCube) JsonReader.jsonToJava(json);
for (Axis axis : ncube.getAxes())
{
axis.buildScaffolding();
}
if (rs.next())
{
throw new IllegalStateException("More than one NCube matching name: " + ncube.getName() + ", app: " + app + ", version: " + version + ", status: " + status + ", sysDate: " + sysDate);
}
Set<String> subCubeList = ncube.getReferencedCubeNames();
refs.addAll(subCubeList);
for (String cubeName : subCubeList)
{
if (!refs.contains(cubeName))
{
getReferencedCubeNames(connection, app, cubeName, version, status, sysDate, refs);
}
}
}
}
catch (IllegalStateException e) { throw e; }
catch (Exception e)
{
String s = "Unable to load nNCube: " + name + ", app: " + app + ", version: " + version + ", status: " + status + ", sysDate: " + sysDate + " from database";
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
/**
* Retrieve all n-cubes that have a name that matches the SQL like statement, within the specified app, status,
* version, and system date.
*/
public Object[] getNCubes(Connection connection, String app, String version, String status, String sqlLike, Date sysDate)
{
validateConnection(connection);
validateApp(app);
validateVersion(version);
validateStatus(status);
if (sqlLike == null)
{
sqlLike = "%";
}
if (sysDate == null)
{
sysDate = new Date();
}
PreparedStatement stmt = null;
try
{
java.sql.Date systemDate = new java.sql.Date(sysDate.getTime());
stmt = connection.prepareStatement("SELECT n_cube_id, n_cube_nm, notes_bin, version_no_cd, status_cd, app_cd, create_dt, update_dt, " +
"create_hid, update_hid, sys_effective_dt, sys_expiration_dt, business_effective_dt, business_expiration_dt FROM n_cube WHERE n_cube_nm LIKE ? AND app_cd = ? AND version_no_cd = ? AND status_cd = ? AND sys_effective_dt <= ? AND (sys_expiration_dt IS NULL OR sys_expiration_dt >= ?)");
stmt.setString(1, sqlLike);
stmt.setString(2, app);
stmt.setString(3, version);
stmt.setString(4, status);
stmt.setDate(5, systemDate);
stmt.setDate(6, systemDate);
ResultSet rs = stmt.executeQuery();
List<NCubeInfoDto> records = new ArrayList<NCubeInfoDto>();
while (rs.next())
{
NCubeInfoDto dto = new NCubeInfoDto();
dto.id = rs.getLong("n_cube_id");
dto.name = rs.getString("n_cube_nm");
byte[] notes = rs.getBytes("notes_bin");
dto.notes = new String(notes == null ? "".getBytes() : notes, "UTF-8");
dto.version = rs.getString("version_no_cd");
dto.status = rs.getString("status_cd");
dto.app = rs.getString("app_cd");
dto.createDate = rs.getDate("create_dt");
dto.updateDate = rs.getDate("update_dt");
dto.createHid = rs.getString("create_hid");
dto.updateHid = rs.getString("update_hid");
dto.sysEffDate = rs.getDate("sys_effective_dt");
dto.sysEndDate = rs.getDate("sys_expiration_dt");
dto.bizEffDate = rs.getDate("business_effective_dt");
dto.bizExpDate = rs.getDate("business_expiration_dt");
records.add(dto);
}
return records.toArray();
}
catch (Exception e)
{
String s = "Unable to fetch NCubes matching '" + sqlLike + "' from database";
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
/**
* Duplicate the specified n-cube, given it the new name, and the same app, version, status as the source n-cube.
*/
public void duplicate(Connection connection, String newName, String name, String newApp, String app, String newVersion, String version, String status, Date sysDate)
{
NCube ncube = loadCube(connection, app, name, version, status, sysDate);
NCube copy = ncube.duplicate(newName);
createCube(connection, newApp, copy, newVersion);
String json = getTestData(connection, app, name, version, sysDate);
updateTestData(connection, newApp, newName, newVersion, json);
String notes = getNotes(connection, app, name, version, sysDate);
updateNotes(connection, newApp, newName, newVersion, notes);
}
/**
* Return an array [] of Strings containing all unique App names.
*/
public Object[] getAppNames(Connection connection, Date sysDate)
{
validateConnection(connection);
if (sysDate == null)
{
sysDate = new Date();
}
PreparedStatement stmt = null;
try
{
java.sql.Date systemDate = new java.sql.Date(sysDate.getTime());
stmt = connection.prepareStatement("SELECT DISTINCT app_cd FROM n_cube WHERE sys_effective_dt <= ? AND (sys_expiration_dt IS NULL OR sys_expiration_dt >= ?)");
stmt.setDate(1, systemDate);
stmt.setDate(2, systemDate);
ResultSet rs = stmt.executeQuery();
List<String> records = new ArrayList<String>();
while (rs.next())
{
records.add(rs.getString(1));
}
Collections.sort(records);
return records.toArray();
}
catch (Exception e)
{
String s = "Unable to fetch all ncube app names from database";
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
/**
* Return an array [] of Strings containing all unique App names.
*/
public Object[] getAppVersions(Connection connection, String app, String status, Date sysDate)
{
validateConnection(connection);
validateApp(app);
validateStatus(status);
if (sysDate == null)
{
sysDate = new Date();
}
PreparedStatement stmt = null;
try
{
java.sql.Date systemDate = new java.sql.Date(sysDate.getTime());
stmt = connection.prepareStatement("SELECT DISTINCT version_no_cd FROM n_cube WHERE app_cd = ? and status_cd = ? AND sys_effective_dt <= ? AND (sys_expiration_dt IS NULL OR sys_expiration_dt >= ?)");
stmt.setString(1, app);
stmt.setString(2, status);
stmt.setDate(3, systemDate);
stmt.setDate(4, systemDate);
ResultSet rs = stmt.executeQuery();
List<String> records = new ArrayList<String>();
while (rs.next())
{
records.add(rs.getString(1));
}
Collections.sort(records); // May need to enhance to ensure 2.19.1 comes after 2.2.1
return records.toArray();
}
catch (Exception e)
{
String s = "Unable to fetch all ncube app versions from database";
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
/**
* Update the passed in NCube. Only SNAPSHOT ncubes can be updated.
* @param connection JDBC connection
* @param ncube NCube to be updated.
* @return boolean true on success, false otherwise
*/
public boolean updateCube(Connection connection, String app, NCube ncube, String version)
{
validateConnection(connection);
validateApp(app);
validateVersion(version);
if (ncube == null)
{
throw new IllegalArgumentException("NCube cannot be null for updating");
}
synchronized(cubeList)
{
PreparedStatement stmt = null;
try
{
stmt = connection.prepareStatement("UPDATE n_cube SET cube_value_bin=?, update_dt=? WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.SNAPSHOT + "'");
stmt.setBytes(1, JsonWriter.objectToJson(ncube).getBytes("UTF-8"));
stmt.setDate(2, new java.sql.Date(System.currentTimeMillis()));
stmt.setString(3, app);
stmt.setString(4, ncube.getName());
stmt.setString(5, version);
int count = stmt.executeUpdate();
if (count != 1)
{
throw new IllegalStateException("Only one (1) row should be updated.");
}
return true;
}
catch (IllegalStateException e) { throw e; }
catch (Exception e)
{
String s = "Unable to update NCube: " + ncube.getName() + ", app: " + app + ", version: " + version;
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
}
/**
* Persist the passed in NCube
* @param connection JDBC connection
* @param ncube NCube to be persisted
*/
public void createCube(Connection connection, String app, NCube ncube, String version)
{
validateConnection(connection);
validateApp(app);
validateVersion(version);
if (ncube == null)
{
throw new IllegalArgumentException("NCube cannot be null when creating a new n-cube");
}
validateCubeName(ncube.getName());
synchronized(cubeList)
{
PreparedStatement stmt = null;
try
{
stmt = connection.prepareStatement("SELECT n_cube_id AS \"id\" FROM n_cube WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ?");
stmt.setString(1, app);
stmt.setString(2, ncube.getName());
stmt.setString(3, version);
ResultSet rs = stmt.executeQuery();
if (rs.next())
{ // NCube with same name and version number already exists.
throw new IllegalStateException("NCube '" + ncube.getName() + "' (" + version + ") already exists.");
}
else
{ // Do INSERT
stmt.close();
stmt = connection.prepareStatement("INSERT INTO n_cube (n_cube_id, app_cd, n_cube_nm, cube_value_bin, version_no_cd, create_dt, sys_effective_dt) VALUES (?, ?, ?, ?, ?, ?, ?)");
stmt.setLong(1, UniqueIdGenerator.getUniqueId());
stmt.setString(2, app);
stmt.setString(3, ncube.getName());
String json = JsonWriter.objectToJson(ncube);
stmt.setBytes(4, json.getBytes("UTF-8"));
stmt.setString(5, version);
java.sql.Date now = new java.sql.Date(System.currentTimeMillis());
stmt.setDate(6, now);
stmt.setDate(7, now);
int rowCount = stmt.executeUpdate();
if (rowCount != 1)
{
throw new IllegalStateException("error saving new NCube: " + ncube.getName() + "', app: " + app + ", version: " + version + " (" + rowCount + " rows inserted, should be 1)");
}
}
cubeList.put(ncube.getName(), ncube);
}
catch (IllegalStateException e) { throw e; }
catch (Exception e)
{
String s = "Unable to save NCube: " + ncube.getName() + ", app: " + app + ", version: " + version + " to database";
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
}
/**
* Move ncubes matching the passed in version and APP_CD from SNAPSHOT to RELEASE
* state. All ncubes move version at the same time. This is by design so that the
* cube join commands do not need to mess with determining what ncube versions
* they join with.
*
* @param connection JDBC connection
* @param version String version to move from SNAPSHOT to RELEASE
* @return int count of ncubes that were released
*/
public int releaseCubes(Connection connection, String app, String version)
{
validateConnection(connection);
validateApp(app);
validateVersion(version);
synchronized(cubeList)
{
PreparedStatement stmt1 = null;
PreparedStatement stmt2 = null;
try
{
stmt1 = connection.prepareStatement("SELECT n_cube_id FROM n_cube WHERE app_cd = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.RELEASE + "'");
stmt1.setString(1, app);
stmt1.setString(2, version);
ResultSet rs = stmt1.executeQuery();
if (rs.next())
{
throw new IllegalStateException("A RELEASE version " + version + " already exists. Have system admin renumber your SNAPSHOT version.");
}
stmt2 = connection.prepareStatement("UPDATE n_cube SET update_dt = ?, status_cd = '" + ReleaseStatus.RELEASE + "' WHERE app_cd = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.SNAPSHOT + "'");
stmt2.setDate(1, new java.sql.Date(System.currentTimeMillis()));
stmt2.setString(2, app);
stmt2.setString(3, version);
return stmt2.executeUpdate();
}
catch (IllegalStateException e) { throw e; }
catch (Exception e)
{
String s = "Unable to release NCubes for app: " + app + ", version: " + version + ", due to an error: " + e.getMessage();
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt1);
jdbcCleanup(stmt2);
}
}
}
/**
* This API creates a SNAPSHOT set of cubes by copying all of
* the RELEASE ncubes that match the oldVersion and app to
* the new version in SNAPSHOT mode. Basically, it duplicates
* an entire set of NCubes and places a new version label on them,
* in SNAPSHOT status.
*/
public int createSnapshotCubes(Connection connection, String app, String relVersion, String newSnapVer)
{
validateConnection(connection);
validateApp(app);
validateVersion(relVersion);
validateVersion(newSnapVer);
if (relVersion.equals(newSnapVer))
{
throw new IllegalArgumentException("New SNAPSHOT version " + relVersion + " cannot be the same as the RELEASE version.");
}
synchronized(cubeList)
{
PreparedStatement stmt0 = null;
PreparedStatement stmt1 = null;
PreparedStatement stmt2 = null;
try
{
stmt0 = connection.prepareStatement("SELECT n_cube_id FROM n_cube WHERE app_cd = ? AND version_no_cd = ?");
stmt0.setString(1, app);
stmt0.setString(2, newSnapVer);
ResultSet rs = stmt0.executeQuery();
if (rs.next())
{
throw new IllegalStateException("New SNAPSHOT Version specified (" + newSnapVer + ") matches an existing version. Specify new SNAPSHOT version that does not exist.");
}
rs.close();
stmt1 = connection.prepareStatement(
"SELECT n_cube_nm, cube_value_bin, create_dt, update_dt, create_hid, update_hid, version_no_cd, status_cd, sys_effective_dt, sys_expiration_dt, business_effective_dt, business_expiration_dt, app_cd, test_data_bin, notes_bin\n" +
"FROM n_cube\n" +
"WHERE app_cd = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.RELEASE + "'");
stmt1.setString(1, app);
stmt1.setString(2, relVersion);
rs = stmt1.executeQuery();
stmt2 = connection.prepareStatement(
"INSERT INTO n_cube (n_cube_id, n_cube_nm, cube_value_bin, create_dt, update_dt, create_hid, update_hid, version_no_cd, status_cd, sys_effective_dt, sys_expiration_dt, business_effective_dt, business_expiration_dt, app_cd, test_data_bin, notes_bin)\n" +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
int count = 0;
while (rs.next())
{
count++;
stmt2.setLong(1, UniqueIdGenerator.getUniqueId());
stmt2.setString(2, rs.getString("n_cube_nm"));
stmt2.setBytes(3, rs.getBytes("cube_value_bin"));
stmt2.setDate(4, new java.sql.Date(System.currentTimeMillis()));
stmt2.setDate(5, new java.sql.Date(System.currentTimeMillis()));
stmt2.setString(6, rs.getString("create_hid"));
stmt2.setString(7, rs.getString("update_hid"));
stmt2.setString(8, newSnapVer);
stmt2.setString(9, ReleaseStatus.SNAPSHOT.name());
stmt2.setDate(10, rs.getDate("sys_effective_dt"));
stmt2.setDate(11, rs.getDate("sys_expiration_dt"));
stmt2.setDate(12, rs.getDate("business_effective_dt"));
stmt2.setDate(13, rs.getDate("business_expiration_dt"));
stmt2.setString(14, rs.getString("app_cd"));
stmt2.setBytes(15, rs.getBytes("test_data_bin"));
stmt2.setBytes(16, rs.getBytes("notes_bin"));
stmt2.executeUpdate();
}
return count;
}
catch (IllegalStateException e) { throw e; }
catch (Exception e)
{
String s = "Unable to create SNAPSHOT NCubes for app: " + app + ", version: " + newSnapVer + ", due to an error: " + e.getMessage();
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt0);
jdbcCleanup(stmt1);
jdbcCleanup(stmt2);
}
}
}
/**
* Change the SNAPSHOT version value.
*/
public void changeVersionValue(Connection connection, String app, String currVersion, String newSnapVer)
{
validateConnection(connection);
validateApp(app);
validateVersion(currVersion);
validateVersion(newSnapVer);
synchronized(cubeList)
{
PreparedStatement stmt1 = null;
PreparedStatement stmt2 = null;
try
{
stmt1 = connection.prepareStatement("SELECT n_cube_id FROM n_cube WHERE app_cd = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.RELEASE + "'");
stmt1.setString(1, app);
stmt1.setString(2, newSnapVer);
ResultSet rs = stmt1.executeQuery();
if (rs.next())
{
throw new IllegalStateException("RELEASE n-cubes found with version " + newSnapVer + ". Choose a different SNAPSHOT version.");
}
stmt2 = connection.prepareStatement("UPDATE n_cube SET update_dt = ?, version_no_cd = ? WHERE app_cd = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.SNAPSHOT + "'");
stmt2.setDate(1, new java.sql.Date(System.currentTimeMillis()));
stmt2.setString(2, newSnapVer);
stmt2.setString(3, app);
stmt2.setString(4, currVersion);
int count = stmt2.executeUpdate();
if (count < 1)
{
throw new IllegalStateException("No SNAPSHOT n-cubes found with version " + currVersion + ", therefore nothing changed.");
}
}
catch (IllegalStateException e) { throw e; }
catch (Exception e)
{
String s = "Unable to change SNAPSHOT version from " + currVersion + " to " + newSnapVer + " for app: " + app + ", due to an error: " + e.getMessage();
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt1);
jdbcCleanup(stmt2);
}
}
}
/**
* Delete the named NCube from the database
* @param connection JDBC connection
* @param name NCube to be deleted
*/
public boolean deleteCube(Connection connection, String app, String name, String version, boolean allowDelete)
{
validateConnection(connection);
validateApp(app);
validateCubeName(name);
validateVersion(version);
synchronized(cubeList)
{
PreparedStatement stmt = null;
try
{
if (allowDelete)
{
stmt = connection.prepareStatement("DELETE FROM n_cube WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ?");
}
else
{
stmt = connection.prepareStatement("DELETE FROM n_cube WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.SNAPSHOT + "'");
}
stmt.setString(1, app);
stmt.setString(2, name);
stmt.setString(3, version);
int rows = stmt.executeUpdate();
if (rows > 0)
{
cubeList.remove(name);
return true;
}
return false;
}
catch (Exception e)
{
String s = "Unable to delete NCube: " + name + ", app: " + app + ", version: " + version + " from database: " + e.getMessage();
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
}
/**
* Update the notes associated to an NCube
* @return true if the update succeeds, false otherwise
*/
public boolean updateNotes(Connection connection, String app, String name, String version, String notes)
{
validateConnection(connection);
validateApp(app);
validateCubeName(name);
validateVersion(version);
synchronized(cubeList)
{
PreparedStatement stmt = null;
try
{
stmt = connection.prepareStatement("UPDATE n_cube SET notes_bin = ?, update_dt = ? WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ?");
stmt.setBytes(1, notes == null ? null : notes.getBytes("UTF-8"));
stmt.setDate(2, new java.sql.Date(System.currentTimeMillis()));
stmt.setString(3, app);
stmt.setString(4, name);
stmt.setString(5, version);
int count = stmt.executeUpdate();
if (count > 1)
{
throw new IllegalStateException("Only one (1) row's notes should be updated.");
}
if (count == 0)
{
throw new IllegalStateException("No NCube matching app: " + app + ", name: " + name + ", version: " + version);
}
return true;
}
catch(IllegalStateException e) { throw e; }
catch(Exception e) { throw new RuntimeException("Unable to update notes for NCube: " + name + ", app: " + app + ", version: " + version, e); }
finally
{
jdbcCleanup(stmt);
}
}
}
/**
* Get the notes associated to an NCube
* @return String notes.
*/
public String getNotes(Connection connection, String app, String name, String version, Date sysDate)
{
validateConnection(connection);
validateApp(app);
validateCubeName(name);
validateVersion(version);
if (sysDate == null)
{
sysDate = new Date();
}
PreparedStatement stmt = null;
try
{
java.sql.Date systemDate = new java.sql.Date(sysDate.getTime());
stmt = connection.prepareStatement("SELECT notes_bin FROM n_cube WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ? AND sys_effective_dt <= ? AND (sys_expiration_dt IS NULL OR sys_expiration_dt >= ?)");
stmt.setString(1, app);
stmt.setString(2, name);
stmt.setString(3, version);
stmt.setDate(4, systemDate);
stmt.setDate(5, systemDate);
ResultSet rs = stmt.executeQuery();
if (rs.next())
{
byte[] notes = rs.getBytes("notes_bin");
return new String(notes == null ? "".getBytes() : notes, "UTF-8");
}
throw new IllegalArgumentException("No NCube matching passed in parameters.");
}
catch (IllegalArgumentException e) { throw e; }
catch (Exception e)
{
String s = "Unable to fetch notes for NCube: " + name + ", app: " + app + ", version: " + version;
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
/**
* Update the test data associated to an NCube
* @return true if the update succeeds, false otherwise
*/
public boolean updateTestData(Connection connection, String app, String name, String version, String testData)
{
validateConnection(connection);
validateApp(app);
validateCubeName(name);
validateVersion(version);
synchronized(cubeList)
{
PreparedStatement stmt = null;
try
{
stmt = connection.prepareStatement("UPDATE n_cube SET test_data_bin=?, update_dt=? WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ? AND status_cd = '" + ReleaseStatus.SNAPSHOT + "'");
stmt.setBytes(1, testData == null ? null : testData.getBytes("UTF-8"));
stmt.setDate(2, new java.sql.Date(System.currentTimeMillis()));
stmt.setString(3, app);
stmt.setString(4, name);
stmt.setString(5, version);
int count = stmt.executeUpdate();
if (count > 1)
{
throw new IllegalStateException("Only one (1) row's test data should be updated.");
}
if (count == 0)
{
throw new IllegalStateException("No NCube matching app: " + app + ", name: " + name + ", version: " + version);
}
return true;
}
catch(IllegalStateException e) { throw e; }
catch(Exception e)
{
String s = "Unable to update test data for NCube: " + name + ", app: " + app + ", version: " + version;
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
}
/**
* Get the Test Data associated to the NCube.
* @return String serialized JSON test data. Use JsonReader to turn it back into
* Java objects.
*/
public static String getTestData(Connection connection, String app, String name, String version, Date sysDate)
{
validateConnection(connection);
validateApp(app);
validateCubeName(name);
validateVersion(version);
if (sysDate == null)
{
sysDate = new Date();
}
PreparedStatement stmt = null;
try
{
java.sql.Date systemDate = new java.sql.Date(sysDate.getTime());
stmt = connection.prepareStatement("SELECT test_data_bin FROM n_cube WHERE app_cd = ? AND n_cube_nm = ? AND version_no_cd = ? AND sys_effective_dt <= ? AND (sys_expiration_dt IS NULL OR sys_expiration_dt >= ?)");
stmt.setString(1, app);
stmt.setString(2, name);
stmt.setString(3, version);
stmt.setDate(4, systemDate);
stmt.setDate(5, systemDate);
ResultSet rs = stmt.executeQuery();
if (rs.next())
{
byte[] testData = rs.getBytes("test_data_bin");
return new String(testData == null ? "".getBytes() : testData, "UTF-8");
}
throw new IllegalArgumentException("No NCube matching passed in parameters.");
}
catch (IllegalArgumentException e) { throw e; }
catch (Exception e)
{
String s = "Unable to fetch test data for NCube: " + name + ", app: " + app + ", version: " + version;
LOG.error(s, e);
throw new RuntimeException(s, e);
}
finally
{
jdbcCleanup(stmt);
}
}
public NCube getNCubeFromResource(String name)
{
try
{
URL url = NCubeManager.class.getResource("/" + name);
File jsonFile = new File(url.getFile());
FileInputStream in = new FileInputStream(jsonFile);
JsonReader reader = new JsonReader(in, true);
JsonObject jObj = (JsonObject) reader.readObject();
reader.close();
String json = JsonWriter.objectToJson(jObj);
NCube ncube = NCube.fromSimpleJson(json);
addCube(ncube);
return ncube;
}
catch (IOException e)
{
String s = "Failed to load ncube from resource: " + name;
LOG.error(s, e);
throw new RuntimeException(s, e);
}
}
public List<NCube> getNCubesFromResource(String name)
{
String lastSuccessful = "";
try
{
URL url = NCubeManager.class.getResource("/" + name);
File arrayJsonCubes = new File(url.getFile());
FileInputStream in = new FileInputStream(arrayJsonCubes);
JsonReader reader = new JsonReader(in, true);
JsonObject ncubes = (JsonObject) reader.readObject();
Object[] cubes = ncubes.getArray();
List<NCube> cubeList = new ArrayList<NCube>(cubes.length);
for (Object cube : cubes)
{
JsonObject ncube = (JsonObject) cube;
String json = JsonWriter.objectToJson(ncube);
NCube nCube = NCube.fromSimpleJson(json);
addCube(nCube);
lastSuccessful = nCube.getName();
cubeList.add(nCube);
}
return cubeList;
}
catch (Exception e)
{
String s = "Failed to load ncubes from resource: " + name + ", last successful cube: " + lastSuccessful;
LOG.error(s);
throw new RuntimeException(s, e);
}
}
}