blob: ec1375f7506b44d15ee489a107637861fae27ebe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.felix.framework.security.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.Map.Entry;
import org.apache.felix.framework.security.verifier.SignerMatcher;
import org.apache.felix.framework.util.SecureAction;
import org.osgi.framework.Bundle;
import org.osgi.service.condpermadmin.BundleSignerCondition;
import org.osgi.service.condpermadmin.Condition;
import org.osgi.service.condpermadmin.ConditionInfo;
/**
* This class caches conditions instances by their infos. Furthermore, it allows
* to eval postponed condition permission tuples as per spec (see 9.45).
*/
// TODO: maybe use bundle events instead of soft/weak references.
public final class Conditions
{
private static final ThreadLocal m_conditionStack = new ThreadLocal();
private final Map m_cache = new WeakHashMap();
private final Bundle m_bundle;
private final String[] m_signers;
private final ConditionInfo[] m_conditionInfos;
private final Condition[] m_conditions;
private final SecureAction m_action;
public Conditions(SecureAction action)
{
this(null, null, null, action);
}
private Conditions(Bundle bundle, String[] signers,
ConditionInfo[] conditions, SecureAction action)
{
m_bundle = bundle;
m_signers = signers;
m_conditionInfos = conditions;
m_conditions = ((conditions != null) && (bundle != null)) ? new Condition[m_conditionInfos.length] : null;
m_action = action;
}
public Conditions getConditions(Bundle bundle, String[] signers,
ConditionInfo[] conditions)
{
Conditions result = null;
Map index = null;
synchronized (m_cache)
{
index = (Map) m_cache.get(conditions);
if (index == null)
{
index = new WeakHashMap();
m_cache.put(conditions, index);
}
}
synchronized (index)
{
if (bundle != null)
{
result = (Conditions) index.get(bundle);
}
}
if (result == null)
{
result = new Conditions(bundle, signers, conditions, m_action);
synchronized (index)
{
index.put(bundle, result);
}
}
return result;
}
// See whether the given list is satisfied or not
public boolean isSatisfied(List posts)
{
for (int i = 0; i < m_conditions.length; i++)
{
if (m_bundle == null)
{
if (!m_conditionInfos[i].getType().equals(
BundleSignerCondition.class.getName()))
{
return false;
}
String[] args = m_conditionInfos[i].getArgs();
boolean match = false;
if (args.length == 0)
{
for (int j = 0; j < m_signers.length; j++)
{
if (SignerMatcher.match(args[0], m_signers[j]))
{
match = true;
break;
}
}
}
if (!match)
{
return false;
}
continue;
}
try
{
Condition condition = null;
boolean add = false;
Class clazz = Class.forName(m_conditionInfos[i].getType());
synchronized (m_conditionInfos)
{
condition = m_conditions[i];
}
if (condition == null)
{
add = true;
condition = createCondition(m_bundle, clazz, m_conditionInfos[i]);
}
if (condition.isPostponed())
{
posts.add(condition);
if (add)
{
synchronized (m_conditionInfos)
{
if (m_conditions[i] == null)
{
m_conditions[i] = condition;
}
}
}
}
else
{
Object current = m_conditionStack.get();
if (current == null)
{
m_conditionStack.set(clazz);
}
else
{
if (current instanceof HashSet)
{
if (((HashSet) current).contains(clazz))
{
return false;
}
((HashSet) current).add(clazz);
}
else
{
if (current == clazz)
{
return false;
}
HashSet frame = new HashSet();
frame.add(current);
frame.add(clazz);
m_conditionStack.set(frame);
current = frame;
}
}
try
{
boolean result = condition.isSatisfied();
if (!condition.isMutable() && ((condition != Condition.TRUE) && (condition != Condition.FALSE)))
{
synchronized (m_conditionInfos)
{
m_conditions[i] = result ? Condition.TRUE : Condition.FALSE;
}
}
else
{
synchronized (m_conditionInfos)
{
m_conditions[i] = condition;
}
}
if (!result)
{
return false;
}
}
finally
{
if (current == null)
{
m_conditionStack.set(null);
}
else
{
((HashSet) current).remove(clazz);
if (((HashSet) current).isEmpty())
{
m_conditionStack.set(null);
}
}
}
}
}
catch (Exception e)
{
// TODO: log this as per spec
e.printStackTrace();
return false;
}
}
return true;
}
public boolean evalRecursive(List entries)
{
return _evalRecursive(entries, 0, new ArrayList(), new HashMap());
}
private boolean _evalRecursive(List entries, int pos, List acc, Map contexts)
{
if (pos == entries.size())
{
// we need to group by type by tuple
Map conditions = new HashMap();
for (Iterator iter = acc.iterator(); iter.hasNext();)
{
for (Iterator iter2 = ((List) iter.next()).iterator(); iter2
.hasNext();)
{
Object entry = iter2.next();
Set group = (Set) conditions.get(entry.getClass());
if (group == null)
{
group = new HashSet();
}
group.add(entry);
conditions.put(entry.getClass(), group);
}
}
// and then eval per group
for (Iterator iter = conditions.entrySet().iterator(); iter.hasNext();)
{
Entry entry = (Entry) iter.next();
Class key = (Class) entry.getKey();
Hashtable context = (Hashtable) contexts.get(key);
if (context == null)
{
context = new Hashtable();
contexts.put(key, context);
}
Set set = (Set) entry.getValue();
Condition[] current =
(Condition[]) set.toArray(new Condition[set.size()]);
// We must be catching recursive evaluation as per spec, hence use a thread
// local stack to do so
Object currentCond = m_conditionStack.get();
if (currentCond == null)
{
m_conditionStack.set(key);
}
else
{
if (currentCond instanceof HashSet)
{
if (((HashSet) currentCond).contains(key))
{
return false;
}
((HashSet) currentCond).add(key);
}
else
{
if (currentCond == key)
{
return false;
}
HashSet frame = new HashSet();
frame.add(current);
frame.add(key);
m_conditionStack.set(frame);
currentCond = frame;
}
}
try
{
if (!current[0].isSatisfied(current, context))
{
return false;
}
}
finally
{
if (currentCond == null)
{
m_conditionStack.set(null);
}
else
{
((HashSet) currentCond).remove(key);
if (((HashSet) currentCond).isEmpty())
{
m_conditionStack.set(null);
}
}
}
}
return true;
}
List entry = (List) entries.get(pos);
for (int i = 0; i < entry.size(); i++)
{
acc.add(entry.get(i));
if (_evalRecursive(entries, pos + 1, acc, contexts))
{
return true;
}
acc.remove(acc.size() - 1);
}
return false;
}
private Condition createCondition(final Bundle bundle, final Class clazz,
final ConditionInfo info) throws Exception
{
try
{
return (Condition) m_action.getMethod(clazz, "getCondition",
new Class[] { Bundle.class, ConditionInfo.class }).invoke(null,
new Object[] { bundle, info });
}
catch (Exception ex)
{
ex.printStackTrace();
return (Condition) m_action.getConstructor(clazz,
new Class[] { Bundle.class, ConditionInfo.class }).newInstance(
new Object[] { bundle, info });
}
}
}