/** Fetch the classloader for the given ApplicationID. */ static URLClassLoader getUrlClassLoader(ApplicationID appId, Map input) { NCube cpCube = getCube(appId, CLASSPATH_CUBE); if (cpCube == null) { // No sys.classpath cube exists, just create regular GroovyClassLoader with no // URLs set into it. // Scope the GroovyClassLoader per ApplicationID return getLocalClassloader(appId); } final String envLevel = SystemUtilities.getExternalVariable("ENV_LEVEL"); if (!input.containsKey("env") && StringUtilities.hasContent( envLevel)) { // Add in the 'ENV_LEVEL" environment variable when looking up sys.* cubes, // if there was not already an entry for it. input.put("env", envLevel); } if (!input.containsKey("username")) { // same as ENV_LEVEL, add it in if not already there. input.put("username", System.getProperty("user.name")); } Object urlCpLoader = cpCube.getCell(input); if (urlCpLoader instanceof URLClassLoader) { return (URLClassLoader) urlCpLoader; } throw new IllegalStateException( "If the sys.classpath cube exists it must return a URLClassLoader."); }
/** Associate Advice to all n-cubes that match the passed in regular expression. */ public static void addAdvice(ApplicationID appId, String wildcard, Advice advice) { validateAppId(appId); ConcurrentMap<String, Advice> current = advices.get(appId); if (current == null) { current = new ConcurrentHashMap<>(); ConcurrentMap<String, Advice> mapRef = advices.putIfAbsent(appId, current); if (mapRef != null) { current = mapRef; } } current.put(advice.getName() + '/' + wildcard, advice); // Apply newly added advice to any fully loaded (hydrated) cubes. String regex = StringUtilities.wildcardToRegexString(wildcard); Map<String, Object> cubes = getCacheForApp(appId); for (Object value : cubes.values()) { if (value instanceof NCube) { // apply advice to hydrated cubes NCube ncube = (NCube) value; Axis axis = ncube.getAxis("method"); addAdviceToMatchedCube(advice, regex, ncube, axis); } } }
public static List<NCube> getNCubesFromResource(String name) { String lastSuccessful = ""; try { Object[] cubes = getJsonObjectFromResource(name); List<NCube> cubeList = new ArrayList<>(cubes.length); for (Object cube : cubes) { JsonObject ncube = (JsonObject) cube; String json = JsonWriter.objectToJson(ncube); NCube nCube = NCube.fromSimpleJson(json); nCube.sha1(); addCube(nCube.getApplicationID(), nCube); lastSuccessful = nCube.getName(); cubeList.add(nCube); } return cubeList; } catch (Exception e) { String s = "Failed to load cubes from resource: " + name + ", last successful cube: " + lastSuccessful; LOG.warn(s); throw new RuntimeException(s, e); } }
/** Retrieve all cube names that are deeply referenced by ApplicationID + n-cube name. */ public static void getReferencedCubeNames(ApplicationID appId, String name, Set<String> refs) { if (refs == null) { throw new IllegalArgumentException( "Could not get referenced cube names, null passed in for Set to hold referenced n-cube names, app: " + appId + ", n-cube: " + name); } validateAppId(appId); NCube.validateCubeName(name); NCube ncube = getCube(appId, name); if (ncube == null) { throw new IllegalArgumentException( "Could not get referenced cube names, n-cube: " + name + " does not exist in app: " + appId); } Set<String> subCubeList = ncube.getReferencedCubeNames(); // TODO: Use explicit stack, NOT recursion for (String cubeName : subCubeList) { if (!refs.contains(cubeName)) { refs.add(cubeName); getReferencedCubeNames(appId, cubeName, refs); } } }
private static void cacheCubes(ApplicationID appId, List<NCubeInfoDto> cubes) { Map<String, Object> appCache = getCacheForApp(appId); for (NCubeInfoDto cubeInfo : cubes) { String key = cubeInfo.name.toLowerCase(); if (!cubeInfo.revision.startsWith("-")) { Object cachedItem = appCache.get(key); if (cachedItem == null || cachedItem instanceof NCubeInfoDto) { // If cube not in cache or already in cache as infoDto, overwrite it appCache.put(key, cubeInfo); } else if (cachedItem instanceof NCube) { // If cube is already cached, make sure the SHA1's match - if not, then cache // the new cubeInfo NCube ncube = (NCube) cachedItem; if (!ncube.sha1().equals(cubeInfo.sha1)) { appCache.put(key, cubeInfo); } } } } }
/** * Update the passed in NCube. Only SNAPSHOT cubes can be updated. * * @param ncube NCube to be updated. * @return boolean true on success, false otherwise */ public static boolean updateCube(ApplicationID appId, NCube ncube, String username) { validateAppId(appId); validateCube(ncube); if (appId.isRelease()) { throw new IllegalArgumentException( ReleaseStatus.RELEASE + " cubes cannot be updated, cube: " + ncube.getName() + ", app: " + appId); } appId.validateBranchIsNotHead(); final String cubeName = ncube.getName(); getPersister().updateCube(appId, ncube, username); ncube.setApplicationID(appId); if (CLASSPATH_CUBE.equalsIgnoreCase( cubeName)) { // If the sys.classpath cube is changed, then the entire class loader must be // dropped. It will be lazily rebuilt. clearCache(appId); } addCube(appId, ncube); broadcast(appId); return true; }
/** * Load n-cube, bypassing any caching. This is necessary for n-cube-editor (IDE time usage). If * the IDE environment is clustered, cannot be getting stale copies from cache. Any advices in the * manager will be applied to the n-cube. * * @return NCube of the specified name from the specified AppID, or null if not found. */ public static NCube loadCube(ApplicationID appId, String cubeName) { NCube ncube = getPersister().loadCube(appId, cubeName); if (ncube == null) { return null; } applyAdvices(ncube.getApplicationID(), ncube); Map<String, Object> cubes = getCacheForApp(appId); cubes.put(cubeName.toLowerCase(), ncube); // Update cache return ncube; }
private static NCube prepareCube(NCube cube) { applyAdvices(cube.getApplicationID(), cube); String cubeName = cube.getName().toLowerCase(); if (!cube.getMetaProperties().containsKey("cache") || Boolean.TRUE.equals( cube.getMetaProperty( "cache"))) { // Allow cubes to not be cached by specified 'cache':false as a cube // meta-property. getCacheForApp(cube.getApplicationID()).put(cubeName, cube); } return cube; }
public static ApplicationID getApplicationID( String tenant, String app, Map<String, Object> coord) { ApplicationID.validateTenant(tenant); ApplicationID.validateApp(tenant); if (coord == null) { coord = new HashMap<>(); } NCube bootCube = getCube(ApplicationID.getBootVersion(tenant, app), SYS_BOOTSTRAP); if (bootCube == null) { throw new IllegalStateException( "Missing " + SYS_BOOTSTRAP + " cube in the 0.0.0 version for the app: " + app); } ApplicationID bootAppId = (ApplicationID) bootCube.getCell(coord); String version = bootAppId.getVersion(); String status = bootAppId.getStatus(); String branch = bootAppId.getBranch(); if (!tenant.equalsIgnoreCase(bootAppId.getTenant())) { LOG.warn( "sys.bootstrap cube for tenant '" + tenant + "', app '" + app + "' is returning a different tenant '" + bootAppId.getTenant() + "' than requested. Using '" + tenant + "' instead."); } if (!app.equalsIgnoreCase(bootAppId.getApp())) { LOG.warn( "sys.bootstrap cube for tenant '" + tenant + "', app '" + app + "' is returning a different app '" + bootAppId.getApp() + "' than requested. Using '" + app + "' instead."); } return new ApplicationID(tenant, app, version, status, branch); }
public static NCube getNCubeFromResource(ApplicationID id, String name) { try { String json = getResourceAsString(name); NCube ncube = NCube.fromSimpleJson(json); ncube.setApplicationID(id); ncube.sha1(); addCube(id, ncube); return ncube; } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException("Failed to load cube from resource: " + name, e); } }
private static void addAdviceToMatchedCube(Advice advice, String regex, NCube ncube, Axis axis) { if (axis != null) { // Controller methods for (Column column : axis.getColumnsWithoutDefault()) { String method = column.getValue().toString(); String classMethod = ncube.getName() + '.' + method + "()"; if (classMethod.matches(regex)) { ncube.addAdvice(advice, method); } } } else { // Expressions String classMethod = ncube.getName() + ".run()"; if (classMethod.matches(regex)) { ncube.addAdvice(advice, "run"); } } }
/** Restore a previously deleted n-cube. */ public static void restoreCubes(ApplicationID appId, Object[] cubeNames, String username) { validateAppId(appId); appId.validateBranchIsNotHead(); if (appId.isRelease()) { throw new IllegalArgumentException( ReleaseStatus.RELEASE + " cubes cannot be restored, app: " + appId); } if (ArrayUtilities.isEmpty(cubeNames)) { throw new IllegalArgumentException( "Error, empty array of cube names passed in to be restored."); } // Batch restore getPersister().restoreCubes(appId, cubeNames, username); // Load cache for (Object name : cubeNames) { if ((name instanceof String)) { String cubeName = (String) name; NCube.validateCubeName(cubeName); NCube ncube = getPersister().loadCube(appId, cubeName); addCube(appId, ncube); } else { throw new IllegalArgumentException("Non string name given for cube to restore: " + name); } } }
/** Testing API (Cache validation) */ static boolean isCubeCached(ApplicationID appId, String cubeName) { validateAppId(appId); NCube.validateCubeName(cubeName); Map<String, Object> ncubes = getCacheForApp(appId); Object cachedItem = ncubes.get(cubeName.toLowerCase()); return cachedItem instanceof NCube || cachedItem instanceof NCubeInfoDto; }
private static NCube checkForConflicts( ApplicationID appId, Map<String, Map> errors, String message, NCubeInfoDto info, NCubeInfoDto head, boolean reverse) { Map<String, Object> map = new LinkedHashMap<>(); map.put("message", message); map.put("sha1", info.sha1); map.put("headSha1", head != null ? head.sha1 : null); try { if (head != null) { long branchCubeId = (long) Converter.convert(info.id, long.class); long headCubeId = (long) Converter.convert(head.id, long.class); NCube branchCube = getPersister().loadCubeById(branchCubeId); NCube headCube = getPersister().loadCubeById(headCubeId); if (info.headSha1 != null) { NCube baseCube = getPersister().loadCubeBySha1(appId, info.name, info.headSha1); Map delta1 = baseCube.getDelta(branchCube); Map delta2 = baseCube.getDelta(headCube); if (NCube.areDeltaSetsCompatible(delta1, delta2)) { if (reverse) { headCube.mergeCellChangeSet(delta1); return headCube; } else { branchCube.mergeCellChangeSet(delta2); return branchCube; } } } List<Delta> diff = branchCube.getDeltaDescription(headCube); if (diff.size() > 0) { map.put("diff", diff); } else { return branchCube; } } else { map.put("diff", null); } } catch (Exception e) { map.put("diff", e.getMessage()); } errors.put(info.name, map); return null; }
/** * Add a cube to the internal cache of available cubes. * * @param ncube NCube to add to the list. */ public static void addCube(ApplicationID appId, NCube ncube) { validateAppId(appId); validateCube(ncube); String cubeName = ncube.getName().toLowerCase(); if (!ncube.getMetaProperties().containsKey("cache") || Boolean.TRUE.equals( ncube.getMetaProperty( "cache"))) { // Allow cubes to not be cached by specified 'cache':false as a cube // meta-property. getCacheForApp(appId).put(cubeName, ncube); } // Apply any matching advices to it applyAdvices(appId, ncube); }
/** Duplicate the given n-cube specified by oldAppId and oldName to new ApplicationID and name, */ public static void duplicate( ApplicationID oldAppId, ApplicationID newAppId, String oldName, String newName, String username) { validateAppId(oldAppId); validateAppId(newAppId); newAppId.validateBranchIsNotHead(); if (newAppId.isRelease()) { throw new IllegalArgumentException( "Cubes cannot be duplicated into a " + ReleaseStatus.RELEASE + " version, cube: " + newName + ", app: " + newAppId); } NCube.validateCubeName(oldName); NCube.validateCubeName(newName); if (oldName.equalsIgnoreCase(newName) && oldAppId.equals(newAppId)) { throw new IllegalArgumentException( "Could not duplicate, old name cannot be the same as the new name when oldAppId matches newAppId, name: " + oldName + ", app: " + oldAppId); } getPersister().duplicateCube(oldAppId, newAppId, oldName, newName, username); if (CLASSPATH_CUBE.equalsIgnoreCase( newName)) { // If another cube is renamed into sys.classpath, // then the entire class loader must be dropped (and then lazily rebuilt). clearCache(newAppId); } else { Map<String, Object> appCache = getCacheForApp(newAppId); appCache.remove(newName.toLowerCase()); } broadcast(newAppId); }
public static boolean renameCube( ApplicationID appId, String oldName, String newName, String username) { validateAppId(appId); appId.validateBranchIsNotHead(); if (appId.isRelease()) { throw new IllegalArgumentException( "Cannot rename a " + ReleaseStatus.RELEASE + " cube, cube: " + oldName + ", app: " + appId); } NCube.validateCubeName(oldName); NCube.validateCubeName(newName); if (oldName.equalsIgnoreCase(newName)) { throw new IllegalArgumentException( "Could not rename, old name cannot be the same as the new name, name: " + oldName + ", app: " + appId); } boolean result = getPersister().renameCube(appId, oldName, newName, username); if (CLASSPATH_CUBE.equalsIgnoreCase(oldName) || CLASSPATH_CUBE.equalsIgnoreCase( newName)) { // If the sys.classpath cube is renamed, or another cube is renamed into // sys.classpath, // then the entire class loader must be dropped (and then lazily rebuilt). clearCache(appId); } else { Map<String, Object> appCache = getCacheForApp(appId); appCache.remove(oldName.toLowerCase()); appCache.remove(newName.toLowerCase()); } broadcast(appId); return result; }
/** * Fetch all the n-cube names for the given ApplicationID. This API will load all cube records for * the ApplicationID (NCubeInfoDtos), and then get the names from them. * * @return Set<String> n-cube names. If an empty Set is returned, then there are no persisted * n-cubes for the passed in ApplicationID. */ public static Set<String> getCubeNames(ApplicationID appId) { Map<String, Object> options = new HashMap<>(); options.put(SEARCH_ACTIVE_RECORDS_ONLY, true); List<NCubeInfoDto> cubeInfos = search(appId, null, null, options); Set<String> names = new TreeSet<>(); for (NCubeInfoDto info : cubeInfos) { names.add(info.name); } if (names.isEmpty()) { // Support tests that load cubes from JSON files... // can only be in there as ncubes, not ncubeDtoInfo for (Object value : getCacheForApp(appId).values()) { if (value instanceof NCube) { NCube cube = (NCube) value; names.add(cube.getName()); } } } return new CaseInsensitiveSet<>(names); }
public static String getNotes(ApplicationID appId, String cubeName) { validateAppId(appId); NCube.validateCubeName(cubeName); Map<String, Object> options = new HashMap<>(); options.put(SEARCH_INCLUDE_NOTES, true); options.put(SEARCH_EXACT_MATCH_NAME, true); List<NCubeInfoDto> infos = search(appId, cubeName, null, options); if (infos.size() == 0) { throw new IllegalArgumentException( "Could not fetch notes, no cube: " + cubeName + " in app: " + appId); } return infos.get(0).notes; }
/** * Apply existing advices loaded into the NCubeManager, to the passed in n-cube. This allows * advices to be added first, and then let them be applied 'on demand' as an n-cube is loaded * later. * * @param appId ApplicationID * @param ncube NCube to which all matching advices will be applied. */ private static void applyAdvices(ApplicationID appId, NCube ncube) { final Map<String, Advice> appAdvices = advices.get(appId); if (MapUtilities.isEmpty(appAdvices)) { return; } for (Map.Entry<String, Advice> entry : appAdvices.entrySet()) { final Advice advice = entry.getValue(); final String wildcard = entry.getKey().replace(advice.getName() + '/', ""); final String regex = StringUtilities.wildcardToRegexString(wildcard); final Axis axis = ncube.getAxis("method"); addAdviceToMatchedCube(advice, regex, ncube, axis); } }
/** * Fetch an n-cube by name from the given ApplicationID. If no n-cubes are loaded, then a * loadCubes() call is performed and then the internal cache is checked again. If the cube is not * found, null is returned. */ public static NCube getCube(ApplicationID appId, String name) { validateAppId(appId); NCube.validateCubeName(name); Map<String, Object> cubes = getCacheForApp(appId); final String lowerCubeName = name.toLowerCase(); if (cubes.containsKey(lowerCubeName)) { // pull from cache final Object cube = cubes.get(lowerCubeName); return Boolean.FALSE == cube ? null : ensureLoaded(cube); } // now even items with metaProperties(cache = 'false') can be retrieved // and normal app processing doesn't do two queries anymore. // used to do getCubeInfoRecords() -> dto // and then dto -> loadCube(id) NCube ncube = getPersister().loadCube(appId, name); if (ncube == null) { cubes.put(lowerCubeName, Boolean.FALSE); return null; } return prepareCube(ncube); }
public static String getTestData(ApplicationID appId, String cubeName) { validateAppId(appId); NCube.validateCubeName(cubeName); return getPersister().getTestData(appId, cubeName); }
/** Get a List<NCubeInfoDto> containing all history for the given cube. */ public static List<NCubeInfoDto> getRevisionHistory(ApplicationID appId, String cubeName) { validateAppId(appId); NCube.validateCubeName(cubeName); List<NCubeInfoDto> revisions = getPersister().getRevisions(appId, cubeName); return revisions; }
public static boolean updateNotes(ApplicationID appId, String cubeName, String notes) { validateAppId(appId); NCube.validateCubeName(cubeName); return getPersister().updateNotes(appId, cubeName, notes); }
static void validateCube(NCube cube) { if (cube == null) { throw new IllegalArgumentException("NCube cannot be null"); } NCube.validateCubeName(cube.getName()); }
public static boolean updateTestData(ApplicationID appId, String cubeName, String testData) { validateAppId(appId); NCube.validateCubeName(cubeName); return getPersister().updateTestData(appId, cubeName, testData); }