@Override public void launch() { // TODO if can't be root, and ports > 1024 are in the allowed port range, // prefer that; could do this on SshMachineLocation which implements PortSupplier, // invoked from PortAttrSensorAndConfigKey, which is invoked from // MachineLifecycleTasks.preStartCustom Networking.checkPortsValid(MutableMap.of("httpPort", getHttpPort())); // We wait for evidence of running because, using // brooklyn.ssh.config.tool.class=brooklyn.util.internal.ssh.cli.SshCliTool, // we saw the ssh session return before the tomcat process was fully running // so the process failed to start. newScript(MutableMap.of("usePidFile", false), LAUNCHING) .body .append( format("cd %s", getRunDir()), BashCommands.requireExecutable("./sbin/nginx"), sudoBashCIfPrivilegedPort( getHttpPort(), format( "nohup ./sbin/nginx -p %s/ -c conf/server.conf > %s 2>&1 &", getRunDir(), getLogFileLocation())), format( "for i in {1..10}\n" + "do\n" + " test -f %1$s && ps -p `cat %1$s` && exit\n" + " sleep 1\n" + "done\n" + "echo \"No explicit error launching nginx but couldn't find process by pid; continuing but may subsequently fail\"\n" + "cat %2$s | tee /dev/stderr", getPidFile(), getLogFileLocation())) .execute(); }
@Override public void launch() { Map ports = MutableMap.of( "httpPort", getHttpPort(), "jmxPort", getJmxPort(), "shutdownPort", getShutdownPort()); NetworkUtils.checkPortsValid(ports); Map flags = MutableMap.of("usePidFile", false); // We wait for evidence of tomcat running because, using // brooklyn.ssh.config.tool.class=brooklyn.util.internal.ssh.cli.SshCliTool, // we saw the ssh session return before the tomcat process was fully running // so the process failed to start. newScript(flags, LAUNCHING) .body .append( format("%s/bin/startup.sh >>$RUN/console 2>&1 </dev/null", getExpandedInstallDir()), "for i in {1..10}\n" + "do\n" + " if [ -s " + getLogFileLocation() + " ]; then exit; fi\n" + " sleep 1\n" + "done\n" + "echo \"Couldn't determine if tomcat-server is running (logs/catalina.out is still empty); continuing but may subsequently fail\"") .execute(); }
public static void aggregateOpenGammaClusterSensors(DynamicFabric webFabric) { // at fabric, take the total for ViewProcesses and Reqs/Sec; // and take avg for reqLatency (note: simple avg -- assuming all regions equal) webFabric.addEnricher( CustomAggregatingEnricher.newSummingEnricher( MutableMap.of("allMembers", true), OpenGammaMonitoringAggregation.VIEW_PROCESSES_COUNT, OpenGammaMonitoringAggregation.VIEW_PROCESSES_COUNT, 0, null)); webFabric.addEnricher( CustomAggregatingEnricher.newSummingEnricher( MutableMap.of("allMembers", true), DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW, DynamicWebAppCluster.REQUESTS_PER_SECOND_IN_WINDOW, null, null)); webFabric.addEnricher( CustomAggregatingEnricher.newSummingEnricher( MutableMap.of("allMembers", true), OpenGammaMonitoringAggregation.OG_SERVER_COUNT, OpenGammaMonitoringAggregation.OG_SERVER_COUNT, null, null)); webFabric.addEnricher( CustomAggregatingEnricher.newAveragingEnricher( MutableMap.of("allMembers", true), HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW, HttpLatencyDetector.REQUEST_LATENCY_IN_SECONDS_IN_WINDOW, null, null)); }
private SshPollValue exec(String command, Map<String, String> env) throws IOException { if (log.isTraceEnabled()) log.trace( "Ssh polling for {}, executing {} with env {}", new Object[] {machine, command, env}); ByteArrayOutputStream stdout = new ByteArrayOutputStream(); ByteArrayOutputStream stderr = new ByteArrayOutputStream(); int exitStatus; if (execAsCommand) { exitStatus = machine.execCommands( MutableMap.<String, Object>of("out", stdout, "err", stderr), "ssh-feed", ImmutableList.of(command), env); } else { exitStatus = machine.execScript( MutableMap.<String, Object>of("out", stdout, "err", stderr), "ssh-feed", ImmutableList.of(command), env); } return new SshPollValue( machine, exitStatus, new String(stdout.toByteArray()), new String(stderr.toByteArray())); }
@Override public void redeployAll() { Map<String, String> wars = MutableMap.copyOf(getConfig(WARS_BY_CONTEXT)); String redeployPrefix = "Redeploy all WARs (count " + wars.size() + ")"; log.debug("Redeplying all WARs across cluster " + this + ": " + getConfig(WARS_BY_CONTEXT)); Iterable<CanDeployAndUndeploy> targetEntities = Iterables.filter(getChildren(), CanDeployAndUndeploy.class); TaskBuilder<Void> tb = Tasks.<Void>builder() .parallel(true) .name(redeployPrefix + " across cluster (size " + Iterables.size(targetEntities) + ")"); for (Entity targetEntity : targetEntities) { TaskBuilder<Void> redeployAllToTarget = Tasks.<Void>builder() .name(redeployPrefix + " at " + targetEntity + " (after ready check)"); for (String warContextPath : wars.keySet()) { redeployAllToTarget.add( Effectors.invocation( targetEntity, DEPLOY, MutableMap.of("url", wars.get(warContextPath), "targetName", warContextPath))); } tb.add( whenServiceUp( targetEntity, redeployAllToTarget.build(), redeployPrefix + " at " + targetEntity + " when ready")); } DynamicTasks.queueIfPossible(tb.build()).orSubmitAsync(this).asTask().getUnchecked(); }
protected Map<?, ?> getRenderingConfigurationFor(String catalogId) { MutableMap<Object, Object> result = MutableMap.of(); CatalogItem<?> item = mgmt.getCatalog().getCatalogItem(catalogId); if (item == null) return result; result.addIfNotNull("iconUrl", item.getIconUrl()); return result; }
@BeforeMethod(alwaysRun = true) public void setUp() { em = new BasicExecutionManager("mycontext"); ec = new BasicExecutionContext(em); cancellations = new Semaphore(0); messages = new ArrayList<String>(); monitorableJobSemaphoreMap = MutableMap.of(); monitorableTasksMap = MutableMap.of(); monitorableTasksMap.clear(); stopwatch = Stopwatch.createStarted(); }
public Task<?> expunge(final Entity entity, final boolean release) { if (mgmt.getEntitlementManager() .isEntitled( Entitlements.getEntitlementContext(), Entitlements.INVOKE_EFFECTOR, Entitlements.EntityAndItem.of(entity, "expunge"))) { return mgmt.getExecutionManager() .submit( MutableMap.of( "displayName", "expunging " + entity, "description", "REST call to expunge entity " + entity.getDisplayName() + " (" + entity + ")"), new Runnable() { @Override public void run() { if (release) Entities.destroyCatching(entity); else mgmt.getEntityManager().unmanage(entity); } }); } throw WebResourceUtils.unauthorized( "User '%s' is not authorized to expunge entity %s", Entitlements.getEntitlementContext().user(), entity); }
public String configFile() { // Check template URL exists String templateUrl = driver.getEntity().getConfig(NginxController.SERVER_CONF_TEMPLATE_URL); ResourceUtils.create(this).checkUrlExists(templateUrl); // Check SSL configuration ProxySslConfig ssl = driver.getEntity().getConfig(NginxController.SSL_CONFIG); if (ssl != null && Strings.isEmpty(ssl.getCertificateDestination()) && Strings.isEmpty(ssl.getCertificateSourceUrl())) { throw new IllegalStateException( "ProxySslConfig can't have a null certificateDestination and null certificateSourceUrl. One or both need to be set"); } // For mapping by URL Iterable<UrlMapping> mappings = ((NginxController) driver.getEntity()).getUrlMappings(); Multimap<String, UrlMapping> mappingsByDomain = LinkedHashMultimap.create(); for (UrlMapping mapping : mappings) { Collection<String> addrs = mapping.getAttribute(UrlMapping.TARGET_ADDRESSES); if (addrs != null && addrs.size() > 0) { mappingsByDomain.put(mapping.getDomain(), mapping); } } Map<String, Object> substitutions = MutableMap.<String, Object>builder() .putIfNotNull("ssl", ssl) .put("urlMappings", mappings) .put("domainMappings", mappingsByDomain) .build(); // Get template contents and process String contents = ResourceUtils.create(driver.getEntity()).getResourceAsString(templateUrl); return TemplateProcessor.processTemplateContents(contents, driver, substitutions); }
@Override public Map<String, Object> getProvisioningFlags(Collection<String> tags) { if (tags.size() > 0) { LOG.warn("Location {}, ignoring provisioning tags {}", this, tags); } return MutableMap.<String, Object>of(); }
protected void doStartPolling() { if (scheduledTask == null || scheduledTask.isDone()) { ScheduledTask task = new ScheduledTask(MutableMap.of("period", getConfig(POLL_PERIOD)), pollingTaskFactory); scheduledTask = ((EntityInternal) entity).getExecutionContext().submit(task); } }
public class JavaSoftwareProcessSshDriverIntegrationTest { private static final long TIMEOUT_MS = 10 * 1000; private MachineProvisioningLocation localhost = new LocalhostMachineProvisioningLocation(MutableMap.of("name", "localhost")); private TestApplication app; @BeforeMethod(alwaysRun = true) public void setup() { app = ApplicationBuilder.newManagedApp(TestApplication.class); } @AfterMethod(alwaysRun = true) public void shutdown() { if (app != null) Entities.destroyAll(app); } @Test(groups = "Integration") public void testJavaStartStopSshDriverStartsAndStopsApp() { final MyEntity entity = app.createAndManageChild(EntitySpecs.spec(MyEntity.class)); app.start(ImmutableList.of(localhost)); Asserts.succeedsEventually( MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { public void run() { assertTrue(entity.getAttribute(SoftwareProcess.SERVICE_UP)); } }); entity.stop(); assertFalse(entity.getAttribute(SoftwareProcess.SERVICE_UP)); } }
@Test public void testPolicyUpdatesModel() { final MockContainerEntity containerA = newContainer(app, "A", 10, 20); final MockContainerEntity containerB = newContainer(app, "B", 11, 21); final MockItemEntity item1 = newItem(app, containerA, "1", 12); final MockItemEntity item2 = newItem(app, containerB, "2", 13); Asserts.succeedsEventually( MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { public void run() { assertEquals(model.getPoolSize(), 2); assertEquals(model.getPoolContents(), ImmutableSet.of(containerA, containerB)); assertEquals(model.getItemWorkrate(item1), 12d); assertEquals(model.getItemWorkrate(item2), 13d); assertEquals(model.getParentContainer(item1), containerA); assertEquals(model.getParentContainer(item2), containerB); assertEquals( model.getContainerWorkrates(), ImmutableMap.of(containerA, 12d, containerB, 13d)); assertEquals(model.getPoolLowThreshold(), 10 + 11d); assertEquals(model.getPoolHighThreshold(), 20 + 21d); assertEquals(model.getCurrentPoolWorkrate(), 12 + 13d); assertFalse(model.isHot()); assertFalse(model.isCold()); } }); }
public static BasicEnricherMemento.Builder newEnricherMementoBuilder(Enricher enricher) { BasicEnricherMemento.Builder builder = BasicEnricherMemento.builder(); builder.type = enricher.getClass().getName(); builder.typeClass = enricher.getClass(); builder.id = enricher.getId(); builder.displayName = enricher.getName(); // TODO persist config keys as well? Or only support those defined on policy class; // current code will lose the ConfigKey type on rebind for anything not defined on class. // Whereas entities support that. // TODO Do we need the "nonPersistableFlagNames" that locations use? Map<ConfigKey<?>, Object> config = ((AbstractEnricher) enricher).getConfigMap().getAllConfig(); for (Map.Entry<ConfigKey<?>, Object> entry : config.entrySet()) { ConfigKey<?> key = checkNotNull(entry.getKey(), "config=%s", config); Object value = configValueToPersistable(entry.getValue()); builder.config.put(key.getName(), value); } Map<String, Object> persistableFlags = MutableMap.<String, Object>builder() .putAll( FlagUtils.getFieldsWithFlagsExcludingModifiers( enricher, Modifier.STATIC ^ Modifier.TRANSIENT)) .remove("id") .remove("name") .build(); builder.config.putAll(persistableFlags); return builder; }
@Test public void testCanSetConfig() { final ExampleJavaEnricher enricher = new ExampleJavaEnricher(MutableMap.of("displayName", "myName", "myConfig1", "myVal1")); entity.addEnricher(enricher); assertEquals(enricher.getName(), "myName"); assertEquals(enricher.myConfig1, "myVal1"); }
private JcloudsSshMachineLocation obtainEc2Machine(Map<?, ?> conf) throws Exception { return obtainMachine( MutableMap.<Object, Object>builder() .putAll(conf) .putIfAbsent("imageId", AWS_EC2_CENTOS_IMAGE_ID) .putIfAbsent("hardwareId", AWS_EC2_SMALL_HARDWARE_ID) .putIfAbsent("inboundPorts", ImmutableList.of(22)) .build()); }
@SuppressWarnings({"unchecked"}) public void start() { // TODO Previous incarnation of this logged this logged polledSensors.keySet(), but we don't // know that anymore // Is that ok, are can we do better? if (log.isDebugEnabled()) log.debug("Starting poll for {} (using {})", new Object[] {entity, this}); if (running) { throw new IllegalStateException( String.format( "Attempt to start poller %s of entity %s when already running", this, entity)); } running = true; for (final Callable<?> oneOffJob : oneOffJobs) { Task<?> task = Tasks.builder() .dynamic(false) .body((Callable<Object>) oneOffJob) .name("Poll") .description("One-time poll job " + oneOffJob) .build(); oneOffTasks.add(((EntityInternal) entity).getExecutionContext().submit(task)); } for (final PollJob<V> pollJob : pollJobs) { final String scheduleName = pollJob.handler.getDescription(); if (pollJob.pollPeriod.compareTo(Duration.ZERO) > 0) { Callable<Task<?>> pollingTaskFactory = new Callable<Task<?>>() { public Task<?> call() { DynamicSequentialTask<Void> task = new DynamicSequentialTask<Void>( MutableMap.of("displayName", scheduleName, "entity", entity), new Callable<Void>() { public Void call() { pollJob.wrappedJob.run(); return null; } }); BrooklynTaskTags.setTransient(task); return task; } }; ScheduledTask task = new ScheduledTask(MutableMap.of("period", pollJob.pollPeriod), pollingTaskFactory); tasks.add((ScheduledTask) Entities.submit(entity, task)); } else { if (log.isDebugEnabled()) log.debug( "Activating poll (but leaving off, as period {}) for {} (using {})", new Object[] {pollJob.pollPeriod, entity, this}); } } }
static void addToWarsByContext(Entity entity, String url, String targetName) { targetName = FILENAME_TO_WEB_CONTEXT_MAPPER.convertDeploymentTargetNameToContext(targetName); // TODO a better way to do atomic updates, see comment above synchronized (entity) { Map<String, String> newWarsMap = MutableMap.copyOf(entity.getConfig(WARS_BY_CONTEXT)); newWarsMap.put(targetName, url); ((EntityInternal) entity).setConfig(WARS_BY_CONTEXT, newWarsMap); } }
@SuppressWarnings({"unchecked", "rawtypes"}) public <T extends Enricher> T createEnricher(EnricherSpec<T> spec) { if (spec.getFlags().containsKey("parent")) { throw new IllegalArgumentException( "Spec's flags must not contain parent; use spec.parent() instead for " + spec); } try { Class<? extends T> clazz = spec.getType(); T enricher; if (isNewStyleEnricher(clazz)) { enricher = constructEnricher(clazz); } else { enricher = constructOldStyle(clazz, MutableMap.copyOf(spec.getFlags())); } if (spec.getDisplayName() != null) ((AbstractEnricher) enricher).setName(spec.getDisplayName()); if (isNewStyleEnricher(clazz)) { ((AbstractEnricher) enricher).setManagementContext(managementContext); Map<String, Object> config = ConfigBag.newInstance().putAll(spec.getFlags()).putAll(spec.getConfig()).getAllConfig(); ((AbstractEnricher) enricher) .configure( MutableMap.copyOf(config)); // TODO AbstractEnricher.configure modifies the map } // TODO Can we avoid this for "new-style policies"? Should we just trust the configure() // method, // which the user may have overridden? // Also see InternalLocationFactory for same issue, which this code is based on. for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) { ((AbstractEnricher) enricher).setConfig((ConfigKey) entry.getKey(), entry.getValue()); } ((AbstractEnricher) enricher).init(); return enricher; } catch (Exception e) { throw Exceptions.propagate(e); } }
private <T> void assertEqualsEventually(final T actual, final T expected) { Asserts.succeedsEventually( MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { @Override public void run() { assertEquals(actual, expected, "actual=" + actual); } }); }
public Map<String, Object> customSshConfigKeys() throws UnknownHostException { return MutableMap.<String, Object>of( "address", Networking.getLocalHost(), SshTool.PROP_SESSION_TIMEOUT.getName(), 20000, SshTool.PROP_CONNECT_TIMEOUT.getName(), 50000, SshTool.PROP_SCRIPT_HEADER.getName(), "#!/bin/bash"); }
@Test public void testModelIncludesItemsAndContainersStartedBeforePolicyCreated() { pool.removePolicy(policy); policy.destroy(); // Set-up containers and items. final MockContainerEntity containerA = newContainer(app, "A", 10, 100); MockItemEntity item1 = newItem(app, containerA, "1", 10); policy = new LoadBalancingPolicy(MutableMap.of(), TEST_METRIC, model); pool.addPolicy(policy); Asserts.succeedsEventually( MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { public void run() { assertEquals(model.getContainerWorkrates(), ImmutableMap.of(containerA, 10d)); } }); }
public WebAppMonitor waitForAtLeastOneAttempt(Duration timeout) { Asserts.succeedsEventually( MutableMap.of("timeout", timeout), new Runnable() { @Override public void run() { Assert.assertTrue(getAttempts() >= 1); } }); return this; }
@Override public void stop() { // Don't `kill -9`, as that doesn't stop the worker processes newScript(MutableMap.of("usePidFile", false), STOPPING) .body .append( format("cd %s", getRunDir()), format("export PID=`cat %s`", getPidFile()), "test -n \"$PID\" || exit 0", sudoIfPrivilegedPort(getHttpPort(), "kill $PID")) .execute(); }
public static void aggregateOpenGammaServerSensors(Entity cluster) { List<? extends List<? extends AttributeSensor<? extends Number>>> summingEnricherSetup = ImmutableList.of( ImmutableList.of(PROCESSING_TIME_PER_SECOND_LAST, PROCESSING_TIME_PER_SECOND_LAST), ImmutableList.of( PROCESSING_TIME_PER_SECOND_IN_WINDOW, PROCESSING_TIME_PER_SECOND_IN_WINDOW), ImmutableList.of(VIEW_PROCESSES_COUNT, VIEW_PROCESSES_COUNT), ImmutableList.of( PROCESS_CPU_TIME_FRACTION_IN_WINDOW, PROCESS_CPU_TIME_FRACTION_IN_WINDOW)); for (List<? extends AttributeSensor<? extends Number>> es : summingEnricherSetup) { AttributeSensor<? extends Number> t = es.get(0); AttributeSensor<? extends Number> total = es.get(1); CustomAggregatingEnricher<?, ?> totaller = CustomAggregatingEnricher.newSummingEnricher( MutableMap.of("allMembers", true), t, total, null, null); cluster.addEnricher(totaller); } cluster.addEnricher( Enrichers.builder() .aggregating(PROCESSING_TIME_PER_SECOND_LAST) .fromMembers() .publishing(PROCESSING_TIME_PER_SECOND_LAST_PER_NODE) .computingAverage() .defaultValueForUnreportedSensors(null) .build()); cluster.addEnricher( Enrichers.builder() .aggregating(PROCESSING_TIME_PER_SECOND_IN_WINDOW) .fromMembers() .publishing(PROCESSING_TIME_PER_SECOND_IN_WINDOW_PER_NODE) .computingAverage() .defaultValueForUnreportedSensors(null) .build()); cluster.addEnricher( Enrichers.builder() .aggregating(PROCESS_CPU_TIME_FRACTION_IN_WINDOW) .fromMembers() .publishing(PROCESS_CPU_TIME_FRACTION_IN_WINDOW_PER_NODE) .computingAverage() .defaultValueForUnreportedSensors(null) .build()); cluster.addEnricher( Enrichers.builder() .aggregating(VIEW_PROCESSES_COUNT) .fromMembers() .publishing(VIEW_PROCESSES_COUNT_PER_NODE) .computingAverage() .defaultValueForUnreportedSensors(0) .build()); }
@Override public void undeploy(String targetName) { checkNotNull(targetName, "targetName"); targetName = FILENAME_TO_WEB_CONTEXT_MAPPER.convertDeploymentTargetNameToContext(targetName); // set it up so future nodes get the right wars if (!removeFromWarsByContext(this, targetName)) { DynamicTasks.submit( Tasks.warning( "Context " + targetName + " not known at " + this + "; attempting to undeploy regardless", null), this); } log.debug( "Undeploying " + targetName + " across cluster " + this + "; WARs now " + getConfig(WARS_BY_CONTEXT)); Iterable<CanDeployAndUndeploy> targets = Iterables.filter(getChildren(), CanDeployAndUndeploy.class); TaskBuilder<Void> tb = Tasks.<Void>builder() .parallel(true) .name( "Undeploy " + targetName + " across cluster (size " + Iterables.size(targets) + ")"); for (Entity target : targets) { tb.add( whenServiceUp( target, Effectors.invocation(target, UNDEPLOY, MutableMap.of("targetName", targetName)), "Undeploy " + targetName + " at " + target + " when ready")); } DynamicTasks.queueIfPossible(tb.build()).orSubmitAsync(this).asTask().getUnchecked(); // Update attribute Set<String> deployedWars = MutableSet.copyOf(getAttribute(DEPLOYED_WARS)); deployedWars.remove( FILENAME_TO_WEB_CONTEXT_MAPPER.convertDeploymentTargetNameToContext(targetName)); setAttribute(DEPLOYED_WARS, deployedWars); }
static boolean removeFromWarsByContext(Entity entity, String targetName) { targetName = FILENAME_TO_WEB_CONTEXT_MAPPER.convertDeploymentTargetNameToContext(targetName); // TODO a better way to do atomic updates, see comment above synchronized (entity) { Map<String, String> newWarsMap = MutableMap.copyOf(entity.getConfig(WARS_BY_CONTEXT)); String url = newWarsMap.remove(targetName); if (url == null) { return false; } ((EntityInternal) entity).setConfig(WARS_BY_CONTEXT, newWarsMap); return true; } }
/** @deprecated since 0.7; support for rebinding old-style entities is deprecated */ @Test public void testHandlesOldStyleEntity() throws Exception { MyOldStyleEntity origE = new MyOldStyleEntity(MutableMap.of("confName", "myval"), origApp); Entities.manage(origE); newApp = rebind(); MyOldStyleEntity newE = (MyOldStyleEntity) Iterables.find(newApp.getChildren(), EntityPredicates.idEqualTo(origE.getId())); assertEquals(newE.getConfig(MyOldStyleEntity.CONF_NAME), "myval"); }
private void assertHasEventEventually( final Sensor<?> sensor, final Predicate<Object> componentPredicate, final Predicate<? super CharSequence> descriptionPredicate) { Asserts.succeedsEventually( MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { @Override public void run() { assertHasEvent(sensor, componentPredicate, descriptionPredicate); } }); }
public static BasicLocationMemento.Builder newLocationMementoBuilder(Location location) { BasicLocationMemento.Builder builder = BasicLocationMemento.builder(); Set<String> nonPersistableFlagNames = MutableMap.<String, Object>builder() .putAll(FlagUtils.getFieldsWithFlagsWithModifiers(location, Modifier.TRANSIENT)) .putAll(FlagUtils.getFieldsWithFlagsWithModifiers(location, Modifier.STATIC)) .put("id", String.class) .filterValues(Predicates.not(Predicates.instanceOf(ConfigKey.class))) .build() .keySet(); Map<String, Object> persistableFlags = MutableMap.<String, Object>builder() .putAll( FlagUtils.getFieldsWithFlagsExcludingModifiers( location, Modifier.STATIC ^ Modifier.TRANSIENT)) .removeAll(nonPersistableFlagNames) .build(); ConfigBag persistableConfig = new ConfigBag() .copy(((AbstractLocation) location).getLocalConfigBag()) .removeAll(nonPersistableFlagNames); builder.type = location.getClass().getName(); builder.typeClass = location.getClass(); builder.id = location.getId(); builder.displayName = location.getDisplayName(); builder.copyConfig(persistableConfig); builder.locationConfig.putAll(persistableFlags); Location parentLocation = location.getParent(); builder.parent = (parentLocation != null) ? parentLocation.getId() : null; for (Location child : location.getChildren()) { builder.children.add(child.getId()); } return builder; }