/* * Copyright 2012 PRODYNA AG * * Licensed under the Eclipse Public License (EPL), Version 1.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.opensource.org/licenses/eclipse-1.0.php or * http://www.nabucco.org/License.html * * 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.nabucco.framework.common.dynamiccode.impl.service.common.aspect.resolving; import org.nabucco.common.cache.CacheFactory; import org.nabucco.common.cache.CacheManager; import org.nabucco.common.cache.CacheType; import org.nabucco.common.cache.TenantCache; import org.nabucco.framework.base.facade.component.connection.ConnectionException; import org.nabucco.framework.base.facade.datatype.Datatype; import org.nabucco.framework.base.facade.datatype.Tenant; import org.nabucco.framework.base.facade.datatype.code.Code; import org.nabucco.framework.base.facade.datatype.logger.NabuccoLogger; import org.nabucco.framework.base.facade.datatype.logger.NabuccoLoggingFactory; import org.nabucco.framework.base.facade.datatype.property.DatatypeProperty; import org.nabucco.framework.base.facade.datatype.property.NabuccoProperty; import org.nabucco.framework.base.facade.datatype.property.NabuccoPropertyType; import org.nabucco.framework.base.facade.datatype.security.UserId; import org.nabucco.framework.base.facade.datatype.visitor.VisitorException; import org.nabucco.framework.base.facade.exception.security.SecurityException; import org.nabucco.framework.base.facade.exception.service.ServiceException; import org.nabucco.framework.base.facade.message.ServiceRequest; import org.nabucco.framework.base.facade.message.ServiceResponse; import org.nabucco.framework.base.facade.message.context.ServiceMessageContext; import org.nabucco.framework.base.facade.message.visitor.ServiceMessageVisitor; import org.nabucco.framework.common.dynamiccode.facade.component.DynamicCodeComponent; import org.nabucco.framework.common.dynamiccode.facade.component.DynamicCodeComponentLocator; import org.nabucco.framework.common.dynamiccode.facade.datatype.DynamicCodeCode; import org.nabucco.framework.common.dynamiccode.facade.message.resolve.DynamicCodeCodeResolveRq; import org.nabucco.framework.common.dynamiccode.facade.message.resolve.DynamicCodeCodeResolveRs; import org.nabucco.framework.common.dynamiccode.facade.service.resolve.ResolveDynamicCode; /** * DynamicCodeResolvingVisitor * * @author Nicolas Moser, PRODYNA AG */ public class DynamicCodeResolvingVisitor extends ServiceMessageVisitor { private static final String CACHE_NAME = "CodeResolvingAspectCache"; /** Logger */ private static NabuccoLogger logger = NabuccoLoggingFactory.getInstance().getLogger( DynamicCodeResolvingVisitor.class); /** The Tenant Enabled Cache */ private static TenantCache<Code> cache; /** The cache timeout */ private long cacheTimeout = 1800000; /** The service context */ private ServiceMessageContext context; /** * Creates a new {@link DynamicCodeResolvingVisitor} instance. * * @param timeout * the cache timeout * @param context * the service context */ public DynamicCodeResolvingVisitor(long timeout, ServiceMessageContext context) { cacheTimeout = timeout; this.context = context; } @Override public void visit(Datatype datatype) throws VisitorException { if (datatype != null) { this.resolveDatatype(datatype); } super.visit(datatype); } /** * Resolve all codes of the given datatype. * * @param datatype * the datatype to resolve * * @throws VisitorException * when the datatype cannot be resolved */ private void resolveDatatype(Datatype datatype) throws VisitorException { for (NabuccoProperty property : datatype.getProperties()) { if (property.getPropertyType() != NabuccoPropertyType.DATATYPE) { continue; } if (!Code.class.isAssignableFrom(property.getType())) { continue; } DatatypeProperty codeProperty = (DatatypeProperty) property; Long refId = codeProperty.getReferenceId(); if (refId == null || refId.equals(0L)) { continue; } try { Code code = this.resolveCode(refId); NabuccoProperty newProperty = codeProperty.createProperty(code); datatype.setProperty(newProperty); } catch (ServiceException se) { logger.error(se, "Error resolving code for id '", refId, "'."); } catch (ConnectionException ce) { logger.error(ce, "Error connecting to Dynamic Code Component."); throw new VisitorException("Error connecting to Dynamic Code Component.", ce); } catch (Exception e) { logger.error(e, "Error resolving code for id '", refId, "'."); throw new VisitorException("Error resolving code for id '" + refId + "'.", e); } } } /** * Resolve the code with the given id. * * @param id * id of the code * * @return the resolved code * * @throws SecurityException * when the current user is not authorized to access the code * @throws ServiceException * when an error during the resolve service occurs * @throws ConnectionException * when the dynamic code compoennt is not accessible */ private Code resolveCode(Long id) throws SecurityException, ServiceException, ConnectionException { if (id == null) { return null; } String key = id.toString(); String tenant = this.getTenant(); if (getCache().contains(tenant, key)) { return getCache().retrieve(tenant, key); } DynamicCodeComponent component = DynamicCodeComponentLocator.getInstance().getComponent(); ResolveDynamicCode resolveService = component.getResolveDynamicCode(); ServiceRequest<DynamicCodeCodeResolveRq> rq = new ServiceRequest<DynamicCodeCodeResolveRq>(context); DynamicCodeCodeResolveRq msg = new DynamicCodeCodeResolveRq(); DynamicCodeCode code = new DynamicCodeCode(); code.setId(id); msg.setCode(code); rq.setRequestMessage(msg); ServiceResponse<DynamicCodeCodeResolveRs> rs = resolveService.resolveDynamicCodeCode(rq); if (rs == null || rs.getResponseMessage() == null) { throw new IllegalStateException("Cannot resolve code with id '" + id + "'."); } code = rs.getResponseMessage().getCode(); if (code != null && code.getId() != null) { getCache().store(tenant, code.getId().toString(), code, cacheTimeout); } return code; } /** * Retrieve the tenant from the current service context. * * @return the current tenant * * @throws SecurityException * when the current user is not authorized to access the cache */ private String getTenant() throws SecurityException { if (context == null) { throw new IllegalStateException("Service Request Context [null] is not valid."); } if (context.getSubject() == null || context.getSubject().getUser() == null) { throw new SecurityException("User 'null' is not authorized to access the cache."); } UserId user = context.getSubject().getUserId(); Tenant tenant = context.getSubject().getTenant(); if (tenant == null || tenant.getValue() == null) { logger.error("User '" + user + "' is not authorized to access the code resolver cache."); throw new SecurityException("User '" + user + "' is not authorized to access the code resolver cache."); } return tenant.getValue(); } /** * Retrieve the cache. * * @return the cache */ private static synchronized final TenantCache<Code> getCache() { if (cache == null) { CacheFactory<Code> factory = new CacheFactory<Code>(); CacheManager manager = new CacheManager(CACHE_NAME); cache = factory.newTenantCache(CacheType.SIMPLE, manager); } return cache; } }