blob: bc91c6b9e69245035c839881d84896e2b6a5e8ef [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2009 Germany and Technical University Berlin, Germany.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.internal.osgi.weaving;
import static org.eclipse.objectteams.otequinox.AspectPermission.DENY;
import static org.eclipse.objectteams.otequinox.AspectPermission.GRANT;
import static org.eclipse.objectteams.otequinox.AspectPermission.UNDEFINED;
import static org.eclipse.objectteams.otequinox.TransformerPlugin.log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import org.eclipse.core.internal.runtime.InternalPlatform;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.BaseBundle;
import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.TeamBinding;
import org.eclipse.objectteams.otequinox.ActivationKind;
import org.eclipse.objectteams.otequinox.AspectBindingRequestAnswer;
import org.eclipse.objectteams.otequinox.AspectPermission;
import org.eclipse.objectteams.otequinox.Constants;
import org.eclipse.objectteams.otequinox.IAspectRequestNegotiator;
import org.eclipse.objectteams.otequinox.TransformerPlugin;
import org.eclipse.osgi.internal.hookregistry.HookConfigurator;
import org.eclipse.osgi.service.datalocation.Location;
import org.objectteams.Team;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
/**
* Manage permissions of aspect bundles requesting to apply aspectBindings and forcedExports.
* The following pieces of information are checked:
* <ul>
* <li>properties set in installation-wide config.ini or as command line args (handled by {@link HookConfigurator} (plus internal class OTStorageHook))</li>
* <li>defaults set per workspace (file negotiationDefaults.txt)</li>
* <li>individual GRANT/DENY per workspace (files grantedForcedExports.txt, deniedForcedExports.txt)</li>
* <li>answers from registered negotiators (extension point org.eclipse.objectteams.otequinox.aspectBindingNegotiators, see {@link IAspectRequestNegotiator})</li>
* </ul>
*
* <p>
* The final answer for a given request is combined from all sources where the priority of any {@link #DENY} answer is highest,
* of {@link #UNDEFINED} is lowest.
* </p>
* <p>
* If a negotiator has determined a decision and its answer has the <code>persistent</code> flag set,
* this particular aspect permission is stored as per-workspace configuration.
* </p>
* @author stephan
* @since 1.2.6
*/
@SuppressWarnings("restriction")
@NonNullByDefault
public class AspectPermissionManager {
// property names for default configuration:
private static final String FORCED_EXPORT_DEFAULT = "forced.export.default";
private static final String ASPECT_BINDING_DEFAULT = "aspect.binding.default";
// workspace files where negotiation configuration is stored:
private static final String NEGOTIATION_DEFAULTS_FILE = "negotiationDefaults.txt";
private static final String GRANTED_FORCED_EXPORTS_FILE = "grantedForcedExports.txt";
private static final String DENIED_FORCED_EXPORTS_FILE = "deniedForcedExports.txt";
private static final String GRANTED_TEAMS_FILE = "grantedTeams.txt";
private static final String DENIED_TEAMS_FILE = "deniedTeams.txt";
// set of aspect plug-ins for which some permission has been denied:
private Set<String> deniedAspects = new HashSet<String>();
// default permission for aspect bindings:
private AspectPermission defaultAspectBindingPermission = GRANT;
// default permission for forced exports:
private AspectPermission defaultForcedExportPermission = UNDEFINED; // not yet granted, but open for receiving a GRANT
// for negotiation of aspect binding requests (incl. forced export):
private List<IAspectRequestNegotiator> negotiators = new ArrayList<IAspectRequestNegotiator>();
// collect all forced exports (denied/granted), granted should balance to an empty structure.
// structure is: aspect-id -> (base bundle x base package)*
private Map<String, List<@NonNull String[]>> deniedForcedExportsByAspect= new HashMap<>();
private Map<String, List<@NonNull String[]>> grantedForcedExportsByAspect= new HashMap<>();
// key is aspectId+"->"+baseId, value is array of team names
private Map<String, Set<String>> deniedTeamsByAspectBinding = new HashMap<String, Set<String>>();
private Map<String, Set<String>> grantedTeamsByAspectBinding = new HashMap<String, Set<String>>();
// the workspace directory for storing the state of this plugin
@Nullable private IPath otequinoxState;
// back link needed for accessing the state location:
private Bundle transformerBundle;
// helper instance needed to stop bundles by name
@SuppressWarnings("deprecation")
private org.osgi.service.packageadmin.@Nullable PackageAdmin packageAdmin;
private ForcedExportsDelegate forcedExportsDelegate;
public AspectPermissionManager(Bundle bundle,
@SuppressWarnings("deprecation") org.osgi.service.packageadmin.@Nullable PackageAdmin packageAdmin)
{
this.transformerBundle = bundle;
this.packageAdmin = packageAdmin;
this.forcedExportsDelegate = new ForcedExportsDelegate();
}
/* local cache for isReady(): */
private boolean isWaitingForLocation = true;
/** Before using this permission manager a client must check whether we're ready (instance location set). */
public boolean isReady() {
if (!isWaitingForLocation)
return true;
try {
InternalPlatform platform = InternalPlatform.getDefault();
Location instanceLocation = platform.getInstanceLocation();
if (!instanceLocation.isSet())
return false; // not yet capable
this.isWaitingForLocation = false;
fetchAspectBindingPermssionsFromWorkspace();
} catch (NoClassDefFoundError ncdfe) {
log(IStatus.WARNING, "Optional class InternalPlatform not found, cannot access workspace location");
this.isWaitingForLocation = false;
return true;
}
if (!this.obligations.isEmpty())
for (Runnable job : this.obligations)
job.run();
return true;
}
/**
* Fetch stored permissions from this plugin's workspace state.
*
* @pre instance location should be set (see {@link #isReady()}),
* otherwise will silently return without accessing workspace settings.
*/
private void fetchAspectBindingPermssionsFromWorkspace() {
try {
IPath state = InternalPlatform.getDefault().getStateLocation(this.transformerBundle, true);
this.otequinoxState = state;
internalFetchAspectBindingPermssionsFromWorkspace(state);
} catch (NoClassDefFoundError ncdfe) {
log(IStatus.WARNING, "Optional class InternalPlatform not found, cannot access workspace location");
return;
}
}
/** Load extensions for EP org.eclipse.objectteams.otequinox.aspectBindingNegotiators. */
public void loadAspectBindingNegotiators(IExtensionRegistry extensionRegistry) {
IConfigurationElement[] aspectBindingNegotiatorsConfigs = extensionRegistry.getConfigurationElementsFor(
Constants.TRANSFORMER_PLUGIN_ID, Constants.ASPECT_NEGOTIATOR_EXTPOINT_ID);
for (int i = 0; i < aspectBindingNegotiatorsConfigs.length; i++) {
IConfigurationElement currentNegotiatorConfig = aspectBindingNegotiatorsConfigs[i];
try {
Object negotiator = currentNegotiatorConfig.createExecutableExtension("class");
if (negotiator != null)
this.negotiators.add(((IAspectRequestNegotiator)negotiator));
} catch (CoreException e) {
log(e, "Failed to instantiate extension "+currentNegotiatorConfig);
}
}
}
/** Delegatee of internal API {@link TransformerPlugin#isDeniedAspectPlugin(String)}. */
public boolean isDeniedAspectPlugin(String symbolicName) {
return this.deniedAspects.contains(symbolicName);
}
/**
* Check whether a given aspect requests forced exports from base,
* and whether these requests are granted/denied by checking all available sources.
*
* Clients should ask {@link #isReady()} (ie., instance location is set) before calling this method,
* otherwise workspace settings have to be silently ignored (any error should be signaled by client).
*
* @param aspectId symbolic name of the aspect bundle
* @param baseBundleId symbolic name of the bound base bundle
* @param forcedExports any forced exports requested in this aspect binding.
* @return whether all requests (if any) have been granted
*/
public boolean checkForcedExports(AspectBinding aspectBinding) {
switch (aspectBinding.forcedExportsPermission) {
case GRANT: return true;
case DENY: return false;
case UNDEFINED:
aspectBinding.forcedExportsPermission = internalCheckForcedExports(aspectBinding);
return aspectBinding.forcedExportsPermission == GRANT;
}
return true;
}
private AspectPermission internalCheckForcedExports(AspectBinding aspectBinding) {
IConfigurationElement[] forcedExports = aspectBinding.forcedExports;
if (forcedExports.length == 0)
return GRANT;
String aspectId = aspectBinding.aspectPlugin;
String baseBundleId = aspectBinding.basePluginName;
List<String[]> deniedForcedExports = getConfiguredForcedExports(aspectId, DENY, deniedForcedExportsByAspect);
List<String[]> grantedForcedExports= getConfiguredForcedExports(aspectId, GRANT, grantedForcedExportsByAspect);
// iterate all requested forcedExports to search for a matching permission:
for (IConfigurationElement forcedExport : forcedExports) { // [0..1] (as defined in the schema)
String forcedExportsRequest = forcedExport.getValue();
if (forcedExportsRequest == null)
continue;
for (@NonNull String singleForcedExportRequest : forcedExportsRequest.split(","))
{
singleForcedExportRequest = singleForcedExportRequest.trim();
String[] listEntry;
boolean grantReported = false;
AspectPermission negotiatedPermission = this.defaultForcedExportPermission;
// DENY by default?
if (negotiatedPermission == DENY) {
log(IStatus.ERROR, "Default denial of forced export regarding package "+singleForcedExportRequest+
" from bundle "+baseBundleId+" as requested by bundle "+aspectId+"; bundle not activated");
this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
return DENY; // NOPE!
}
// DENY from configuration?
listEntry = findRequestInList(baseBundleId, singleForcedExportRequest, deniedForcedExports);
if (listEntry != null) {
log(IStatus.ERROR, "Explicit denial of forced export regarding package "+singleForcedExportRequest+
" from bundle "+baseBundleId+" as requested by bundle "+aspectId+"; bundle not activated");
this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
return DENY; // NOPE!
}
// GRANT from configuration?
listEntry = findRequestInList(baseBundleId, singleForcedExportRequest, grantedForcedExports);
if (listEntry != null) {
log(IStatus.INFO, "Forced export granted for "+aspectId+": "+singleForcedExportRequest+" (from bundle "+baseBundleId+")");
grantReported = true;
grantedForcedExports.remove(listEntry);
negotiatedPermission = GRANT;
}
// default and persistent configuration did not DENY, proceed to the negotiators:
boolean shouldPersist = false;
for (IAspectRequestNegotiator negotiator : this.negotiators) {
AspectBindingRequestAnswer answer = negotiator.checkForcedExport(aspectId, baseBundleId, singleForcedExportRequest, negotiatedPermission);
if (answer.permission.compareTo(negotiatedPermission) > 0) // increasing priority of answer?
{
shouldPersist = answer.persistent;
negotiatedPermission = answer.permission;
// locally store as default for subsequent requests (not persistent, see below):
if (answer.allRequests)
this.defaultForcedExportPermission = negotiatedPermission;
if (negotiatedPermission == DENY)
break; // end of discussion.
}
}
// make decision persistent?
if (shouldPersist && negotiatedPermission != UNDEFINED)
// FIXME(SH): handle "allRequests":
persistForcedExportsAnswer(aspectId, baseBundleId, singleForcedExportRequest, negotiatedPermission);
// report:
if (negotiatedPermission == GRANT) {
if (!grantReported)
log(IStatus.INFO, "Negotiation granted forced export for "+aspectId+
": "+singleForcedExportRequest+" (from bundle "+baseBundleId+')');
} else {
String verb = "did not grant";
if (negotiatedPermission == DENY)
verb = "denied";
log(IStatus.ERROR, "Negotiation "+verb+" forced export for "+aspectId+
": "+singleForcedExportRequest+" (from bundle "+baseBundleId+")"+
". Aspect is not activated.");
this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
return DENY; // don't install illegal aspect
}
}
}
if (!grantedForcedExports.isEmpty())
reportUnmatchForcedExports(aspectId, grantedForcedExports);
return GRANT;
}
/**
* Get the forced exports configured for a given aspect bundle with permission <code>perm</code>.
* Consult {@link HookConfigurator} and store the result in <code>map</code>.
*
* @param aspectId symbolic name of the aspect in focus
* @param perm are we asking about DENY or GRANT?
* @param map in/out param for storing results from OTStorageHook
* @return list of pairs (base bundle x base package)
*/
private List<@NonNull String[]> getConfiguredForcedExports( String aspectId,
AspectPermission perm,
Map<String, List<@NonNull String[]>> map)
{
List<@NonNull String[]> forcedExports= map.get(aspectId);
if (forcedExports == null) {
// fetch declarations from config.ini or other locations.
forcedExports= forcedExportsDelegate.getForcedExportsByAspect(aspectId, perm);
map.put(aspectId, forcedExports);
}
return forcedExports;
}
private String @Nullable[] findRequestInList(String baseBundleId, String basePackage, List<String[]> list) {
for (String[] singleExport : list)
if ( singleExport[0].equals(baseBundleId)
&& singleExport[1].equals(basePackage))
{
return singleExport;
}
return null;
}
/**
* If the structure of grantedForcedExports is not empty we have mismatches between forced-export declarations.
* Report these mismatches as warnings.
*/
void reportUnmatchForcedExports(String aspectId, List<String[]> unmatchedForcedExports)
{
for (String[] export: unmatchedForcedExports) {
String baseId = export[0];
String pack = export[1];
log(IStatus.WARNING, "Aspect "+aspectId+
" does not declare forced export of package "+
pack+" from bundle "+baseId+
" as declared in config.ini (or system property)");
}
}
/* Simple strategy to append a forced export to a file (existing or to be created). */
private void persistForcedExportsAnswer(String aspectId, String baseBundleId, String basePackage, AspectPermission negotiatedPermission)
{
IPath state = this.otequinoxState;
if (state == null) {
log(IStatus.ERROR, "Can't persist forcedExports permission, no workspace location accessable.");
return;
}
try {
String fileName = (negotiatedPermission == DENY) ? DENIED_FORCED_EXPORTS_FILE : GRANTED_FORCED_EXPORTS_FILE;
IPath forcedExportsPath = state.append(fileName);
File forcedExportsFile = new File(forcedExportsPath.toOSString());
if (!forcedExportsFile.exists())
forcedExportsFile.createNewFile();
try (FileWriter writer = new FileWriter(forcedExportsFile, true)) { // FIXME(SH): consider merge (after decision about file format)
writer.append('\n');
writer.append(baseBundleId);
writer.append("\n[\n\t");
writer.append(basePackage);
writer.append(";x-friends:=\"");
writer.append(aspectId);
writer.append("\"\n]\n");
writer.flush();
}
} catch (IOException ioe) {
log(ioe, "Failed to persist negotiation result");
}
}
/**
* Check the permissions for all given teams.
* @param teamsForBase the teams to check
* @return the set of denied teams
*/
Set<TeamBinding> checkAspectPermissionDenial(Collection<TeamBinding> teamsForBase)
{
Set<TeamBinding> deniedTeams = new HashSet<TeamBinding>();
for (TeamBinding teamForBase : teamsForBase) {
AspectBinding aspectBinding = teamForBase.getAspectBinding();
String aspectBundleName = aspectBinding.aspectPlugin;
if (aspectBinding.hasBeenDenied) {
deniedTeams.add(teamForBase);
} else {
if (!checkForcedExports(aspectBinding)) {
deniedTeams.add(teamForBase);
stopAspectBundle(aspectBinding, aspectBundleName, "requests unconfirmed forced export(s).");
} else if (!checkTeamBinding(aspectBundleName, aspectBinding.basePluginName, teamForBase)) {
deniedTeams.add(teamForBase);
stopAspectBundle(aspectBinding, aspectBundleName, "requests unconfirmed aspect binding(s).");
}
}
}
return deniedTeams;
}
void stopAspectBundle(AspectBinding aspectBinding, String aspectBundleName, String reason) {
try {
aspectBinding.hasBeenDenied = true;
Bundle aspectBundle = aspectBinding.aspectBundle;
if (aspectBundle != null) {
aspectBundle.stop();
log(IStatus.ERROR, "Stopped bundle "+aspectBundleName+" which "+reason);
} else {
log(IStatus.ERROR, "Cannot stop aspect bundle "+aspectBundleName);
}
} catch (Throwable t) { // don't let the aspect bundle get by by throwing an unexpected exception!
log(t, "Failed to stop bundle "+aspectBundleName+" which "+reason);
}
}
/**
* Check permission for the aspect binding of one specific team.
*
* Clients should ask {@link #isReady()} (ie., instance location is set) before calling this method,
* otherwise workspace settings have to be silently ignored (any error should be signaled by client).
*
* @param aspectBundleId
* @param baseBundleId
* @param teamBinding
* @return whether this team is permitted to adapt classes from the given base bundle.
*/
boolean checkTeamBinding(String aspectBundleId, String baseBundleId, TeamBinding teamBinding) {
if (teamBinding.checkedPermission != null)
return teamBinding.checkedPermission == AspectPermission.GRANT;
boolean isGranted = internalCheckTeamBinding(aspectBundleId, baseBundleId, teamBinding.teamName);
teamBinding.checkedPermission = isGranted ? AspectPermission.GRANT : AspectPermission.DENY;
return isGranted;
}
boolean internalCheckTeamBinding(String aspectBundleId, String baseBundleId, String teamClass)
{
boolean shouldReportGrant = false; // grant by default should not be reported
AspectPermission negotiatedPermission = this.defaultAspectBindingPermission;
// DENY by default?
if (negotiatedPermission == DENY) {
log(IStatus.ERROR, "Default denial of aspect binding regarding base bundle "+baseBundleId+
" as requested by bundle "+aspectBundleId+"; bundle not activated");
this.deniedAspects.add(aspectBundleId); // keep for answering the TransformerHook.
return false; // NOPE!
}
String key = aspectBundleId+"->"+baseBundleId;
// denied from configuration?
Set<String> deniedTeams = deniedTeamsByAspectBinding.get(key);
if (deniedTeams != null && !deniedTeams.isEmpty()) {
if (deniedTeams.contains(teamClass)) {
log(IStatus.ERROR, "Configured denial of aspect binding regarding base bundle "+baseBundleId+
" as requested by bundle "+aspectBundleId+"; bundle not activated");
deniedAspects.add(aspectBundleId);
return false;
}
}
// granted from configuration?
Set<String> grantedTeams = grantedTeamsByAspectBinding.get(key);
if (grantedTeams != null && grantedTeams.contains(teamClass)) {
negotiatedPermission = GRANT;
shouldReportGrant = true;
}
// default and persistent configuration did not DENY, proceed to the negotiators:
boolean shouldPersist = false;
String denyingNegotiator = null;
for (IAspectRequestNegotiator negotiator : this.negotiators) {
AspectBindingRequestAnswer answer = negotiator.checkAspectBinding(aspectBundleId, baseBundleId, teamClass, negotiatedPermission);
if (answer.permission.compareTo(negotiatedPermission) > 0) // increasing priority of answer?
{
shouldPersist = answer.persistent;
negotiatedPermission = answer.permission;
shouldReportGrant = negotiatedPermission == GRANT;
// locally store as default for subsequent requests:
if (answer.allRequests)
this.defaultAspectBindingPermission = negotiatedPermission; // FIXME: differentiate: apply to all / all of same aspect bundle
if (negotiatedPermission == DENY) {
denyingNegotiator = negotiator.getClass().getName();
break; // end of discussion.
}
}
}
// make decision persistent?
if (shouldPersist && negotiatedPermission != UNDEFINED)
persistTeamBindingAnswer(aspectBundleId, baseBundleId, teamClass, negotiatedPermission);
// report:
if (negotiatedPermission == GRANT) {
if (shouldReportGrant)
log(IStatus.INFO, "Negotiation granted aspect binding for "+aspectBundleId+
" to base bundle "+baseBundleId+" by means of team "+teamClass+'.');
} else {
String front = (negotiatedPermission == DENY)
? "Negotiator "+denyingNegotiator + " denied"
: "Negotiation did not grant";
log(IStatus.ERROR, front+" aspect binding for "+aspectBundleId+
" to base bundle "+baseBundleId+" by means of team "+teamClass+
". Aspect is not activated.");
this.deniedAspects.add(aspectBundleId); // keep for answering the TransformerPlugin.
return false; // don't install illegal aspect
}
return true;
}
List<Runnable> obligations = new ArrayList<Runnable>();
public void addBaseBundleObligations(final List<Team> teamInstances, final Collection<TeamBinding> teamClasses, final BaseBundle baseBundle) {
schedule(new Runnable() {
public void run() {
List<TeamBinding> teamsToRevert = new ArrayList<TeamBinding>();
// aspect bindings:
for (TeamBinding teamClass : teamClasses)
if (!checkTeamBinding(teamClass.getAspectBinding().aspectPlugin, baseBundle.bundleName, teamClass))
teamsToRevert.add(teamClass);
if (!teamsToRevert.isEmpty())
revert(teamsToRevert);
}
void revert(List<TeamBinding> teamsToRevert) {
try {
Set<Bundle> bundlesToStop = new HashSet<Bundle>();
for (TeamBinding teamClass : teamClasses) {
if (teamClass.getActivation() != ActivationKind.NONE) {
for (Team teamInstance : teamInstances)
if (teamInstance.getClass() == teamClass.teamClass)
teamInstance.deactivate(Team.ALL_THREADS);
// could also check if roles are present already ...
}
Bundle aspectBundle = teamClass.getAspectBinding().aspectBundle;
if (aspectBundle != null)
bundlesToStop.add(aspectBundle);
}
for (Bundle bundle : bundlesToStop) {
if ((bundle.getState() & (Bundle.STARTING|Bundle.ACTIVE)) != 0) {
log(IStatus.ERROR, "Stopping aspect bundle "+bundle.getSymbolicName()+" with denied aspect binding(s)");
bundle.stop();
}
}
} catch (Exception e) {
log(e, "Failed to revert aspect bundle with denied aspect bindings.");
}
}
});
}
void schedule(Runnable job) {
if (isReady()) // became ready since last query?
job.run();
else
synchronized(obligations) {
obligations.add(job);
}
}
void stopIllegalBundle(String symbolicName) {
String msgCore = "stop bundle "+symbolicName+" whose requests for forced exports have been denied";
@SuppressWarnings("deprecation")
org.osgi.service.packageadmin.PackageAdmin packAdmin = this.packageAdmin;
if (packAdmin == null) {
log(IStatus.ERROR, "Needing to "+msgCore+" but package admin is not available");
} else {
@SuppressWarnings("deprecation")
Bundle[] bundles = packAdmin.getBundles(symbolicName, null);
if (bundles == null)
log(IStatus.ERROR, "Needing to "+msgCore+" but bundle cannot be retrieved");
else
try {
bundles[0].stop();
} catch (BundleException e) {
log(e, "Failed to " + msgCore);
}
}
}
// ==== File I/O: ====
private void internalFetchAspectBindingPermssionsFromWorkspace(IPath state) {
// defaults:
IPath configFilePath = state.append(NEGOTIATION_DEFAULTS_FILE);
File configFile = new File(configFilePath.toOSString());
if (configFile.exists()) {
Properties props = new Properties();
try {
try (FileInputStream inStream = new FileInputStream(configFile)) {
props.load(inStream);
}
String value = (String) props.get(ASPECT_BINDING_DEFAULT);
if (value != null)
try {
defaultAspectBindingPermission = AspectPermission.valueOf(value);
} catch (IllegalArgumentException iae) {
defaultAspectBindingPermission = AspectPermission.DENY;
log(iae, "Cannot set default aspect permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
}
value = (String) props.get(FORCED_EXPORT_DEFAULT);
if (value != null)
try {
defaultForcedExportPermission = AspectPermission.valueOf(value);
} catch (IllegalArgumentException iae) {
defaultForcedExportPermission = AspectPermission.DENY;
log(iae, "Cannot set default forced exports permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
}
} catch (IOException ioex) {
log(ioex, "Failed to read configuration file "+configFilePath.toOSString());
}
} else {
try {
File stateDir = new File(state.toOSString());
if (!stateDir.exists())
stateDir.mkdirs();
configFile.createNewFile();
writeNegotiationDefaults(configFile);
} catch (IOException ioex) {
log(ioex, "Failed to create configuration file "+configFilePath.toOSString());
}
}
// configured grant / deny per team:
configFilePath = state.append(GRANTED_TEAMS_FILE);
configFile = new File(configFilePath.toOSString());
if (configFile.exists())
parseTeamPermissionFile(grantedTeamsByAspectBinding, configFile);
configFilePath = state.append(DENIED_TEAMS_FILE);
configFile = new File(configFilePath.toOSString());
if (configFile.exists())
parseTeamPermissionFile(deniedTeamsByAspectBinding, configFile);
// configured grant / denied for forced exports:
configFilePath = state.append(DENIED_FORCED_EXPORTS_FILE);
configFile = new File(configFilePath.toOSString());
if (configFile.exists())
forcedExportsDelegate.parseForcedExportsFile(configFile, DENY);
configFilePath = state.append(GRANTED_FORCED_EXPORTS_FILE);
configFile = new File(configFilePath.toOSString());
if (configFile.exists())
forcedExportsDelegate.parseForcedExportsFile(configFile, GRANT);
}
private void writeNegotiationDefaults(File configFile)
throws IOException
{
try (FileWriter writer = new FileWriter(configFile)) {
writer.append(ASPECT_BINDING_DEFAULT+'='+defaultAspectBindingPermission.toString()+'\n');
writer.append(FORCED_EXPORT_DEFAULT+'='+defaultForcedExportPermission.toString()+'\n');
writer.flush();
}
log(IStatus.INFO, "Created aspect binding defaults file "+configFile.getCanonicalPath());
}
private void parseTeamPermissionFile(Map<String, Set<String>> teamsByAspectBinding, File configFile) {
try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.length() > 0 && line.charAt(0) == '#') continue;
@NonNull String[] parts = line.split("=");
if (parts.length == 2) {
Set<String> teams = new HashSet<String>();
StringTokenizer teamToks = new StringTokenizer(parts[1], ",");
while (teamToks.hasMoreElements())
teams.add(teamToks.nextToken());
teamsByAspectBinding.put(parts[0], teams);
}
}
} catch (IOException e) {
log(e, "Failed to read permission file "+configFile.getAbsolutePath());
}
}
private void persistTeamBindingAnswer(String aspectBundleId, String baseBundleId, String teamClass, AspectPermission negotiatedPermission)
{
IPath state = this.otequinoxState;
if (state != null) {
Map<String, Set<String>> teamsByAspect = null;
IPath configFilePath = null;
switch (negotiatedPermission) {
case GRANT:
teamsByAspect = this.grantedTeamsByAspectBinding;
configFilePath = state.append(GRANTED_TEAMS_FILE);
break;
case DENY:
teamsByAspect = this.deniedTeamsByAspectBinding;
configFilePath = state.append(DENIED_TEAMS_FILE);
break;
default: return; // TODO: also persist UNDEFINED (just to avoid asking again?)
}
// in fact we store the entire state for the given category (grant / deny)
// so first insert the new answer into the existing map:
String key = aspectBundleId+"->"+baseBundleId;
Set<String> teams = teamsByAspect.get(key);
if (teams == null)
teamsByAspect.put(key, teams = new HashSet<String>());
teams.add(teamClass);
// now dump the entire map:
File configFile = new File(configFilePath.toOSString());
try {
if (!configFile.exists())
configFile.createNewFile();
try (FileWriter writer = new FileWriter(configFile, false)) {
writer.write("# Aspect permission file generated from aspect negotiation results.\n");
for (Map.Entry<String, Set<String>> entry : teamsByAspect.entrySet()) {
writer.append(entry.getKey()).append('=');
String sep = "";
for (String t : entry.getValue()) {
writer.append(sep).append(t);
sep = ",";
}
writer.append('\n');
}
writer.flush();
}
} catch (IOException ioe) {
log(ioe, "Failed to persist negotiation result");
}
}
}
}