Fix FELIX-2705 Provide a way to extend the logger strategy
Define a new ErrorHandler service interface. The iPOJO logger invokes this services when a warning or an error is thrown.
Also generalize the usage of the logger
Add tests about FELIX-2705 and re-enable logger tests.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1037859 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/ErrorHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/ErrorHandler.java
new file mode 100644
index 0000000..4792594
--- /dev/null
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/ErrorHandler.java
@@ -0,0 +1,45 @@
+/*
+ * 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.ipojo;
+
+/**
+ * Error Handler Service Definition.
+ * When exposed, this service is invoked when iPOJO throws a warning
+ * or an error. It's a hook on the internal iPOJO logger.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ErrorHandler {
+
+ /**
+ * Method invokes when an error occurred.
+ * @param instance the instance (can be <code>null</null>)
+ * @param message the error message
+ * @param error the error itself (can be <code>null</code>)
+ */
+ public void onError(ComponentInstance instance, String message, Throwable error);
+
+ /**
+ * Method invokes when a warning occurred.
+ * @param instance the instance (can be <code>null</null>)
+ * @param message the error message
+ * @param error the error itself (can be <code>null</code>)
+ */
+ public void onWarning(ComponentInstance instance, String message, Throwable error);
+
+}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
index 3ffa2b2..49f1649 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
@@ -98,7 +98,7 @@
protected List m_listeners = new ArrayList(1);
/**
- * The logger for the factory (and all component instances).
+ * The logger for the factory.
*/
protected final Logger m_logger;
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java
index fa48335..25b0cce 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -341,6 +341,7 @@
try {
m_factory = factory;
m_instance = m_factory.createComponentInstance(m_configuration);
+ m_logger.log(Logger.INFO, "Instance created");
} catch (UnacceptableConfiguration e) {
m_logger.log(Logger.ERROR, "A matching factory was found for " + m_configuration + ", but the instantiation failed : "
+ e.getMessage());
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
index e8d6210..709d70c 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
@@ -84,6 +84,11 @@
private final ComponentFactory m_factory;
/**
+ * The instance logger.
+ */
+ private final Logger m_logger;
+
+ /**
* The instance description.
*/
private final PrimitiveInstanceDescription m_description;
@@ -153,6 +158,15 @@
m_context = context;
m_handlers = handlers;
m_description = new PrimitiveInstanceDescription(m_factory.getComponentDescription(), this);
+ m_logger = new Logger(m_context, this);
+ }
+
+ /**
+ * The instance logger.
+ * @return the logger
+ */
+ public Logger getLogger() {
+ return m_logger;
}
/**
@@ -263,13 +277,13 @@
}
return field.get(pojo);
} catch (SecurityException e) {
- m_factory.getLogger().log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
} catch (NoSuchFieldException e) {
- m_factory.getLogger().log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
} catch (IllegalArgumentException e) {
- m_factory.getLogger().log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
} catch (IllegalAccessException e) {
- m_factory.getLogger().log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
+ m_logger.log(Logger.ERROR, "Cannot reflect on field " + fieldName + " to obtain the value : " + e.getMessage());
}
return null;
} else {
@@ -302,7 +316,7 @@
try {
m_handlers[i].start();
} catch (IllegalStateException e) {
- m_factory.getLogger().log(Logger.ERROR, e.getMessage());
+ m_logger.log(Logger.ERROR, e.getMessage());
stop();
throw e;
}
@@ -443,6 +457,7 @@
} catch (IllegalStateException e) {
// When an illegal state exception happens, the instance manager must be stopped immediately.
stop();
+ m_logger.log(Logger.ERROR, e.getMessage(), e);
return;
}
} else {
@@ -454,6 +469,7 @@
} catch (IllegalStateException e) {
// When an illegal state exception happens, the instance manager must be stopped immediately.
stop();
+ m_logger.log(Logger.ERROR, e.getMessage());
return;
}
}
@@ -541,7 +557,7 @@
try {
m_clazz = m_factory.loadClass(m_className);
} catch (ClassNotFoundException e) {
- m_factory.getLogger().log(Logger.ERROR, "[" + m_name + "] Class not found during the loading phase : " + e.getMessage());
+ m_logger.log(Logger.ERROR, "[" + m_name + "] Class not found during the loading phase : " + e.getMessage(), e);
stop();
return;
}
@@ -603,40 +619,39 @@
}
}
} catch (IllegalAccessException e) {
- m_factory.getLogger().log(Logger.ERROR,
- "[" + m_name + "] createInstance -> The POJO constructor is not accessible : " + e.getMessage());
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The POJO constructor is not accessible : " + e.getMessage(), e);
stop();
throw new RuntimeException("Cannot create a POJO instance, the POJO constructor is not accessible : " + e.getMessage());
} catch (SecurityException e) {
- m_factory.getLogger().log(
+ m_logger.log(
Logger.ERROR,
"["
+ m_name
+ "] createInstance -> The POJO constructor is not accessible (security reason) : "
- + e.getMessage());
+ + e.getMessage(), e);
stop();
throw new RuntimeException("Cannot create a POJO instance, the POJO constructor is not accessible : " + e.getMessage());
} catch (InvocationTargetException e) {
- m_factory.getLogger().log(
+ m_logger.log(
Logger.ERROR,
"["
+ m_name
+ "] createInstance -> Cannot invoke the constructor method - the constructor throws an exception : "
- + e.getTargetException().getMessage());
+ + e.getTargetException().getMessage(), e.getTargetException());
onError(null, m_className, e.getTargetException());
stop();
throw new RuntimeException("Cannot create a POJO instance, the POJO constructor has thrown an exception: " + e.getTargetException().getMessage());
} catch (NoSuchMethodException e) {
- m_factory.getLogger().log(Logger.ERROR,
- "[" + m_name + "] createInstance -> Cannot invoke the constructor (method not found) : " + e.getMessage());
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> Cannot invoke the constructor (method not found) : " + e.getMessage(), e);
stop();
throw new RuntimeException("Cannot create a POJO instance, the POJO constructor cannot be found : " + e.getMessage());
} catch (Throwable e) {
// Catch every other possible error and runtime exception.
- m_factory.getLogger().log(Logger.ERROR,
- "[" + m_name + "] createInstance -> The POJO constructor invocation failed : " + e.getMessage());
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The POJO constructor invocation failed : " + e.getMessage(), e);
stop();
- e.printStackTrace();
throw new RuntimeException("Cannot create a POJO instance, the POJO constructor invocation has thrown an exception : " + e.getMessage());
}
} else {
@@ -664,12 +679,12 @@
instance = factory.invoke(null, args);
} catch (NoSuchMethodException e2) {
// Error : factory-method not found
- m_factory.getLogger().log(
+ m_logger.log(
Logger.ERROR,
"["
+ m_name
+ "] createInstance -> Cannot invoke the factory-method (method not found) : "
- + e2.getMessage());
+ + e2.getMessage(), e2);
stop();
throw new RuntimeException("Cannot create a POJO instance, the factory-method cannot be found : " + e2.getMessage());
}
@@ -685,26 +700,26 @@
} catch (InvocationTargetException e) {
// Error : invocation failed
- m_factory.getLogger().log(Logger.ERROR,
- "[" + m_name + "] createInstance -> The factory-method throws an exception : " + e.getTargetException());
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The factory-method throws an exception : " + e.getTargetException(), e.getTargetException());
onError(null, m_className, e.getTargetException());
stop();
throw new RuntimeException("Cannot create a POJO instance, the factory-method has thrown an exception: " + e.getTargetException().getMessage());
} catch (NoSuchMethodException e) {
// Error : _setInstanceManager method is missing
- m_factory.getLogger()
+ m_logger
.log(
Logger.ERROR,
"["
+ m_name
+ "] createInstance -> Cannot invoke the factory-method (the _setInstanceManager method does not exist) : "
- + e.getMessage());
+ + e.getMessage(), e);
stop();
throw new RuntimeException("Cannot create a POJO instance, the factory-method cannot be found : " + e.getMessage());
} catch (Throwable e) {
// Catch every other possible error and runtime exception.
- m_factory.getLogger().log(Logger.ERROR,
- "[" + m_name + "] createInstance -> The factory-method invocation failed : " + e.getMessage());
+ m_logger.log(Logger.ERROR,
+ "[" + m_name + "] createInstance -> The factory-method invocation failed : " + e.getMessage(), e);
stop();
throw new RuntimeException("Cannot create a POJO instance, the factory-method invocation has thrown an exception : " + e.getMessage());
}
@@ -949,7 +964,7 @@
if (result != initialValue) {
//TODO analyze impact of removing conflict detection
if ((handlerResult != null && !handlerResult.equals(result)) || (result != null && handlerResult == null)) {
- m_factory.getLogger().log(
+ m_logger.log(
Logger.WARNING,
"A conflict was detected on the injection of "
+ fieldName);
@@ -1069,7 +1084,7 @@
return null;
}
// Cannot happen
- m_factory.getLogger().log(Logger.ERROR, "A methodID cannot be associated with a method from the POJO class: " + methodId);
+ m_logger.log(Logger.ERROR, "A methodID cannot be associated with a method from the POJO class: " + methodId);
return null;
} else {
return method;
@@ -1158,17 +1173,17 @@
* @see org.apache.felix.ipojo.ComponentInstance#reconfigure(java.util.Dictionary)
*/
public void reconfigure(Dictionary configuration) {
- m_factory.getLogger().log(Logger.INFO, "Reconfiguring " + getInstanceName());
+ m_logger.log(Logger.INFO, "Reconfiguring " + getInstanceName());
for (int i = 0; i < m_handlers.length; i++) {
m_handlers[i].getHandler().reconfigure(configuration);
}
// We synchronized the state computation.
synchronized (this) {
if (m_state == STOPPED) {
- m_factory.getLogger().log(Logger.INFO, "Instance stopped during reconfiguration - Try to restart");
+ m_logger.log(Logger.INFO, "Instance stopped during reconfiguration - Try to restart");
start();
} else if (m_state == INVALID) {
- m_factory.getLogger().log(Logger.INFO, "Instance invalid during reconfiguration - Recompute state");
+ m_logger.log(Logger.INFO, "Instance invalid during reconfiguration - Recompute state");
// Try to revalidate the instance after reconfiguration
for (int i = 0; i < m_handlers.length; i++) {
if (m_handlers[i].getState() != VALID) {
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/PrimitiveHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/PrimitiveHandler.java
index 7e15c11..94a7544 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/PrimitiveHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/PrimitiveHandler.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -34,23 +34,28 @@
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public abstract class PrimitiveHandler extends Handler implements FieldInterceptor, MethodInterceptor {
-
+
/**
* The "Primitive" Handler type (value).
*/
public static final String HANDLER_TYPE = "primitive";
-
+
/**
* The reference on the instance manager.
*/
private InstanceManager m_manager;
-
-
+
+
/**
- * The factory of the instance manager.
+ * The factory of the instance manager.
*/
private ComponentFactory m_factory;
-
+
+ /**
+ * Instance Logger used by the handler.
+ */
+ private Logger m_instanceLogger;
+
/**
* Attaches the current handler to the given instance.
* @param manager the instance on which the current handler will be attached.
@@ -58,26 +63,31 @@
*/
protected final void attach(ComponentInstance manager) {
m_manager = (InstanceManager) manager;
+ m_instanceLogger = m_manager.getLogger();
}
-
+
/**
* Sets the factory of the managed instance.
- * @param factory the factory
+ * @param factory the factory
* @see org.apache.felix.ipojo.Handler#setFactory(org.apache.felix.ipojo.Factory)
*/
public final void setFactory(Factory factory) {
m_factory = (ComponentFactory) factory;
}
-
+
/**
- * gets the logger of the managed instance.
+ * Gets the logger of the managed instance.
+ * IF no instance attached yet, use the factory logger.
* @return the logger to use to log messages.
* @see org.apache.felix.ipojo.Handler#getLogger()
*/
public Logger getLogger() {
- return m_factory.getLogger();
+ if (m_instanceLogger == null) {
+ return m_factory.getLogger();
+ }
+ return m_instanceLogger;
}
-
+
/**
* Gets the instance manager managing the instance.
* @return the instance manager
@@ -85,7 +95,7 @@
public InstanceManager getInstanceManager() {
return m_manager;
}
-
+
/**
* Gets the factory which creates the managed instance.
* @return the factory which creates the managed instance.
@@ -93,7 +103,7 @@
public ComponentFactory getFactory() {
return m_factory;
}
-
+
/**
* Gets the PojoMetadata of the content of the managed
* instance. This method allows getting manipulation
@@ -104,25 +114,25 @@
public PojoMetadata getPojoMetadata() {
return m_factory.getPojoMetadata();
}
-
+
/**
* Gets a plugged handler of the same container.
- * This method must be called only in the start method (or after).
+ * This method must be called only in the start method (or after).
* In the configure method, this method can be inconsistent
- * as all handlers are not initialized.
+ * as all handlers are not initialized.
* @param name the name of the handler to find (class name or qualified
- * handler name (<code>ns:name</code>)).
+ * handler name (<code>ns:name</code>)).
* @return the handler object or <code>null</code> if the handler is not found.
*/
public final Handler getHandler(String name) {
return m_manager.getHandler(name);
}
-
+
/**
- * Callback method called when a managed field
+ * Callback method called when a managed field
* receives a new value. The given pojo can be
* null if the new value is set by another handler.
- * This default implementation does nothing.
+ * This default implementation does nothing.
* @param pojo the pojo object setting the value
* @param fieldName the field name
* @param value the value received by the field
@@ -147,23 +157,23 @@
public Object onGet(Object pojo, String fieldName, Object value) {
return value;
}
-
+
/**
* Callback method called when a method will be invoked.
- * This default implementation does nothing.
+ * This default implementation does nothing.
* @param pojo the pojo on which the method is called.
* @param method the method invoked.
* @param args the arguments array.
* @see MethodInterceptor#onEntry(Object, Method, Object[])
*/
- public void onEntry(Object pojo, Method method, Object[] args) {
+ public void onEntry(Object pojo, Method method, Object[] args) {
// Nothing to do in the default implementation
}
/**
* Callback method called when a method ends.
* This method is called when a thread exits a method (before a return or a throw).
- * If the given returned object is <code>null</code>, either the method is
+ * If the given returned object is <code>null</code>, either the method is
* <code>void</code>, or it returns <code>null</code>.
* You must not modified the returned object.
* The default implementation does nothing.
@@ -172,13 +182,13 @@
* @param returnedObj the returned object (boxed for primitive type)
* @see MethodInterceptor#onExit(Object, Method, Object)
*/
- public void onExit(Object pojo, Method method, Object returnedObj) {
+ public void onExit(Object pojo, Method method, Object returnedObj) {
// Nothing to do in the default implementation
}
-
+
/**
* Callback method called when an error occurs.
- * This method is called when the execution throws an exception
+ * This method is called when the execution throws an exception
* in the given method.
* The default implementation does nothing.
* @param pojo the pojo on which the method was accessed.
@@ -189,12 +199,12 @@
public void onError(Object pojo, Method method, Throwable throwable) {
// Nothing to do in the default implementation
}
-
+
/**
- * Callback method called when the execution of a method will terminate :
+ * Callback method called when the execution of a method will terminate :
* just before to throw an exception or before to return.
* {@link MethodInterceptor#onExit(Object, Method, Object)} or
- * {@link MethodInterceptor#onError(Object, Method, Throwable)}
+ * {@link MethodInterceptor#onError(Object, Method, Throwable)}
* were already called.
* This default implementation does nothing.
* @param pojo the pojo on which the method was accessed.
@@ -203,17 +213,17 @@
public void onFinally(Object pojo, Method method) {
// Nothing to do in the default implementation
}
-
+
/**
* Callback method called when an instance of the component is created, but
* before someone can use it.
* The default implementation does nothing.
* @param instance the created instance
*/
- public void onCreation(Object instance) {
+ public void onCreation(Object instance) {
// Nothing to do in the default implementation
}
-
-
+
+
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
index bdbb269..c41c6f8 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
@@ -297,10 +297,10 @@
callback.callOnInstance(pojo, ref, getService(ref));
}
} catch (NoSuchMethodException e) {
- m_handler.error("The method " + callback.getMethodName() + " does not exist in the implementation class " + m_handler.getInstanceManager().getClassName());
+ m_handler.error("The method " + callback.getMethodName() + " does not exist in the implementation class " + m_handler.getInstanceManager().getClassName(), e);
m_handler.getInstanceManager().stop();
} catch (IllegalAccessException e) {
- m_handler.error("The method " + callback.getMethodName() + " is not accessible in the implementation class " + m_handler.getInstanceManager().getClassName());
+ m_handler.error("The method " + callback.getMethodName() + " is not accessible in the implementation class " + m_handler.getInstanceManager().getClassName(), e);
m_handler.getInstanceManager().stop();
} catch (InvocationTargetException e) {
m_handler.error("The method " + callback.getMethodName() + " in the implementation class " + m_handler.getInstanceManager().getClassName() + " throws an exception : " + e.getTargetException().getMessage(), e.getTargetException());
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
index 1f6169d..f9f0643 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
@@ -179,7 +179,7 @@
+ m_handler.getInstanceManager().getInstanceName()
+ "] The customized service object creation policy "
+ "(" + creationStrategyClass.getName() + ") is not accessible: "
- + e.getMessage());
+ + e.getMessage(), e);
getInstanceManager().stop();
return;
} catch (InstantiationException e) {
@@ -187,7 +187,7 @@
+ m_handler.getInstanceManager().getInstanceName()
+ "] The customized service object creation policy "
+ "(" + creationStrategyClass.getName() + ") cannot be instantiated: "
- + e.getMessage());
+ + e.getMessage(), e);
getInstanceManager().stop();
return;
}
@@ -332,7 +332,7 @@
if (m_serviceRegistration != null) {
unregisterService();
}
-
+
if (m_handler.getInstanceManager().getState() == ComponentInstance.VALID
&& m_serviceRegistration == null && isAtLeastAServiceControllerValid()) {
// Build the service properties list
@@ -372,7 +372,7 @@
protected synchronized void unregisterService() {
// Create a copy of the service reference in the case we need
// to inject it to the post-unregistration callback.
-
+
ServiceReference ref = null;
if (m_serviceRegistration != null) {
ref = m_serviceRegistration.getReference();
@@ -526,11 +526,11 @@
}
return null;
}
-
+
public ServiceController getControllerBySpecification(String spec) {
return (ServiceController) m_controllers.get(spec);
}
-
+
/**
* Checks if at least one service controller is valid.
* @return <code>true</code> if one service controller at least
@@ -538,12 +538,12 @@
*/
private boolean isAtLeastAServiceControllerValid() {
Collection controllers = m_controllers.values();
-
+
// No controller
if (controllers.isEmpty()) {
return true;
}
-
+
Iterator iterator = controllers.iterator();
while (iterator.hasNext()) {
ServiceController controller = (ServiceController) iterator.next();
@@ -553,12 +553,12 @@
}
return false;
}
-
+
private String[] getServiceSpecificationsToRegister() {
if (m_controllers.isEmpty()) {
return m_serviceSpecifications;
}
-
+
ArrayList l = new ArrayList();
if (m_controllers.containsKey("ALL")) {
ServiceController ctrl = (ServiceController) m_controllers.get("ALL");
@@ -566,7 +566,7 @@
l.addAll(Arrays.asList(m_serviceSpecifications));
}
}
-
+
Iterator iterator = m_controllers.keySet().iterator();
while (iterator.hasNext()) {
String spec = (String) iterator.next();
@@ -581,9 +581,9 @@
l.remove(spec);
}
}
-
+
return (String[]) l.toArray(new String[l.size()]);
-
+
}
public void setPostRegistrationCallback(Callback cb) {
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Logger.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
index 3c8029c..5382603 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -18,6 +18,8 @@
*/
package org.apache.felix.ipojo.util;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ErrorHandler;
import org.apache.felix.ipojo.Extender;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@@ -25,23 +27,25 @@
/**
* iPOJO Logger.
- * This class is an helper class implementing a simple log system.
+ * This class is an helper class implementing a simple log system.
* This logger sends log messages to a log service if available.
- *
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class Logger {
-
+
/**
* The iPOJO default log level property.
*/
public static final String IPOJO_LOG_LEVEL_PROP = "ipojo.log.level";
-
+
/**
* iPOJO log level manifest header.
+ * The uppercase 'I' is important as BND removes all headers that do not
+ * start with an uppercase are not added to the bundle.
* Use an upper case to support bnd.
*/
- public static final String IPOJO_LOG_LEVEL_HEADER = "IPOJO-log-level";
+ public static final String IPOJO_LOG_LEVEL_HEADER = "Ipojo-log-level";
/**
* The Log Level ERROR.
@@ -75,6 +79,11 @@
private String m_name;
/**
+ * The instance associated to the logger if any.
+ */
+ private ComponentInstance m_instance;
+
+ /**
* The trace level of this logger.
*/
private int m_level;
@@ -90,7 +99,20 @@
m_level = level;
m_context = context;
}
-
+
+ /**
+ * Creates a logger.
+ * @param context the bundle context
+ * @param instance the instance
+ * @param level the trace level
+ */
+ public Logger(BundleContext context, ComponentInstance instance, int level) {
+ m_instance = instance;
+ m_name = m_instance.getInstanceName();
+ m_level = level;
+ m_context = context;
+ }
+
/**
* Create a logger.
* Uses the default logger level.
@@ -102,6 +124,16 @@
}
/**
+ * Create a logger.
+ * Uses the default logger level.
+ * @param context the bundle context
+ * @param instance the instance
+ */
+ public Logger(BundleContext context, ComponentInstance instance) {
+ this(context, instance, getDefaultLevel(context));
+ }
+
+ /**
* Logs a message.
* @param level the level of the message
* @param msg the the message to log
@@ -110,6 +142,7 @@
if (m_level >= level) {
dispatch(level, msg);
}
+ invokeErrorHandler(level, msg, null);
}
/**
@@ -122,10 +155,11 @@
if (m_level >= level) {
dispatch(level, msg, exception);
}
+ invokeErrorHandler(level, msg, exception);
}
-
+
/**
- * Internal log method.
+ * Internal log method.
* @param level the level of the message.
* @param msg the message to log
*/
@@ -139,7 +173,7 @@
} else {
Extender.getIPOJOBundleContext().getServiceReference(LogService.class.getName());
}
-
+
if (ref != null) {
log = (LogService) m_context.getService(ref);
}
@@ -186,7 +220,7 @@
System.err.println(message);
break;
}
-
+
if (log != null) {
m_context.ungetService(ref);
}
@@ -208,14 +242,14 @@
} else {
Extender.getIPOJOBundleContext().getServiceReference(LogService.class.getName());
}
-
+
if (ref != null) {
log = (LogService) m_context.getService(ref);
}
} catch (IllegalStateException e) {
// Handle the case where the iPOJO bundle is stopping
}
-
+
String message = null;
switch (level) {
case DEBUG:
@@ -260,35 +294,65 @@
exception.printStackTrace();
break;
}
-
+
if (log != null) {
m_context.ungetService(ref);
}
}
-
+
+ /**
+ * Invokes the error handler service is present.
+ * @param level the log level
+ * @param msg the message
+ * @param error the error
+ */
+ private void invokeErrorHandler(int level, String msg, Throwable error) {
+ // First check the level
+ if (level > WARNING) {
+ return; // Others levels are not supported.
+ }
+ // Try to get the error handler service
+ try {
+ ServiceReference ref = m_context.getServiceReference(ErrorHandler.class.getName());
+ if (ref != null) {
+ ErrorHandler handler = (ErrorHandler) m_context.getService(ref);
+ if (level == ERROR) {
+ handler.onError(m_instance, msg, error);
+ } else if (level == WARNING) {
+ handler.onWarning(m_instance, msg, error);
+ } // The others case are not supported
+ m_context.ungetService(ref);
+ return;
+ } // Else do nothing...
+ } catch (IllegalStateException e) {
+ // Ignore
+ // The iPOJO bundle is stopping.
+ }
+ }
+
/**
* Gets the default logger level.
- * The property is searched inside the framework properties,
- * the system properties, and in the manifest from the given
- * bundle context. By default, set the level to {@link Logger#WARNING}.
+ * The property is searched inside the framework properties,
+ * the system properties, and in the manifest from the given
+ * bundle context. By default, set the level to {@link Logger#WARNING}.
* @param context the bundle context.
* @return the default log level.
*/
private static int getDefaultLevel(BundleContext context) {
// First check in the framework and in the system properties
String level = context.getProperty(IPOJO_LOG_LEVEL_PROP);
-
+
// If null, look in the bundle manifest
if (level == null) {
String key = IPOJO_LOG_LEVEL_PROP.replace('.', '-');
level = (String) context.getBundle().getHeaders().get(key);
}
-
+
// if still null try the second header
if (level == null) {
level = (String) context.getBundle().getHeaders().get(IPOJO_LOG_LEVEL_HEADER);
}
-
+
if (level != null) {
if (level.equalsIgnoreCase("info")) {
return INFO;
@@ -300,10 +364,10 @@
return ERROR;
}
}
-
+
// Either l is null, either the specified log level was unknown
// Set the default to WARNING
return WARNING;
-
+
}
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Property.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Property.java
index f7bc98c..a2d3534 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Property.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Property.java
@@ -527,10 +527,10 @@
}
m_invoked = true;
} catch (NoSuchMethodException e) {
- m_handler.error("The method " + m_method + " does not exist in the implementation class " + m_manager.getClassName());
+ m_handler.error("The method " + m_method + " does not exist in the implementation class " + m_manager.getClassName(), e);
m_manager.stop();
} catch (IllegalAccessException e) {
- m_handler.error("The method " + m_method + " is not accessible in the implementation class " + m_manager.getClassName());
+ m_handler.error("The method " + m_method + " is not accessible in the implementation class " + m_manager.getClassName(), e);
m_manager.stop();
} catch (InvocationTargetException e) {
m_handler.error("The method " + m_method + " in the implementation class " + m_manager.getClassName() + "throws an exception : " + e.getTargetException().getMessage(), e.getTargetException());
diff --git a/ipojo/tests/core/logger/erroneous-component.xml b/ipojo/tests/core/logger/erroneous-component.xml
new file mode 100644
index 0000000..9ac7735
--- /dev/null
+++ b/ipojo/tests/core/logger/erroneous-component.xml
@@ -0,0 +1,5 @@
+<ipojo>
+<component immediate="true" classname="org.apache.felix.ipojo.tests.core.component.MyErroneousComponent">
+ <provides/>
+</component>
+</ipojo>
diff --git a/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/ErrorHandlerTest.java b/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/ErrorHandlerTest.java
new file mode 100644
index 0000000..1e0f7d6
--- /dev/null
+++ b/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/ErrorHandlerTest.java
@@ -0,0 +1,160 @@
+package org.apache.felix.ipojo.tests.core;
+
+import static org.ops4j.pax.exam.CoreOptions.felix;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.MavenUtils.asInProject;
+import static org.ops4j.pax.swissbox.tinybundles.core.TinyBundles.newBundle;
+import static org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOBuilder.withiPOJO;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ErrorHandler;
+import org.apache.felix.ipojo.tests.core.component.MyComponent;
+import org.apache.felix.ipojo.tests.core.component.MyErroneousComponent;
+import org.apache.felix.ipojo.tests.core.service.MyService;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Inject;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.ow2.chameleon.testing.helpers.IPOJOHelper;
+import org.ow2.chameleon.testing.helpers.OSGiHelper;
+
+@RunWith( JUnit4TestRunner.class )
+public class ErrorHandlerTest {
+
+ @Inject
+ private BundleContext context;
+
+ private OSGiHelper osgi;
+
+ private IPOJOHelper ipojo;
+
+ @Before
+ public void init() {
+ osgi = new OSGiHelper(context);
+ ipojo = new IPOJOHelper(context);
+ }
+
+ @After
+ public void stop() {
+ ipojo.dispose();
+ osgi.dispose();
+ }
+
+ @Configuration
+ public static Option[] configure() {
+
+ File tmp = new File("target/tmp");
+ tmp.mkdirs();
+
+ Option[] opt = options(
+ felix(),
+// equinox(),
+ provision(
+ // Runtime.
+ mavenBundle().groupId( "org.apache.felix" ).artifactId( "org.apache.felix.log" ).version(asInProject()),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.ipojo").version(asInProject()),
+ mavenBundle().groupId("org.ow2.chameleon.testing").artifactId("osgi-helpers").versionAsInProject()
+ ),
+ provision(
+ newBundle()
+ .add( MyService.class )
+ .set(Constants.BUNDLE_SYMBOLICNAME,"ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.tests.core.service")
+ .build()
+ ),
+ provision(
+ // Component
+ newBundle()
+ .add(MyComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME,"MyComponent")
+ .set(Constants.IMPORT_PACKAGE, "org.apache.felix.ipojo.tests.core.service")
+ .set("Ipojo-log-level", "info")
+ .build( withiPOJO(new File(tmp, "provider-with-level-in-manifest.jar"), new File("component.xml")))
+ ),
+ provision(
+ // Component
+ newBundle()
+ .add(MyErroneousComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME,"MyErroneousComponent")
+ .set("Ipojo-log-level", "debug")
+ .set(Constants.IMPORT_PACKAGE, "org.apache.felix.ipojo.tests.core.service")
+ .build( withiPOJO(new File(tmp, "erroneous-provider-with-level-in-manifest.jar"), new File("erroneous-component.xml")))
+ )
+ );
+ return opt;
+ }
+
+ @Test
+ public void testErrorHandlerEmpty() throws InterruptedException, InvalidSyntaxException {
+ MyErrorHandler handler = new MyErrorHandler();
+ context.registerService(ErrorHandler.class.getName(), handler, null);
+
+ System.out.println(handler.m_errors);
+
+ Assert.assertTrue(handler.m_errors.isEmpty());
+ }
+
+ @Test
+ public void testErrorHandler() throws InterruptedException, InvalidSyntaxException {
+ MyErrorHandler handler = new MyErrorHandler();
+ context.registerService(ErrorHandler.class.getName(), handler, null);
+
+ try {
+ ipojo.createComponentInstance("org.apache.felix.ipojo.tests.core.component.MyErroneousComponent");
+ } catch (Exception e ) {
+ System.out.println(e);
+ }
+
+
+ System.out.println(handler.m_errors);
+
+ Assert.assertFalse(handler.m_errors.isEmpty());
+ Assert.assertTrue(handler.m_errors.contains("org.apache.felix.ipojo.tests.core.component.MyErroneousComponent-0:[org.apache.felix.ipojo.tests.core.component.MyErroneousComponent-0] createInstance -> Cannot invoke the constructor method - the constructor throws an exception : bad:bad"));
+ }
+
+
+ private class MyErrorHandler implements ErrorHandler {
+
+ private List<String> m_errors = new ArrayList<String>();
+
+ public void onError(ComponentInstance instance, String message,
+ Throwable error) {
+ System.out.println("on Error ! " + instance + " - " + message);
+ if (instance == null) {
+ if (error == null) {
+ m_errors.add("no-instance:" + message);
+ } else {
+ m_errors.add("no-instance:" + message + ":" + error.getMessage());
+ }
+ } else {
+ if (error == null) {
+ m_errors.add(instance.getInstanceName() + ":" + message);
+ } else {
+ m_errors.add(instance.getInstanceName() + ":" + message + ":" + error.getMessage());
+ }
+ }
+ }
+
+ public void onWarning(ComponentInstance instance, String message,
+ Throwable error) {
+ System.out.println("on warning ! " + instance + " - " + message);
+ }
+
+ }
+
+
+}
diff --git a/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/ManifestLoggerInfoTest.java b/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/ManifestLoggerInfoTest.java
index 7a9e9b7..5ca7c9d 100644
--- a/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/ManifestLoggerInfoTest.java
+++ b/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/ManifestLoggerInfoTest.java
@@ -1,5 +1,6 @@
package org.apache.felix.ipojo.tests.core;
+import static org.ops4j.pax.exam.CoreOptions.equinox;
import static org.ops4j.pax.exam.CoreOptions.felix;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
@@ -9,6 +10,7 @@
import static org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOBuilder.withiPOJO;
import java.io.File;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
@@ -20,13 +22,14 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Customizer;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.swissbox.tinybundles.core.TinyBundles;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -78,7 +81,7 @@
Option[] opt = options(
felix(),
-// equinox(),
+ equinox(),
provision(
// Runtime.
mavenBundle().groupId( "org.apache.felix" ).artifactId( "org.apache.felix.log" ).version(asInProject()),
@@ -98,15 +101,22 @@
.add(MyComponent.class)
.set(Constants.BUNDLE_SYMBOLICNAME,"MyComponent")
.set(Constants.IMPORT_PACKAGE, "org.apache.felix.ipojo.tests.core.service")
- .set("ipojo-log-level", "info")
+ .set("Ipojo-log-level", "info")
.build( withiPOJO(new File(tmp, "provider-with-level-in-manifest.jar"), new File("component.xml")))
- )
- );
+ ),
+ new Customizer() {
+ @Override
+ public InputStream customizeTestProbe( InputStream testProbe )
+ {
+ return TinyBundles.modifyBundle(testProbe)
+ .set(Constants.IMPORT_PACKAGE, "org.apache.felix.ipojo.tests.core.service")
+ .build();
+ }
+ });
return opt;
}
@Test
- @Ignore //TODO Why we have a classloading issue here ?
public void testMessages() throws InterruptedException, InvalidSyntaxException {
Bundle bundle = osgi.getBundle("MyComponent");
Assert.assertNotNull(bundle);
@@ -123,9 +133,9 @@
- // Assert.assertNotNull(osgi.getServiceObject(MyService.class.getName(), null));
+ Assert.assertNotNull(osgi.getServiceObject(MyService.class.getName(), null));
-// osgi.waitForService("org.apache.felix.ipojo.tests.core.service.MyService", null, 5000);
+ osgi.waitForService("org.apache.felix.ipojo.tests.core.service.MyService", null, 5000);
List<String> messages = getMessages(log.getLog());
System.out.println(messages);
Assert.assertTrue(messages.contains("Ready"));
diff --git a/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/component/MyErroneousComponent.java b/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/component/MyErroneousComponent.java
new file mode 100644
index 0000000..feca648
--- /dev/null
+++ b/ipojo/tests/core/logger/src/test/java/org/apache/felix/ipojo/tests/core/component/MyErroneousComponent.java
@@ -0,0 +1,15 @@
+package org.apache.felix.ipojo.tests.core.component;
+
+import org.apache.felix.ipojo.tests.core.service.MyService;
+
+public class MyErroneousComponent implements MyService {
+
+ public MyErroneousComponent() {
+ throw new NullPointerException("bad");
+ }
+
+ public void foo() {
+ // Nothing to do.
+ }
+
+}