Skip to content

Commit

Permalink
Ensemble logging
Browse files Browse the repository at this point in the history
  • Loading branch information
filipekt committed Dec 28, 2014
1 parent 7b4d937 commit cf94f80
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 4 deletions.
229 changes: 229 additions & 0 deletions jdeeco-core/src/cz/cuni/mff/d3s/deeco/task/EnsembleLogger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package cz.cuni.mff.d3s.deeco.task;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import cz.cuni.mff.d3s.deeco.knowledge.ReadOnlyKnowledgeManager;
import cz.cuni.mff.d3s.deeco.logging.Log;
import cz.cuni.mff.d3s.deeco.model.runtime.api.EnsembleController;
import cz.cuni.mff.d3s.deeco.scheduler.Scheduler;

/**
* Main usage of this class: logging of the changes in ensemble membership
* @see {@link EnsembleLogger#logEvent(EnsembleController, ReadOnlyKnowledgeManager, Scheduler, boolean)}
* @author Tomas Filipek
*/
class EnsembleLogger {

/**
* Encapsulates three pieces of data: ensemble name, coordinator ID and member ID.
* It is used to index the information whether the membership condition holds for the
* given triplet.
* @author Tomas Filipek
*/
private static class MembershipRecord {

/**
* Identification of the ensemble
*/
private final String ensembleName;

/**
* Identification of the coordinator
*/
private final String coordinatorID;

/**
* Identification of the member
*/
private final String memberID;

/**
* @param ensembleName Identification of the ensemble
* @param coordinatorID Identification of the coordinator
* @param memberID Identification of the member
*/
public MembershipRecord(String ensembleName, String coordinatorID, String memberID) {
this.ensembleName = ensembleName;
this.coordinatorID = coordinatorID;
this.memberID = memberID;
}

/**
* The produced hash code depends on precisely the following fields:
* {@link MembershipRecord#ensembleName} ,
* {@link MembershipRecord#coordinatorID} ,
* {@link MembershipRecord#memberID}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime
* result
+ ((coordinatorID == null) ? 0 : coordinatorID
.hashCode());
result = prime
* result
+ ((ensembleName == null) ? 0 : ensembleName.hashCode());
result = prime * result
+ ((memberID == null) ? 0 : memberID.hashCode());
return result;
}

/**
* The equality holds if and only if the following fields are equal amid the two instances:
* {@link MembershipRecord#ensembleName} ,
* {@link MembershipRecord#coordinatorID} ,
* {@link MembershipRecord#memberID}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof EnsembleLogger.MembershipRecord)) {
return false;
}
EnsembleLogger.MembershipRecord other = (EnsembleLogger.MembershipRecord) obj;
if (coordinatorID == null) {
if (other.coordinatorID != null) {
return false;
}
} else if (!coordinatorID.equals(other.coordinatorID)) {
return false;
}
if (ensembleName == null) {
if (other.ensembleName != null) {
return false;
}
} else if (!ensembleName.equals(other.ensembleName)) {
return false;
}
if (memberID == null) {
if (other.memberID != null) {
return false;
}
} else if (!memberID.equals(other.memberID)) {
return false;
}
return true;
}
}

/**
* The event log will be saved to this file.
*/
private final File ensembleEventLog = new File("logs/ensembles.xml");

/**
* An instance of {@link Writer} that writes into {@link EnsembleLogger#ensembleEventLog}.
* @see {@link EnsembleLogger#ensembleEventLog}
*/
private final OutputStreamWriter out;

/**
* <p> Used to remember the last encountered value of the membership condition, computed on the
* input objects which are given as an instance of {@link MembershipRecord}. </p>
* <p> <b>Example:</b><br/>
* key = triplet of (ensemble E, coordinator C, member M)<br/>
* value = true (meaning that when last checked, M was in ensemble E, where C was the coordinator)
* </p>
*/
private final Map<EnsembleLogger.MembershipRecord, Boolean> membershipRecords = new HashMap<>();

/**
* Initializes the output streams
*/
private EnsembleLogger() {
OutputStreamWriter osw;
try {
FileOutputStream fos = new FileOutputStream(ensembleEventLog);
osw = new OutputStreamWriter(fos);
} catch (Exception ex) {
osw = null;
}
out = osw;
}

/**
* The singleton instance of this class
*/
private static final EnsembleLogger INSTANCE = new EnsembleLogger();

/**
* @return The singleton instance of this class
* @see {@link EnsembleLogger#INSTANCE}
*/
public static EnsembleLogger getInstance() {
return INSTANCE;
}

/**
* Logs the information about ensemble membership to the output file. It remembers the
* previous value, so the output is produced only if it has changed from the last time.
* @param ensembleController Controller of the {@link EnsembleTask} where this method is called.
* @param shadowKnowledgeManager Knowledge of the other component
* @param scheduler Used to get the current time
* @param membership The current value of the membership condition
*/
public void logEvent(EnsembleController ensembleController, ReadOnlyKnowledgeManager shadowKnowledgeManager,
Scheduler scheduler, boolean membership){
long timeSeconds = scheduler.getCurrentMilliseconds() / 1000L;
String ensembleName = ensembleController.getEnsembleDefinition().getName();
String coordinatorID = ensembleController.getComponentInstance().getKnowledgeManager().getId();
String memberID = shadowKnowledgeManager.getId();
EnsembleLogger.MembershipRecord mr = new MembershipRecord(ensembleName, coordinatorID, memberID);
boolean oldMembership = membershipRecords.get(mr) == null ? false : membershipRecords.get(mr);
if (oldMembership != membership){
membershipRecords.put(mr, Boolean.valueOf(membership));
String[] attrNames = new String[] {"coordinator", "member", "membership", "ensemble", "time"};
String[] attrValues = new String[] {coordinatorID, memberID, Boolean.toString(membership), ensembleName, Long.toString(timeSeconds)};
String elementText = getElementText("event", attrNames, attrValues);
try {
out.append(elementText);
out.flush();
} catch (IOException | NullPointerException ex) {
Log.e("Could not log an ensemble event to " + ensembleEventLog.getAbsolutePath().toString());
}
}
}

/**
* Builds a textual representation of an XML element defined by the parameters.
* A newline is added to the end of the element.
* @param name Name of the element
* @param attributeNames Names of the attributes, in the order as they will appear
* @param attributeValues Values of the attributes, in the same order as the names
* @return A textual representation of an XML element defined by the parameters
*/
private String getElementText(String name, String[] attributeNames, String[] attributeValues){
if ((name == null) || name.isEmpty() || (attributeNames == null) ||
(attributeValues == null) || (attributeNames.length != attributeValues.length)){
return null;
}
StringBuilder sb = new StringBuilder();
sb.append("<");
sb.append(name);
sb.append(" ");
for (int i = 0; i < attributeNames.length; i++){
String attrName = attributeNames[i];
String attrValue = attributeValues[i];
sb.append(attrName);
sb.append("=\"");
sb.append(attrValue);
sb.append("\" ");
}
sb.append("/>");
sb.append("\n");
return sb.toString();
}
}
14 changes: 10 additions & 4 deletions jdeeco-core/src/cz/cuni/mff/d3s/deeco/task/EnsembleTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

import cz.cuni.mff.d3s.deeco.knowledge.ChangeSet;
import cz.cuni.mff.d3s.deeco.knowledge.KnowledgeManager;
import cz.cuni.mff.d3s.deeco.knowledge.KnowledgeUpdateException;
import cz.cuni.mff.d3s.deeco.knowledge.ShadowKnowledgeManagerRegistry;
import cz.cuni.mff.d3s.deeco.knowledge.KnowledgeNotFoundException;
import cz.cuni.mff.d3s.deeco.knowledge.KnowledgeUpdateException;
import cz.cuni.mff.d3s.deeco.knowledge.ReadOnlyKnowledgeManager;
import cz.cuni.mff.d3s.deeco.knowledge.ShadowKnowledgeManagerRegistry;
import cz.cuni.mff.d3s.deeco.knowledge.ShadowsTriggerListener;
import cz.cuni.mff.d3s.deeco.knowledge.TriggerListener;
import cz.cuni.mff.d3s.deeco.knowledge.ValueSet;
Expand All @@ -25,8 +25,8 @@
import cz.cuni.mff.d3s.deeco.model.runtime.api.ParameterDirection;
import cz.cuni.mff.d3s.deeco.model.runtime.api.TimeTrigger;
import cz.cuni.mff.d3s.deeco.model.runtime.api.Trigger;
import cz.cuni.mff.d3s.deeco.model.runtime.meta.RuntimeMetadataFactory;
import cz.cuni.mff.d3s.deeco.model.runtime.impl.TriggerImpl;
import cz.cuni.mff.d3s.deeco.model.runtime.meta.RuntimeMetadataFactory;
import cz.cuni.mff.d3s.deeco.runtime.ArchitectureObserver;
import cz.cuni.mff.d3s.deeco.scheduler.Scheduler;
import cz.cuni.mff.d3s.deeco.task.KnowledgePathHelper.KnowledgePathAndRoot;
Expand Down Expand Up @@ -495,14 +495,20 @@ public void invoke(Trigger trigger) throws TaskInvocationException {
}
}
}

private void evaluateMembershipAndPerformExchange(ReadOnlyKnowledgeManager shadowKnowledgeManager) throws TaskInvocationException {
// Invoke the membership condition and if the membership condition returned true, invoke the knowledge exchange
boolean membership;
if (checkMembership(PathRoot.COORDINATOR, shadowKnowledgeManager)) {
architectureObserver.ensembleFormed(ensembleController.getEnsembleDefinition(), ensembleController.getComponentInstance(),
ensembleController.getComponentInstance().getKnowledgeManager().getId(),shadowKnowledgeManager.getId());
performExchange(PathRoot.COORDINATOR, shadowKnowledgeManager);
membership = true;
} else {
membership = false;
}
EnsembleLogger.getInstance().logEvent(ensembleController, shadowKnowledgeManager, scheduler, membership);

// Do the same with the roles exchanged
if (checkMembership(PathRoot.MEMBER, shadowKnowledgeManager)) {
architectureObserver.ensembleFormed(ensembleController.getEnsembleDefinition(), ensembleController.getComponentInstance(),
Expand Down

0 comments on commit cf94f80

Please sign in to comment.