001/* 002 * JGrapes Event Driven Framework 003 * Copyright (C) 2016-2018 Michael N. Lipp 004 * 005 * This program is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Affero General Public License as published by 007 * the Free Software Foundation; either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License along 016 * with this program; if not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jgrapes.core; 020 021import java.util.Map; 022import java.util.Optional; 023import java.util.concurrent.ConcurrentHashMap; 024 025/** 026 * Represents a subchannel. Subchannels delegate the invocations of a 027 * {@link Channel}'s methods to their respective main channel. Events fired on 028 * a subchannel are therefore handled by the framework as if they were fired on 029 * the main channel. Firing events on a subchannel instance instead of on the 030 * main channel is a means to associate several events with a common context. 031 */ 032public interface Subchannel extends Channel, Associator { 033 034 /** 035 * Returns the main channel. 036 * 037 * @return the mainChannel 038 */ 039 Channel mainChannel(); 040 041 /** 042 * Returns the main channel's match value. 043 * 044 * @see Channel#defaultCriterion() 045 */ 046 @Override 047 default Object defaultCriterion() { 048 return mainChannel().defaultCriterion(); 049 } 050 051 /** 052 * Delegates to main channel. 053 * 054 * @see Channel#isEligibleFor(Object) 055 */ 056 @Override 057 default boolean isEligibleFor(Object value) { 058 return mainChannel().isEligibleFor(value); 059 } 060 061 /** 062 * Returns a string representation of the channel. 063 * 064 * @param subchannel the subchannel 065 * @return the string 066 */ 067 static String toString(Subchannel subchannel) { 068 StringBuilder builder = new StringBuilder(); 069 builder.append(Channel.toString(subchannel.mainChannel())) 070 .append('{') 071 .append(Components.objectName(subchannel)) 072 .append('}'); 073 return builder.toString(); 074 } 075 076 /** 077 * Creates a new subchannel of the given component's channel. 078 * 079 * @param component the component used to get the main channel 080 * @return the subchannel 081 */ 082 static Subchannel create(Component component) { 083 return new DefaultSubchannel(component.channel()); 084 } 085 086 /** 087 * A simple implementation of {@link Subchannel}. 088 */ 089 class DefaultSubchannel implements Subchannel { 090 private final Channel mainChannel; 091 private Map<Object, Object> contextData; 092 093 /** 094 * Creates a new instance with the given main channel and response 095 * pipeline. 096 * 097 * @param mainChannel the main channel 098 */ 099 public DefaultSubchannel(Channel mainChannel) { 100 this.mainChannel = mainChannel; 101 } 102 103 /* 104 * (non-Javadoc) 105 * 106 * @see org.jgrapes.core.Subchannel#getMainChannel() 107 */ 108 @Override 109 public Channel mainChannel() { 110 return mainChannel; 111 } 112 113 /** 114 * Establishes a "named" association to an associated object. Note that 115 * anything that represents an id can be used as value for 116 * parameter `name`, it does not necessarily have to be a string. 117 * 118 * @param by the "name" 119 * @param with the object to be associated 120 */ 121 @SuppressWarnings({ "PMD.ShortVariable", "unchecked" }) 122 public <A extends Associator> A setAssociated(Object by, Object with) { 123 if (contextData == null) { 124 contextData = new ConcurrentHashMap<>(); 125 } 126 if (with == null) { 127 contextData.remove(by); 128 } else { 129 contextData.put(by, with); 130 } 131 return (A) this; 132 } 133 134 /** 135 * Retrieves the associated object following the association 136 * with the given "name". This general version of the method 137 * supports the retrieval of values of arbitrary types 138 * associated by any "name" types. 139 * 140 * @param by the "name" 141 * @param type the tape of the value to be retrieved 142 * @param <V> the type of the value to be retrieved 143 * @return the associate, if any 144 */ 145 @SuppressWarnings("PMD.ShortVariable") 146 public <V> Optional<V> associated(Object by, Class<V> type) { 147 if (contextData == null) { 148 return Optional.empty(); 149 } 150 return Optional.ofNullable(contextData.get(by)) 151 .filter(found -> type.isAssignableFrom(found.getClass())) 152 .map(type::cast); 153 } 154 155 /* 156 * (non-Javadoc) 157 * 158 * @see java.lang.Object#toString() 159 */ 160 @Override 161 public String toString() { 162 return Subchannel.toString(this); 163 } 164 165 } 166}