/** * Get more info at : www.jrebirth.org . * Copyright JRebirth.org © 2011-2013 * Contact : sebastien.bordes@jrebirth.org * * Licensed 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.jrebirth.af.core.facade; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.stream.Collectors; import org.jrebirth.af.api.command.Command; import org.jrebirth.af.api.component.basic.Component; import org.jrebirth.af.api.exception.CoreException; import org.jrebirth.af.api.exception.CoreRuntimeException; import org.jrebirth.af.api.exception.JRebirthThreadException; import org.jrebirth.af.api.facade.FacadeReady; import org.jrebirth.af.api.facade.GlobalFacade; import org.jrebirth.af.api.facade.JRebirthEventType; import org.jrebirth.af.api.facade.LocalFacade; import org.jrebirth.af.api.key.UniqueKey; import org.jrebirth.af.api.log.JRLogger; import org.jrebirth.af.api.service.Service; import org.jrebirth.af.api.ui.Model; import org.jrebirth.af.core.key.Key; import org.jrebirth.af.core.log.JRLoggerFactory; /** * The class <strong>AbstractFacade</strong>. * * An abstract facade can manage singleton of object which implements the FacadeReady interface * * @author Sébastien Bordes * * @param <R> A type that implements FacadeReady */ public abstract class AbstractFacade<R extends FacadeReady<R>> extends AbstractGlobalReady implements LocalFacade<R>, FacadeMessages { /** The class logger. */ private static final JRLogger LOGGER = JRLoggerFactory.getLogger(AbstractFacade.class); /** The map that store FacadeReady singletons. */ private final Map<UniqueKey<? extends R>, WeakReference<R>> componentMap; /** * Default Constructor. * * @param globalFacade the global facade of the application */ public AbstractFacade(final GlobalFacade globalFacade) { super(globalFacade); // Initialize the synchronized map for singletons this.componentMap = Collections.synchronizedMap(new WeakHashMap<UniqueKey<? extends R>, WeakReference<R>>()); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <E extends R> void register(final UniqueKey<E> uniqueKey, final E readyObject) { // Synchronize the registration synchronized (this.componentMap) { // reload the key readyObject.setKey((UniqueKey<R>) uniqueKey); // Attach the facade to allow to retrieve any components readyObject.setLocalFacade(this); // Store the component into the singleton map this.componentMap.put(readyObject.getKey(), new WeakReference<R>(readyObject)); } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public <E extends R> void register(final E readyObject, final Object... keyPart) { register(Key.create((Class<R>) readyObject.getClass(), keyPart), readyObject); } /** * {@inheritDoc} */ @Override public <E extends R> void unregister(final UniqueKey<E> uniqueKey) { synchronized (this.componentMap) { final R readyObject = this.componentMap.get(uniqueKey).get(); if (readyObject != null) { // Unlisten all previously listened WaveType if (readyObject instanceof Component<?>) { try { getGlobalFacade().getNotifier().unlistenAll((Component<?>) readyObject); } catch (final JRebirthThreadException e) { LOGGER.error(UNLISTEN_ALL_ERROR, readyObject.getClass().getSimpleName(), e); } } // Release the key readyObject.setKey(null); // Release the facade link readyObject.setLocalFacade(null); } // Remove the component from the singleton map this.componentMap.remove(uniqueKey); } } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <E extends R> void unregister(final E readyObject, final Object... keyPart) { unregister(Key.create((Class<R>) readyObject.getClass(), keyPart)); } /** * {@inheritDoc} */ @Override public <E extends R> boolean exists(final UniqueKey<E> uniqueKey) { boolean res; synchronized (this.componentMap) { // Check from singleton map it he key exists and if the weak reference is not null res = this.componentMap.containsKey(uniqueKey) && this.componentMap.get(uniqueKey).get() != null; } return res; } /** * {@inheritDoc} */ @Override public <E extends R> boolean exists(final Class<E> clazz, final Object... keyPart) { return exists(Key.create(clazz, keyPart)); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <E extends R> List<E> retrieveAll(final UniqueKey<E> uniqueKey) { // TODO evaluate performances !!!!! return (List<E>) this.componentMap.entrySet().stream() .filter(entry -> entry.getKey().getClassField() == uniqueKey.getClassField()) .map(e -> e.getValue().get()).filter(e -> e != null) .collect(Collectors.toList()); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <E extends R> E retrieve(final UniqueKey<E> uniqueKey) { E readyObject = null; synchronized (this.componentMap) { // retrieve the component from the singleton map // It the component is already registered, get it to return it if (exists(uniqueKey)) { // If no key is provided retrieve from the singleton map // Extract the value from the weak reference readyObject = (E) this.componentMap.get(uniqueKey).get(); } } if (readyObject == null) { // If the component isn't contained into the component map, create and register it try { // Build the new instance of the component readyObject = buildComponent(uniqueKey); // Register it register(uniqueKey, readyObject); // The component is accessible from facade, let's start its initialization readyObject.setup(); } catch (final CoreException ce) { LOGGER.error(COMPONENT_RETRIEVAL_ERROR, ce); throw new CoreRuntimeException(ce); // Pop up the exception wrapped into a runtime exception } } return readyObject; } /** * {@inheritDoc} */ @Override public <E extends R> E retrieve(final Class<E> clazz, final Object... keyPart) { return retrieve(Key.create(clazz, keyPart)); } /** * Build a new instance of the ready object class. * * @param uniqueKey the unique key for the component to get * * @return a new instance of the given clazz and key * * @param <E> the type of the ready object to retrieve * * @throws CoreException if an error occurred */ @SuppressWarnings("unchecked") protected <E extends R> E buildComponent(final UniqueKey<E> uniqueKey) throws CoreException { // Build a new instance of the component final E readyObject = getGlobalFacade().getComponentFactory().buildComponent(uniqueKey.getClassField()); // Retrieve the right event type to track JRebirthEventType type = JRebirthEventType.NONE; if (readyObject instanceof Model) { type = JRebirthEventType.CREATE_MODEL; } else if (readyObject instanceof Service) { type = JRebirthEventType.CREATE_SERVICE; } else if (readyObject instanceof Command) { type = JRebirthEventType.CREATE_COMMAND; } // Track this instantiation event getGlobalFacade().trackEvent(type, this.getClass(), readyObject.getClass()); // Attach the local facade // Already Done by register method readyObject.setLocalFacade(this); // Create the unique key readyObject.setKey((UniqueKey<R>) uniqueKey); // EnhancedComponent Ready ! return readyObject; } }