Java tutorial
/* * Demoiselle Framework * Copyright (C) 2010 SERPRO * ---------------------------------------------------------------------------- * This file is part of Demoiselle Framework. * * Demoiselle Framework is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License version 3 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License version 3 * along with this program; if not, see <http://www.gnu.org/licenses/> * or write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301, USA. * ---------------------------------------------------------------------------- * Este arquivo parte do Framework Demoiselle. * * O Framework Demoiselle um software livre; voc pode redistribu-lo e/ou * modific-lo dentro dos termos da GNU LGPL verso 3 como publicada pela Fundao * do Software Livre (FSF). * * Este programa distribudo na esperana que possa ser til, mas SEM NENHUMA * GARANTIA; sem uma garantia implcita de ADEQUAO a qualquer MERCADO ou * APLICAO EM PARTICULAR. Veja a Licena Pblica Geral GNU/LGPL em portugus * para maiores detalhes. * * Voc deve ter recebido uma cpia da GNU LGPL verso 3, sob o ttulo * "LICENCA.txt", junto com esse programa. Se no, acesse <http://www.gnu.org/licenses/> * ou escreva para a Fundao do Software Livre (FSF) Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA. */ package br.gov.frameworkdemoiselle.internal.interceptor; import java.lang.reflect.Type; import java.util.Date; import java.util.Iterator; import java.util.Locale; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import br.gov.frameworkdemoiselle.DemoiselleException; import br.gov.frameworkdemoiselle.annotation.Name; import br.gov.frameworkdemoiselle.internal.bootstrap.CoreBootstrap; import br.gov.frameworkdemoiselle.internal.implementation.CoreBundle; import br.gov.frameworkdemoiselle.internal.implementation.DefaultAuditInfo; import br.gov.frameworkdemoiselle.internal.implementation.DefaultAuditor; import br.gov.frameworkdemoiselle.security.AuditInfo; import br.gov.frameworkdemoiselle.security.Auditable; import br.gov.frameworkdemoiselle.security.Auditor; import br.gov.frameworkdemoiselle.security.RequiredPermission; import br.gov.frameworkdemoiselle.security.SecurityContext; import br.gov.frameworkdemoiselle.security.User; import br.gov.frameworkdemoiselle.util.ResourceBundle; import br.gov.frameworkdemoiselle.util.Strings; /** * This is the default interceptor for Auditable elements. This interceptor will * look for registered implementations for {@link Auditor} interface. For each * registered auditor, an {@link AuditInfo} implementation will be looked up, * and if a match is found than the auditor's {@link Auditor#audit(AuditInfo)} * method will be called. * * @author SERPRO * */ @Interceptor @Auditable public class AuditableInterceptor { private final ResourceBundle bundle; private final Logger log; private final Instance<Auditor> auditorInstances; private final Instance<AuditInfo> auditInfoInstances; private final Instance<SecurityContext> securityContext; @Inject public AuditableInterceptor(Instance<Auditor> auditor, Instance<AuditInfo> auditInfoInstances, Instance<SecurityContext> securityContext, Logger log, @Name("demoiselle-core-bundle") ResourceBundle bundle) { this.log = log; this.bundle = bundle; this.auditorInstances = auditor; this.auditInfoInstances = auditInfoInstances; this.securityContext = securityContext; } @AroundInvoke public Object audit(InvocationContext ic) throws Exception { // TODO: Use the bundle to log information String operation = getOperation(ic); if (log.isDebugEnabled()) { log.debug("Starting auditing on operation : " + operation); } // TODO: This has to be parameterized with some form if ("getDelegate".equals(operation)) { return ic.proceed(); } if (!auditorInstances.isUnsatisfied()) { try { throwExceptionIfHasOnlyDefaultAuditorInstances(); throwExceptionIfHasOnlyDefaultAuditInfoInstances(); for (Iterator<Auditor> it = auditorInstances.iterator(); it.hasNext();) { Auditor internalAuditor = it.next(); Class<?> internalAuditorClass = internalAuditor.getClass(); // Proxy classes are skiped. The correct implementation is // used. if (isAuditorAProxy(internalAuditorClass)) { internalAuditorClass = (Class<?>) internalAuditorClass.getSuperclass(); } if (isAuditorAllowed(internalAuditorClass)) { // Other Auditor instances exists or were already // processed. continue; } String auditorClass = internalAuditorClass.getName(); Class<?> auditorClassTypeParameter = getAuditorTargetResource(internalAuditorClass); if (log.isTraceEnabled()) { log.trace("--------------------------------------------"); log.trace("Current InternalAuditor instance: " + auditorClass); log.trace("Current AuditorClassTypeParameter: " + auditorClassTypeParameter); log.trace("--------------------------------------------"); } for (Iterator<AuditInfo> ita = auditInfoInstances.iterator(); ita.hasNext();) { AuditInfo auditInfo = ita.next(); Class<?> auditInfoClass = auditInfo.getClass(); if (DefaultAuditInfo.class.isAssignableFrom(auditInfoClass)) { continue; } Class<?> auditInfoClassTypeParameter = getAuditorTargetResource(auditInfoClass); if (log.isTraceEnabled()) { log.trace("--------------------------------------------"); log.trace("Current AuditInfoClass: " + auditInfoClass); log.trace("Current AuditInfoClassTypeParameter: " + auditInfoClassTypeParameter); log.trace("--------------------------------------------"); } // We only process audit info instance whose type // parameter matches the // type parameter for the auditor. if (auditorClassTypeParameter == auditInfoClassTypeParameter) { log.debug("auditorClassTypeParameter == auditInfoClassTypeParameter: " + auditorClassTypeParameter + " : " + auditInfoClassTypeParameter); log.debug("TypeParameters Match. Processing auditInfo instance:" + auditInfo + " ..."); processAuditInfo(ic, operation, internalAuditor, auditorClass, auditInfo); } else { // Do not process this audit info for the current // auditor. log.debug("TypeParameters doen't match. Skiping this auditInfo (" + auditInfoClass + ") for this auditor (" + auditorClass + ")"); continue; } } log.debug("All AuditInfo processed for auditor: " + auditorClass); } } catch (DemoiselleException ex) { // TODO: Move message to bundle log.error("Instance of : " + getType(ic) + " is annotated with Auditable but without correct configuration!"); log.error(ex.toString(), ex); throw ex; } catch (Exception ex) { log.error(ex.toString(), ex); throw ex; } } else { log.warn("No instances of br.gov.frameworkdemoiselle.security.Auditor found on classpath!"); } return ic.proceed(); } private void throwExceptionIfHasOnlyDefaultAuditInfoInstances() { if (hasOnlyDefaultAuditInfoInstances(auditInfoInstances)) { throw new DemoiselleException( CoreBundle.get().getString("audit-info-not-defined", Auditor.class.getSimpleName())); } } private void throwExceptionIfHasOnlyDefaultAuditorInstances() { if (hasOnlyDefaultAuditorInstances(auditorInstances)) { throw new DemoiselleException( CoreBundle.get().getString("auditor-not-defined", Auditor.class.getSimpleName())); } } /** * @param internalAuditorClass * @return * @throws Exception */ private Class<?> getAuditorTargetResource(Class<?> internalAuditorClass) throws Exception { Class<?> auditorClassTypeParameter = getFirstInterfaceTypeParameter(internalAuditorClass); return auditorClassTypeParameter; } /** * @param internalAuditorClass * @return */ private boolean isAuditorAProxy(Class<?> internalAuditorClass) { return internalAuditorClass.getName().toLowerCase(Locale.ENGLISH).contains("$proxy$"); } /** * @param internalAuditorClass * @return */ private boolean isAuditorAllowed(Class<?> internalAuditorClass) { return DefaultAuditor.class.isAssignableFrom(internalAuditorClass); } private boolean hasOnlyDefaultAuditInfoInstances(Instance<AuditInfo> auditInfoInstances) { for (Iterator<AuditInfo> it = auditInfoInstances.iterator(); it.hasNext();) { AuditInfo internalAuditInfo = it.next(); if (!DefaultAuditInfo.class.isAssignableFrom(internalAuditInfo.getClass())) { return false; } } return true; } private Class<?> getFirstInterfaceTypeParameter(Class<?> internalAuditorClass) throws Exception { Type genericInterface = internalAuditorClass.getGenericInterfaces()[0]; if (genericInterface != null) { String gStr = genericInterface.toString(); String strType = StringUtils.EMPTY; if (gStr.indexOf('<') != -1) { strType = gStr.substring(gStr.indexOf('<') + 1, gStr.indexOf('>')); } if (StringUtils.isNotEmpty(strType)) { return Class.forName(strType); } } return null; } private boolean hasOnlyDefaultAuditorInstances(Instance<Auditor> auditorInstances) { for (Iterator<Auditor> it = auditorInstances.iterator(); it.hasNext();) { Auditor internalAuditor = it.next(); if (!DefaultAuditor.class.isAssignableFrom(internalAuditor.getClass())) { return false; } } return true; } /** * @param ic * @param operation * @param internalAuditor * @param auditorClass * @param auditInfo * @throws Exception */ private void processAuditInfo(InvocationContext ic, String operation, Auditor internalAuditor, String auditorClass, AuditInfo auditInfo) throws Exception { setAuditInfoProperties(ic, operation, auditInfo); try { // Asking the auditorInstances to implement de audit operation internalAuditor.audit(auditInfo); } catch (RuntimeException ex) { log.error(ex.toString(), ex); throw ex; } } /** * @param ic * @param operation * @param auditInfo * @throws Exception */ private void setAuditInfoProperties(InvocationContext ic, String operation, AuditInfo<?> auditInfo) throws Exception { if (auditInfo != null) { auditInfo.setOperation(operation); auditInfo.setParameters(ic.getParameters()); auditInfo.setTarget(ic.getTarget()); auditInfo.setTargetType(getType(ic)); // TODO: Should we use the br.gov.frameworkdemoiselle.security.User // interface? auditInfo.setUser(getUsername()); auditInfo.setResource(getResource(ic)); auditInfo.setDateTime(new Date()); } } /** * Returns the id of the currently logged in user. * * @return the id of the currently logged in user */ private String getUsername() { String username = ""; if (securityContext.isUnsatisfied()) { log.warn("No instances of br.gov.frameworkdemoiselle.security.SecurityContext found on classpath!"); return username; } User user = securityContext.get().getUser(); if (user != null && user.getId() != null) { username = user.getId(); } return username; } /** * Returns the resource defined in {@code @Auditable} annotation, the name * defined in {@code @Name} annotation or the class name itself * * @param ic * the {@code InvocationContext} in which the method is being * called * @return the resource defined in {@code @RequiredPermission} annotation, * the name defined in {@code @Name} annotation or the class name * itself */ private String getResource(InvocationContext ic) { Auditable auditable = ic.getMethod().getAnnotation(Auditable.class); if (auditable == null || Strings.isEmpty(auditable.resource())) { if (ic.getTarget().getClass().getAnnotation(Name.class) == null) { // Returning resource Target.class.simpleName...; return getType(ic).getName(); } else { // Returning resource Target.class.annotation(Name).value...; return ic.getTarget().getClass().getAnnotation(Name.class).value(); } } else { // Returning annotation defined resource... return auditable.resource(); } } private String getOperation(InvocationContext ic) { Auditable auditable = ic.getMethod().getAnnotation(Auditable.class); if (auditable == null || Strings.isEmpty(auditable.resource())) { if (ic.getMethod().getAnnotation(Name.class) == null) { return ic.getMethod().getName(); } else { return ic.getMethod().getAnnotation(Name.class).value(); } } else { return auditable.operation(); } } private final Class<?> getType(final InvocationContext ic) { Class<?> type = ic.getTarget().getClass(); type = getTargetType(type); return type; } public static Class<?> getTargetType(Class<?> type) { if (!CoreBootstrap.isAnnotatedType(type)) { type = type.getSuperclass(); } return type; } }