/**
* Copyright 2013-2017 Linagora, Université Joseph Fourier, Floralis
*
* The present code is developed in the scope of the joint LINAGORA -
* Université Joseph Fourier - Floralis research program and is designated
* as a "Result" pursuant to the terms and conditions of the LINAGORA
* - Université Joseph Fourier - Floralis research program. Each copyright
* holder of Results enumerated here above fully & independently holds complete
* ownership of the complete Intellectual Property rights applicable to the whole
* of said Results, and may freely exploit it in any manner which does not infringe
* the moral rights of the other copyright holders.
*
* 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
*
* 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 net.roboconf.core.model.beans;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArraySet;
import net.roboconf.core.model.helpers.InstanceHelpers;
/**
* An instance object represents a running component instance.
* @author Vincent Zurczak - Linagora
*/
public class Instance implements Serializable {
/**
* A constant to store the IP address in {@link #data}.
* <p>Storing this information in a scoped instance is enough.</p>
*/
public static final String IP_ADDRESS = "ip.address";
/**
* A constant to store the machine ID in {@link #data}.
* <p>Storing this information in a scoped instance is enough.</p>
*/
public static final String MACHINE_ID = "machine.id";
/**
* A constant to store in {@link #data} and that indicates a scoped instance was "taken" by a target handler.
* <p>Storing this information in a scoped instance is enough.</p>
* <p>
* When a scoped instance is still NOT_DEPLOYED but that this key is present
* in {@link #data}, it means the creation of a machine was acknowledged by the
* DM but that the processing has not yet started for this instance.
* </p>
* <p>
* Such situations could occur when a user starts a lot of scoped instances
* at once.
* </p>
*/
public static final String TARGET_ACQUIRED = "target.acquired";
/**
* A constant to store the application name in {@link #data}.
* <p>Storing this information in a scoped instance is enough.</p>
*/
public static final String APPLICATION_NAME = "application.name";
/**
* A constant to store the last problem in {@link #data}.
* <p>Storing this information in a scoped instance is enough.</p>
*/
public static final String LAST_PROBLEM = "last.problem";
/**
* A constant to indicate a scoped instance is ready for post-configuration.
* <p>
* To set only once a target handler has (successfully) completed its configuration process.
* </p>
* <p>
* Storing this information in a scoped instance is enough.
* </p>
*/
public static final String READY_FOR_CFG_MARKER = "ready.for.local.script.configuation";
/**
* A constant to store the timestamp of the first heart beat received for this instance.
* <p>
* To be stored in {@link #data}.
* Storing this information in a scoped instance is enough.
* </p>
* <p>
* If an instance goes into the PROBLEM state, and that a new heart beat comes after, then
* we should overwrite this information.
* </p>
*/
public static final String RUNNING_FROM = "running.from";
private static final long serialVersionUID = -3320865356277185064L;
private String name;
private Component component;
private Instance parent;
private final Collection<Instance> children = new CopyOnWriteArraySet<> ();
private InstanceStatus status = InstanceStatus.NOT_DEPLOYED;
public final Collection<String> channels = new HashSet<> ();
public final Map<String,String> overriddenExports = new HashMap<> ();
// Data can be accessed through several threads and for various reasons.
// ConcurrentHashMap does not accept null values. We could wrap such a map
// in a method or in a sub-class to prevent these NPE, but a synchronized map
// should be enough and should prevent unpredictable reactions.
public final Map<String,String> data = Collections.synchronizedMap( new LinkedHashMap<String,String>( 0 ));
// At runtime, imported variables are grouped by prefix.
// The prefix is a component or a facet name.
private final Map<String,Collection<Import>> variablePrefixToImports = new TreeMap<> ();
/**
* Constructor.
*/
public Instance() {
// nothing
}
/**
* Constructor.
* @param name the instance name
*/
public Instance( String name ) {
this.name = name;
}
/**
* @return the name
*/
public String getName() {
return this.name;
}
/**
* @param name the name to set
*/
public void setName( String name ) {
this.name = name;
}
/**
* @return the status
*/
public synchronized InstanceStatus getStatus() {
return this.status;
}
/**
* @param status the status to set
*/
public synchronized void setStatus( InstanceStatus status ) {
this.status = status;
}
/**
* @return the component
*/
public Component getComponent() {
return this.component;
}
/**
* @param component the component to set
*/
public void setComponent( Component component ) {
this.component = component;
}
/**
* @return the parent
*/
public Instance getParent() {
return this.parent;
}
/**
* @param parent the parent to set
*/
public void setParent( Instance parent ) {
this.parent = parent;
}
/**
* @return the children
*/
public Collection<Instance> getChildren() {
return this.children;
}
@Override
public int hashCode() {
return InstanceHelpers.computeInstancePath( this ).hashCode();
}
@Override
public boolean equals( Object obj ) {
return obj instanceof Instance
&& InstanceHelpers.haveSamePath( this, (Instance) obj);
}
@Override
public String toString() {
return this.name;
}
/**
* Sets the name in a chain approach.
*/
public Instance name( String name ) {
this.name = name;
return this;
}
/**
* Sets the component in a chain approach.
*/
public Instance component( Component component ) {
this.component = component;
return this;
}
/**
* Sets the parent in a chain approach.
*/
public Instance parent( Instance parent ) {
this.parent = parent;
return this;
}
/**
* Adds a channel in a chain approach.
*/
public Instance channel( String channel ) {
this.channels.add( channel );
return this;
}
/**
* Sets the status in a chain approach.
*/
public synchronized Instance status( InstanceStatus status ) {
this.status = status;
return this;
}
/**
* @return the imports (not null, key: component or facet name, value: the associated imports)
*/
public Map<String,Collection<Import>> getImports() {
return this.variablePrefixToImports;
}
/**
* @author Noël - LIG
*/
public enum InstanceStatus implements Serializable {
NOT_DEPLOYED( true ),
DEPLOYING( false ),
DEPLOYED_STOPPED( true ),
UNRESOLVED( true ),
WAITING_FOR_ANCESTOR( true ),
STARTING( false ),
DEPLOYED_STARTED( true ),
STOPPING( false ),
UNDEPLOYING( false ),
PROBLEM( false );
private final boolean stable;
/**
* Constructor.
* @param stable
*/
InstanceStatus( boolean stable ) {
this.stable = stable;
}
/**
* A secured alternative to {@link InstanceStatus#valueOf(String)}.
* @param s a string (can be null)
* @return the associated runtime status, or {@link InstanceStatus#NOT_DEPLOYED} otherwise
*/
public static InstanceStatus whichStatus( String s ) {
InstanceStatus result = exactStatus( s );
return result == null ? NOT_DEPLOYED : result;
}
/**
* A secured alternative to {@link InstanceStatus#valueOf(String)}.
* @param s a string (can be null)
* @return the associated runtime status, or null otherwise
*/
public static InstanceStatus exactStatus( String s ) {
InstanceStatus result = null;
for( InstanceStatus status : InstanceStatus.values()) {
if( status.toString().equalsIgnoreCase( s )) {
result = status;
break;
}
}
return result;
}
/**
* A secured way to determine whether a string designates an existing status.
* @param s a string (can be null)
* @return true if it is a state name, false otheriwse
*/
public static boolean isValidState( String s ) {
boolean valid = false;
for( InstanceStatus status : InstanceStatus.values()) {
if( status.toString().equalsIgnoreCase( s )) {
valid = true;
break;
}
}
return valid;
}
/**
* @return the stable
*/
public boolean isStable() {
return this.stable;
}
}
}