/** * The class <strong>WaveBase</strong>. * * <p>This Bean is used to move wave's data through layer. It allow to manage priorities. */ public class WaveBase implements Wave, LinkMessages { /** The class logger. */ private static final JRLogger LOGGER = JRLoggerFactory.getLogger(WaveBase.class); /** The space separator. */ private static final String SPACE_SEP = " "; /** The Wave Unique Identifier. */ private final String wuid; /** The Wave timestamp. */ private final long timestamp; /** The wave status (can be bound). */ private final ObjectProperty<Status> statusProperty = new SimpleObjectProperty<>(Status.Created); /** The group of the wave used to dispatch the right event. */ private WaveGroup waveGroup = WaveGroup.UNDEFINED; /** The type of the wave used to call the right method name of the receiver object. */ private WaveType waveType; /** The from class to used for create waves. */ private Class<?> fromClass; /** The related component class to used for create waves. */ private Class<?> componentClass; /** The priority used to process wave according to a custom order. */ private int priority; /** * The related wave to the current wave, cold be a parent wave or child wave according context. */ private Wave relatedWave; /** A map used to contain all data. */ private final Map<WaveItem<?>, WaveData<?>> waveItemsMap = new HashMap<>(); /** A sorted list that contains all data. */ private final List<WaveData<?>> waveDataList = new ArrayList<>(); /** The wave bean. */ private Map<Class<? extends WaveBean>, WaveBean> waveBeanMap; /** The type extending WaveBean to use to embed some values. */ // private Class<? extends WaveBean> waveBeanClass; /** The list of wave Listener to warn when wave status changed. */ private final List<WaveListener> waveListeners = Collections.synchronizedList(new ArrayList<WaveListener>()); /** The list of Wave Handlers used to manage the Handled status. */ private List<? extends Object> waveHandlers; /** Default Constructor. */ WaveBase() { super(); // Generate a random but unique identifier this.wuid = UUID.randomUUID().toString(); // Store the creation date this.timestamp = System.currentTimeMillis(); } /** {@inheritDoc} */ @Override public WaveGroup waveGroup() { return this.waveGroup; } /** {@inheritDoc} */ @Override public Wave waveGroup(final WaveGroup waveGroup) { this.waveGroup = waveGroup; return this; } /** {@inheritDoc} */ @Override public WaveType waveType() { return this.waveType; } /** {@inheritDoc} */ @Override public Wave waveType(final WaveType waveType) { this.waveType = waveType; return this; } /** {@inheritDoc} */ @Override public Class<?> fromClass() { return this.fromClass; } /** {@inheritDoc} */ @Override public Wave fromClass(final Class<?> fromClass) { this.fromClass = fromClass; return this; } /** {@inheritDoc} */ @Override public Class<?> componentClass() { return this.componentClass; } /** {@inheritDoc} */ @Override public Wave componentClass(final Class<?> componentClass) { this.componentClass = componentClass; return this; } /** {@inheritDoc} */ @Override public int priority() { return this.priority; } /** {@inheritDoc} */ @Override public Wave priority(final int priority) { this.priority = priority; return this; } /** {@inheritDoc} */ @Override public Wave relatedWave() { return this.relatedWave; } /** {@inheritDoc} */ @Override public Wave relatedWave(final Wave nextWave) { this.relatedWave = nextWave; return this; } /** {@inheritDoc} */ @Override public List<WaveData<?>> waveDatas() { return this.waveDataList; } /** {@inheritDoc} */ @Override public Wave addDatas(final WaveData<?>... waveDatas) { for (final WaveData<?> waveData : waveDatas) { // Init the order of the wave Data waveData.setOrder(waveDatas().size()); // Grab the previous value if any final WaveData<?> previous = this.waveItemsMap.get(waveData.getKey()); // Store into the map to allow access by WaveItem this.waveItemsMap.put(waveData.getKey(), waveData); // Remove the old value from the list if (previous != null) { this.waveDataList.remove(previous); } // Add into the list to enable sorting this.waveDataList.add(waveData); // Sort the list Collections.sort(this.waveDataList); } return this; } /** {@inheritDoc} */ @Override public <T> Wave add(final WaveItem<T> waveItem, final T value) { final WaveData<T> waveData = Builders.waveData(waveItem, value); addDatas(waveData); return this; } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> WaveData<T> getData(final WaveItem<T> waveItem) { return (WaveData<T>) this.waveItemsMap.get(waveItem); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> T get(final WaveItem<T> waveItem) { return (T) (this.waveItemsMap.containsKey(waveItem) ? this.waveItemsMap.get(waveItem).getValue() : null); } /** {@inheritDoc} */ @Override public boolean contains(final WaveItem<?> waveItem) { return this.waveItemsMap.containsKey(waveItem); } /** {@inheritDoc} */ @Override public boolean containsNotNull(final WaveItem<?> waveItem) { return contains(waveItem) && getData(waveItem).getValue() != null; } /** {@inheritDoc} */ @Override public String getWUID() { return this.wuid; } /** {@inheritDoc} */ @Override public long getTimestamp() { return this.timestamp; } // /** // * {@inheritDoc} // */ // @Override // public WaveBean waveBean() { // if (this.waveBean == null) { // if (this.waveBeanClass == null || WaveBean.class.equals(this.waveBeanClass)) { // // Build an empty wave bean to avoid null pointer exception // this.waveBean = new DefaultWaveBean(); // } else { // try { // this.waveBean = this.waveBeanClass.newInstance(); // } catch (InstantiationException | IllegalAccessException e) { // LOGGER.error(WAVE_BEAN_CREATION_ERROR, e, this.waveBeanClass.toString()); // } finally { // if (this.waveBean == null) { // this.waveBean = new DefaultWaveBean(); // } // } // } // } // // return this.waveBean; // } // /** // * {@inheritDoc} // */ // @Override // public Wave waveBean(final WaveBean waveBean) { // if (waveBean != null) { // this.waveBean = waveBean; // this.waveBeanClass = waveBean.getClass(); // } // return this; // } /** {@inheritDoc} */ @Override public Wave waveBean(final WaveBean waveBean) { if (this.waveBeanMap == null) { this.waveBeanMap = new HashMap<>(); } this.waveBeanMap.put(waveBean.getClass(), waveBean); // this.waveBeanClass = waveBean.getClass(); return this; } /** {@inheritDoc} */ // @Override @Override public <WB extends WaveBean> WB waveBean(final Class<WB> waveBeanClass) { if (this.waveBeanMap == null) { this.waveBeanMap = new HashMap<>(); } if (!this.waveBeanMap.containsKey(waveBeanClass)) { try { final WB waveBean = waveBeanClass.newInstance(); this.waveBeanMap.put(waveBeanClass, waveBean); } catch (InstantiationException | IllegalAccessException e) { LOGGER.error(WAVE_BEAN_CREATION_ERROR, e, waveBeanClass.toString()); } finally { // if (this.waveBean == null) { // this.waveBean = new DefaultWaveBean(); // } } } return (WB) this.waveBeanMap.get(waveBeanClass); } // // /** // * @return Returns the waveBeanClass. // */ // public Class<? extends WaveBean> getWaveBeanClass() { // return this.waveBeanClass; // } // /** // * @param waveBeanClass The waveBeanClass to set. // */ // public void setWaveBeanClass(final Class<? extends WaveBean> waveBeanClass) { // this.waveBeanClass = waveBeanClass; // } /** {@inheritDoc} */ @Override public List<WaveBean> waveBeanList() { return new ArrayList<>(this.waveBeanMap.values()); } /** {@inheritDoc} */ @Override public Wave waveBeanList(final List<WaveBean> waveBeanList) { if (waveBeanList != null && !waveBeanList.isEmpty()) { if (this.waveBeanMap == null) { this.waveBeanMap = new HashMap<>(); } waveBeanList.forEach(wb -> this.waveBeanMap.put(wb.getClass(), wb)); } return this; } /** {@inheritDoc} */ @Override public Wave addWaveListener(final WaveListener waveListener) { this.waveListeners.add(waveListener); return this; } /** {@inheritDoc} */ @Override public Wave removeWaveListener(final WaveListener waveListener) { this.waveListeners.remove(waveListener); return this; } /** {@inheritDoc} */ @Override public Status status() { synchronized (this) { return this.statusProperty.get(); } } /** {@inheritDoc} */ @Override public ObjectProperty<Status> statusProperty() { return this.statusProperty; } /** {@inheritDoc} */ @Override public Wave status(final Status status) { synchronized (this) { if (this.statusProperty.get() == status) { // throw new CoreRuntimeException("The status " + status.toString() + " has been already set // for this wave " + toString()); } else { this.statusProperty.set(status); fireStatusChanged(); } } return this; } /** Fire a wave status change. */ private void fireStatusChanged() { // System.out.println("fireStatusChanged " + this.status.toString()); for (final WaveListener waveListener : this.waveListeners) { switch (this.statusProperty.get()) { case Created: waveListener.waveCreated(this); break; case Sent: waveListener.waveSent(this); break; case Processing: waveListener.waveProcessed(this); break; case Consumed: waveListener.waveConsumed(this); break; case Handled: waveListener.waveHandled(this); break; case Failed: waveListener.waveFailed(this); break; default: break; } } } /** {@inheritDoc} */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); if (waveGroup() != null) { sb.append(waveGroup()).append(SPACE_SEP); } if (fromClass() != null) { sb.append("fromClass=").append(fromClass().getSimpleName()).append(SPACE_SEP); } if (componentClass() != null) { sb.append("relatedClass=").append(componentClass().getSimpleName()).append(SPACE_SEP); } if (waveType() != null) { sb.append(waveType()).append(SPACE_SEP); } if (getWUID() != null) { sb.append("(").append(getWUID()).append(") "); } if (waveDatas().size() > 0) { sb.append("\r\nData=>"); for (final WaveData<?> wd : waveDatas()) { sb.append(wd.getKey()).append("=").append(wd.getValue()); } } return sb.toString(); } // /** // * {@inheritDoc} // */ // @Override // public Wave waveBean(final WaveBean waveBean) { // if (waveBean != null) { // this.waveBean = waveBean; // this.waveBeanClass = waveBean.getClass(); // } // return this; // } /** {@inheritDoc} */ @Override public void setWaveHandlers(final List<? extends Object> waveHandlers) { this.waveHandlers = waveHandlers; } /** {@inheritDoc} */ @Override public void removeWaveHandler(final Object waveHandler) { if (waveHandler != null) { // Remove the handler that has terminated this.waveHandlers.remove(waveHandler); // Update the status if required if (status() == Status.Consumed && this.waveHandlers.isEmpty()) { status(Status.Handled); } } } /** {@inheritDoc} */ @Override public boolean hasWaveBean(final Class<? extends WaveBean> waveBeanClass) { if (this.waveBeanMap != null && !this.waveBeanMap.isEmpty()) { return this.waveBeanMap.containsKey(waveBeanClass); } return false; } }
/** * The class <strong>JRebirthThreadPoolExecutor</strong> is used to to manage the JRebirth Thread * Pool (<b>JTP</b>). * * @author Sébastien Bordes */ public class JRebirthThreadPoolExecutor extends ThreadPoolExecutor implements ConcurrentMessages { /** The class logger. */ private static final JRLogger LOGGER = JRLoggerFactory.getLogger(JRebirthThreadPoolExecutor.class); /** The pending list of JRebirthRunnable. */ private final List<JRebirthRunnable> pending = new ArrayList<>(); /** * Default Constructor. * * @param threadNumber the number of thread managed by the pool * @param threadFactory the factory used to add a thread into the pool */ public JRebirthThreadPoolExecutor(final int threadNumber, final ThreadFactory threadFactory) { super( threadNumber, threadNumber, 0L, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(threadNumber, new JRebirthRunnableComparator()), threadFactory); } /** * Check if a slot is available for the given task priority. * * @param taskPriority the priority to check * @return true if this priority can be run right now */ public boolean checkAvailability(final RunnablePriority taskPriority) { // The next task could be added if: // _ a slot is available // _ the task has a lower priority than current executed return getActiveCount() < getCorePoolSize() || checkPriority(taskPriority); } /** * Check given priority with current pending list. * * @param taskPriority the priority to check * @return true if the priority is greater than those pending */ private boolean checkPriority(final RunnablePriority taskPriority) { boolean highPriority = false; for (final JRebirthRunnable jr : this.pending) { highPriority |= taskPriority.getLevel() > jr.getPriority().getLevel(); } return !highPriority; } // /** // * Execute the JRebirthRunnable. // * // * @param task to run // */ // public void execute(final JRebirthRunnable task) { // super.execute(task); // } /** {@inheritDoc} */ @Override protected void beforeExecute(final Thread t, final Runnable r) { this.pending.add((JRebirthRunnable) r); super.beforeExecute(t, r); } /** {@inheritDoc} */ @Override protected void afterExecute(final Runnable r, final Throwable t) { super.afterExecute(r, t); this.pending.remove(r); Throwable rootCause = null; if (t == null && r instanceof Future<?>) { try { final Object result = ((Future<?>) r).get(); if (result != null) { LOGGER.log(FUTURE_DONE, r.hashCode(), result.toString()); } } catch (final CancellationException | ExecutionException e) { rootCause = e.getCause(); } catch (final InterruptedException ie) { Thread.currentThread().interrupt(); // ignore/reset } } if (t != null) { LOGGER.log(JTP_ERROR, t); } if (rootCause != null) { LOGGER.log(JTP_ERROR_EXPLANATION, rootCause); } } }
/** * The class <strong>FXMLUtils</strong>. * * @author Sébastien Bordes */ public final class FXMLUtils implements FXMLMessages { /** The class logger. */ private static final JRLogger LOGGER = JRLoggerFactory.getLogger(FXMLUtils.class); /** Private constructor. */ private FXMLUtils() { // Nothing to do } /** * Load a FXML component without resource bundle. * * <p>The fxml path could be : * * <ul> * <li>Relative : fxml file will be loaded with the classloader of the given model class * <li>Absolute : fxml file will be loaded with default thread class loader, packages must be * separated by / character * </ul> * * @param model the model that will manage the fxml node * @param fxmlPath the fxml string path * @return a FXMLComponent object that wrap a fxml node with its controller * @param <M> the model type that will manage this fxml node */ public static <M extends Model> FXMLComponentBase loadFXML(final M model, final String fxmlPath) { return loadFXML(model, fxmlPath, null); } /** * Load a FXML component. * * <p>The fxml path could be : * * <ul> * <li>Relative : fxml file will be loaded with the classloader of the given model class * <li>Absolute : fxml file will be loaded with default thread class loader, packages must be * separated by / character * </ul> * * @param model the model that will manage the fxml node * @param fxmlPath the fxml string path * @param bundlePath the bundle string path * @return a FXMLComponent object that wrap a fxml node with its controller * @param <M> the model type that will manage this fxml node */ @SuppressWarnings("unchecked") public static <M extends Model> FXMLComponentBase loadFXML( final M model, final String fxmlPath, final String bundlePath) { final FXMLLoader fxmlLoader = new FXMLLoader(); // Use Custom controller factory to attach the root model to the controller fxmlLoader.setControllerFactory(new DefaultFXMLControllerBuilder(model)); fxmlLoader.setLocation(convertFxmlUrl(model, fxmlPath)); try { if (bundlePath != null) { fxmlLoader.setResources(ResourceBundle.getBundle(bundlePath)); } } catch (final MissingResourceException e) { LOGGER.log(MISSING_RESOURCE_BUNDLE, e, bundlePath); } Node node = null; boolean error = false; try { error = fxmlLoader.getLocation() == null; if (error) { node = TextBuilder.create().text(FXML_ERROR_NODE_LABEL.getText(fxmlPath)).build(); } else { node = (Node) fxmlLoader.load(fxmlLoader.getLocation().openStream()); } } catch (final IOException e) { throw new CoreRuntimeException(FXML_NODE_DOESNT_EXIST.getText(fxmlPath), e); } final FXMLController<M, ?> fxmlController = (FXMLController<M, ?>) fxmlLoader.getController(); // It's tolerated to have a null controller for an fxml node if (fxmlController != null) { // The fxml controller must extends AbstractFXMLController if (!error && !(fxmlLoader.getController() instanceof AbstractFXMLController)) { throw new CoreRuntimeException( BAD_FXML_CONTROLLER_ANCESTOR.getText( fxmlLoader.getController().getClass().getCanonicalName())); } // Link the View component with the fxml controller fxmlController.setModel(model); } return new FXMLComponentBase(node, fxmlController); } /** * Convert The url of fxml files to allow local and path loading. * * @param model the model class that will be used for relative loading * @param fxmlPath the path of the fxml file (relative or absolute) * @return the FXML file URL * @param <M> the model type that will manage this fxml node */ private static <M extends Model> URL convertFxmlUrl(final M model, final String fxmlPath) { URL fxmlUrl = null; // Replace all '.' separator by path separator '/' if (model != null) { // Try to load the resource from the same path as the model class fxmlUrl = model.getClass().getResource(fxmlPath); } if (fxmlUrl == null) { // Try to load the resource from the full path org/jrebirth/core/ui/Test.fxml fxmlUrl = Thread.currentThread().getContextClassLoader().getResource(fxmlPath); } return fxmlUrl; } }