Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions resolver/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<artifactId>osgi.annotation</artifactId>
<version>6.0.1</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.log</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.resolver;

import org.osgi.resource.Resource;
import org.osgi.service.log.FormatterLogger;

/**
* A {@link Logger} that redirects all messages to the {@link FormatterLogger}
* provided by the resolve context.
*/
class ContextLogger extends Logger
{
private final FormatterLogger m_delegate;
private final Logger m_fallback;

ContextLogger(FormatterLogger delegate, Logger fallback)
{
// the delegate decides which levels are enabled
super(LOG_DEBUG);
m_delegate = delegate;
m_fallback = fallback;
}

@Override
public boolean isDebugEnabled()
{
return m_delegate.isDebugEnabled();
}

@Override
public void debug(String format, Object... args)
{
m_delegate.debug(format, args);
}

@Override
protected void doLog(int level, String msg, Throwable throwable)
{
switch (level)
{
case LOG_ERROR:
m_delegate.error("%s", msg, throwable);
break;
case LOG_WARNING:
m_delegate.warn("%s", msg, throwable);
break;
case LOG_INFO:
m_delegate.info("%s", msg, throwable);
break;
default:
m_delegate.debug("%s", msg, throwable);
}
}

@Override
public void logUsesConstraintViolation(Resource resource, ResolutionError error)
{
m_fallback.logUsesConstraintViolation(resource, error);
m_delegate.debug("Uses constraint violation for %s: %s", resource,
error.getMessage());
}
}
12 changes: 12 additions & 0 deletions resolver/src/main/java/org/apache/felix/resolver/Logger.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ public final void debug(String msg)
_log(LOG_DEBUG, msg, null);
}

/**
* Logs a debug message using {@link String#format(String, Object...)}
* syntax; the message is only formatted if debug logging is enabled.
*/
public void debug(String format, Object... args)
{
if (isDebugEnabled())
{
_log(LOG_DEBUG, String.format(format, args), null);
}
}

protected void doLog(int level, String msg, Throwable throwable)
{
if (level > m_logLevel)
Expand Down
42 changes: 35 additions & 7 deletions resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ static class ResolveSession implements Runnable
private final ConcurrentMap<String, List<String>> m_usesCache = new ConcurrentHashMap<String, List<String>>();
private ResolutionError m_currentError;
volatile private CancellationException m_isCancelled = null;
private Logger m_logger;

static ResolveSession createSession(ResolveContext resolveContext, Executor executor, Resource dynamicHost, Requirement dynamicReq, List<Capability> dynamicCandidates)
{
Expand Down Expand Up @@ -119,6 +120,18 @@ private ResolveSession(ResolveContext resolveContext, Executor executor, Resourc
}
}

// The resolve context logger is queried once per session
Logger getLogger(Logger fallback)
{
if (m_logger == null)
{
m_logger = m_resolveContext.getLogger()
.<Logger>map(l -> new ContextLogger(l, fallback))
.orElse(fallback);
}
return m_logger;
}

private void initMandatoryAndOptionalResources() {
if (!isDynamic()) {
m_mandatoryResources.addAll(getContext().getMandatoryResources());
Expand Down Expand Up @@ -348,6 +361,14 @@ void checkForCancel() throws ResolutionException {
}
}

/**
* Constructor used by {@link java.util.ServiceLoader}.
*/
public ResolverImpl()
{
this(new Logger(Logger.LOG_ERROR));
}

public ResolverImpl(Logger logger)
{
this(logger, Runtime.getRuntime().availableProcessors());
Expand Down Expand Up @@ -423,6 +444,13 @@ public Map<Resource, List<Wire>> resolve(ResolveContext rc, Executor executor) t
private Map<Resource, List<Wire>> doResolve(ResolveSession session) throws ResolutionException {
Map<Resource, List<Wire>> wireMap = new HashMap<Resource, List<Wire>>();
boolean retry;
Logger logger = session.getLogger(m_logger);
if (logger.isDebugEnabled())
{
logger.debug("Resolving %d mandatory and %d optional resources",
session.getMandatoryResources().size(),
session.getOptionalResources().size());
}
do
{
retry = false;
Expand Down Expand Up @@ -455,7 +483,7 @@ private Map<Resource, List<Wire>> doResolve(ResolveSession session) throws Resol
// log all the resolution exceptions for the uses constraint violations
for (Map.Entry<Resource, ResolutionError> usesError : faultyResources.entrySet())
{
m_logger.logUsesConstraintViolation(usesError.getKey(), usesError.getValue());
logger.logUsesConstraintViolation(usesError.getKey(), usesError.getValue());
}
if (!retry)
{
Expand Down Expand Up @@ -1364,9 +1392,9 @@ else if (!sourceBlame.m_cap.getResource().equals(blame.m_cap.getResource()))
session.getContext(), allCandidates,
resource, pkgName,
sourceBlame, blame);
if (m_logger.isDebugEnabled())
if (session.getLogger(m_logger).isDebugEnabled())
{
m_logger.debug(
session.getLogger(m_logger).debug(
"Candidate permutation failed due to a conflict with a "
+ "fragment import; will try another if possible."
+ " (" + rethrow.getMessage() + ")");
Expand Down Expand Up @@ -1416,9 +1444,9 @@ else if (!sourceBlame.m_cap.getResource().equals(blame.m_cap.getResource()))
session.addPermutation(PermutationType.USES, permRef1.get());
session.addPermutation(PermutationType.USES, permRef2.get());
}
if (m_logger.isDebugEnabled())
if (session.getLogger(m_logger).isDebugEnabled())
{
m_logger.debug("Candidate permutation failed due to a conflict between "
session.getLogger(m_logger).debug("Candidate permutation failed due to a conflict between "
+ "an export and import; will try another if possible."
+ " (" + rethrow.getMessage() + ")");
}
Expand Down Expand Up @@ -1495,9 +1523,9 @@ else if (!sourceBlame.m_cap.getResource().equals(blame.m_cap.getResource()))
}
}

if (m_logger.isDebugEnabled())
if (session.getLogger(m_logger).isDebugEnabled())
{
m_logger.debug("Candidate permutation failed due to a conflict between "
session.getLogger(m_logger).debug("Candidate permutation failed due to a conflict between "
+ "imports; will try another if possible."
+ " (" + rethrow.getMessage() + ")"
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CancellationException;

Expand All @@ -34,6 +35,7 @@
import org.osgi.resource.Resource;
import org.osgi.resource.Wire;
import org.osgi.resource.Wiring;
import org.osgi.service.log.FormatterLogger;

/**
* A resolve context provides resources, options and constraints to the
Expand Down Expand Up @@ -253,6 +255,32 @@ public void onCancel(Runnable callback) {
// do nothing by default
}

/**
* Returns an optional {@link FormatterLogger} that the resolver can use to
* emit diagnostic messages during the resolve operation.
* <p>
* The default implementation returns an empty {@link Optional}. A resolve
* context that wants to receive log messages must override this method and
* return the same thread-safe logger for the duration of a resolve
* operation. The resolver is not required to call this method or to use the
* logger.
* <p>
* A resolve context that only obtains loggers from a {@code LoggerFactory}
* can import {@code org.osgi.service.log} with a consumer range (for example
* {@code [1.4,2.0)}). A resolve context that provides its own
* {@code FormatterLogger} implementation must use a provider range (for
* example {@code [1.4,1.5)}). Outside OSGi, the logger API used by the
* resolver and the resolve context must come from the same class space.
*
* @return An {@link Optional} containing the {@link FormatterLogger} to use
* during the resolve operation, or an empty Optional if no logger
* is available. Must not be {@code null}.
* @since 1.2
*/
public Optional<FormatterLogger> getLogger() {
return Optional.empty();
}

/**
* Returns the subset of {@link Wiring#getRequiredResourceWires(String)
* required} wires that provide wires to {@link Capability capabilities}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

/**
* Resolver Service Package Version 1.1.
* Resolver Service Package Version 1.2.
* <p>
* Bundles wishing to use this package must list the package in the
* Import-Package header of the bundle's manifest. This package has two types of
Expand All @@ -24,16 +24,16 @@
* <p>
* Example import for consumers using the API in this package:
* <p>
* {@code Import-Package: org.osgi.service.resolver; version="[1.1,2.0)"}
* {@code Import-Package: org.osgi.service.resolver; version="[1.2,2.0)"}
* <p>
* Example import for providers implementing the API in this package:
* <p>
* {@code Import-Package: org.osgi.service.resolver; version="[1.1,1.2)"}
* {@code Import-Package: org.osgi.service.resolver; version="[1.2,1.3)"}
*
* @author $Id: 260072a99b71d2c4e6cfd174fd6acc1d20768e25 $
*/

@Version("1.1.1")
@Version("1.2.0")
package org.osgi.service.resolver;

import org.osgi.annotation.versioning.Version;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 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.

org.apache.felix.resolver.ResolverImpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.apache.felix.resolver.Logger;
Expand All @@ -57,12 +58,38 @@
import org.osgi.resource.Resource;
import org.osgi.resource.Wire;
import org.osgi.resource.Wiring;
import org.osgi.service.log.FormatterLogger;
import org.osgi.service.resolver.ResolutionException;
import org.osgi.service.resolver.ResolveContext;
import org.osgi.service.resolver.Resolver;

public class ResolverTest
{
@Test
public void testResolveContextLogger() throws Exception
{
FormatterLogger logger = Mockito.mock(FormatterLogger.class);
Mockito.when(logger.isDebugEnabled()).thenReturn(true);

Resolver resolver = new ResolverImpl();
Map<Resource, Wiring> wirings = new HashMap<Resource, Wiring>();
Map<Requirement, List<Capability>> candMap = new HashMap<Requirement, List<Capability>>();
List<Resource> mandatory = populateScenario1(wirings, candMap);
ResolveContextImpl rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.<Resource> emptyList())
{
@Override
public Optional<FormatterLogger> getLogger()
{
return Optional.of(logger);
}
};

resolver.resolve(rci);

Mockito.verify(logger).debug("Resolving %d mandatory and %d optional resources",
new Object[] { mandatory.size(), 0 });
}

@Test
public void testScenario1() throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.resolver.test;

import java.util.ServiceLoader;

import org.apache.felix.resolver.ResolverImpl;
import org.junit.Assert;
import org.junit.Test;
import org.osgi.service.resolver.Resolver;

public class ServiceLoaderTest
{
@Test
public void testResolverIsProvidedByServiceLoader()
{
Resolver resolver = ServiceLoader.load(Resolver.class).iterator().next();
Assert.assertTrue(resolver instanceof ResolverImpl);
}
}
Loading