Abbiamo avuto un'esigenza simile in una delle nostre applicazioni precedenti. La soluzione che abbiamo trovato era un ResourceManager che recuperava risorse (Logger, file di configurazione, ecc.) Da (contesto) ClassLoader.
In genere ogni applicazione distribuita come EAR ottiene il proprio ClassLoader e la libreria può quindi chiamare ResourceManager.getLogger() per ottenere il Logger associato all'attuale Thread/Applicazione. In questo modo non hai bisogno di passarlo con ogni chiamata di metodo nella libreria (richiede che tu possa cambiare la libreria).
import java.util.*;
import java.util.logging.*;
public class ResourceManager
{
private static final Map<ClassLoader, Map<String, Object>> resources =
Collections.synchronizedMap(new WeakHashMap<ClassLoader, Map<String, Object>>());
public static final String LOGGER = Logger.class.getName();
static
{
// adjust for log4j or other frameworks
final Logger logger = Logger.getLogger("logging.default");
logger.setLevel(Level.ALL);
logger.addHandler(new ConsoleHandler()
{
{
setOutputStream(System.out);
setLevel(Level.ALL);
}
});
registerResource(null, LOGGER, logger);
}
private static ClassLoader getApplicationScope()
{
return Thread.currentThread().getContextClassLoader();
}
public static void registerResource(final String name, final Object resource)
{
registerResource(getApplicationScope(), name, resource);
}
public static synchronized void registerResource(final ClassLoader scope, final String name, final Object resource)
{
Map<String, Object> hm = null;
hm = resources.get(scope);
if (hm == null)
{
hm = Collections.synchronizedMap(new HashMap<String, Object>());
resources.put(scope, hm);
}
hm.put(name, resource);
}
public static Object getResource(final String name)
{
for(ClassLoader scope = getApplicationScope();;scope = scope.getParent())
{
final Map<String, Object> hm = resources.get(scope);
if ((hm != null) && hm.containsKey(name))
{
return hm.get(name);
}
if (scope == null) break;
}
return null;
}
public static void registerLogger(final Logger logger)
{
registerResource(LOGGER, logger);
}
public static Logger getLogger()
{
return (Logger)getResource(LOGGER);
}
}
Registrati Logger nella fase di init di EJB/WebApp (deve essere registrato prima di ogni chiamata a getLogger):
Logger logger = Logger.getLogger([Application Logger Name]);
ResourceManager.registerLogger(logger);
Recupera Logger in libreria (metodo di utilità):
private Logger getLogger()
{
return ResourceManager.getLogger();
}
Ciò restituirà il registratore per l'applicazione (EAR) associato al thread corrente.
Non limitato ai logger, funziona anche per altre risorse che si desidera condividere.
Limitazioni:
non funzionerà se si comprime più applicazioni/EJB per orecchio schierato
ResourceManager e biblioteca registrazione bisogno di essere sullo stesso o un ClassLoader superiore alla biblioteca e applicazione. Se esiste un'opzione per il raggruppamento, allora l'approccio di Alexanders è più pulito. (usiamo java.util.logging che è di default a livello del server quindi il suo approccio non funziona)
Come nota a margine, questa soluzione può anche essere utilizzata per condividere tutti i tipi di risorse, non solo i logger. – Stefan
potresti voler incollare sezioni di codice rilevanti nella tua risposta, nel caso in cui il link dovesse morire in futuro. – rouble
Un'altra cosa da notare su questo metodo è che ResourceManager.registerLogger() deve essere chiamato prima dell'istanza della libreria. In caso contrario, i logger delle classi verranno impostati utilizzando il logger predefinito. Questo può essere un compromesso per alcune implementazioni. – rouble