1/* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.oracle.webservices.internal.api.message; 27 28import com.sun.istack.internal.NotNull; 29import com.sun.istack.internal.Nullable; 30import com.sun.xml.internal.ws.api.message.Packet; 31import com.sun.xml.internal.ws.client.RequestContext; 32import com.sun.xml.internal.ws.client.ResponseContext; 33 34import javax.xml.ws.WebServiceContext; 35 36import java.util.AbstractMap; 37import java.util.Map.Entry; 38import java.util.HashSet; 39import java.util.IdentityHashMap; 40import java.util.Map; 41import java.util.Set; 42 43/** 44 * {@link PropertySet} that combines properties exposed from multiple 45 * {@link PropertySet}s into one. 46 * 47 * <p> 48 * This implementation allows one {@link PropertySet} to assemble 49 * all properties exposed from other "satellite" {@link PropertySet}s. 50 * (A satellite may itself be a {@link DistributedPropertySet}, so 51 * in general this can form a tree.) 52 * 53 * <p> 54 * This is useful for JAX-WS because the properties we expose to the application 55 * are contributed by different pieces, and therefore we'd like each of them 56 * to have a separate {@link PropertySet} implementation that backs up 57 * the properties. For example, this allows FastInfoset to expose its 58 * set of properties to {@link RequestContext} by using a strongly-typed fields. 59 * 60 * <p> 61 * This is also useful for a client-side transport to expose a bunch of properties 62 * into {@link ResponseContext}. It simply needs to create a {@link PropertySet} 63 * object with methods for each property it wants to expose, and then add that 64 * {@link PropertySet} to {@link Packet}. This allows property values to be 65 * lazily computed (when actually asked by users), thus improving the performance 66 * of the typical case where property values are not asked. 67 * 68 * <p> 69 * A similar benefit applies on the server-side, for a transport to expose 70 * a bunch of properties to {@link WebServiceContext}. 71 * 72 * <p> 73 * To achieve these benefits, access to {@link DistributedPropertySet} is slower 74 * compared to {@link PropertySet} (such as get/set), while adding a satellite 75 * object is relatively fast. 76 * 77 * @author Kohsuke Kawaguchi 78 */ 79public abstract class BaseDistributedPropertySet extends BasePropertySet implements DistributedPropertySet { 80 81 /** 82 * All {@link PropertySet}s that are bundled into this {@link PropertySet}. 83 */ 84 private final Map<Class<? extends com.oracle.webservices.internal.api.message.PropertySet>, PropertySet> satellites 85 = new IdentityHashMap<Class<? extends com.oracle.webservices.internal.api.message.PropertySet>, PropertySet>(); 86 87 private final Map<String, Object> viewthis; 88 89 public BaseDistributedPropertySet() { 90 this.viewthis = super.createView(); 91 } 92 93 @Override 94 public void addSatellite(@NotNull PropertySet satellite) { 95 addSatellite(satellite.getClass(), satellite); 96 } 97 98 @Override 99 public void addSatellite(@NotNull Class<? extends com.oracle.webservices.internal.api.message.PropertySet> keyClass, @NotNull PropertySet satellite) { 100 satellites.put(keyClass, satellite); 101 } 102 103 @Override 104 public void removeSatellite(PropertySet satellite) { 105 satellites.remove(satellite.getClass()); 106 } 107 108 public void copySatelliteInto(@NotNull DistributedPropertySet r) { 109 for (Map.Entry<Class<? extends com.oracle.webservices.internal.api.message.PropertySet>, PropertySet> entry : satellites.entrySet()) { 110 r.addSatellite(entry.getKey(), entry.getValue()); 111 } 112 } 113 114 @Override 115 public void copySatelliteInto(MessageContext r) { 116 copySatelliteInto((DistributedPropertySet)r); 117 } 118 119 @Override 120 public @Nullable <T extends com.oracle.webservices.internal.api.message.PropertySet> T getSatellite(Class<T> satelliteClass) { 121 T satellite = (T) satellites.get(satelliteClass); 122 if (satellite != null) { 123 return satellite; 124 } 125 126 for (PropertySet child : satellites.values()) { 127 if (satelliteClass.isInstance(child)) { 128 return satelliteClass.cast(child); 129 } 130 131 if (DistributedPropertySet.class.isInstance(child)) { 132 satellite = DistributedPropertySet.class.cast(child).getSatellite(satelliteClass); 133 if (satellite != null) { 134 return satellite; 135 } 136 } 137 } 138 return null; 139 } 140 141 @Override 142 public Map<Class<? extends com.oracle.webservices.internal.api.message.PropertySet>, com.oracle.webservices.internal.api.message.PropertySet> getSatellites() { 143 return satellites; 144 } 145 146 @Override 147 public Object get(Object key) { 148 // check satellites 149 for (PropertySet child : satellites.values()) { 150 if (child.supports(key)) { 151 return child.get(key); 152 } 153 } 154 155 // otherwise it must be the master 156 return super.get(key); 157 } 158 159 @Override 160 public Object put(String key, Object value) { 161 // check satellites 162 for (PropertySet child : satellites.values()) { 163 if(child.supports(key)) { 164 return child.put(key,value); 165 } 166 } 167 168 // otherwise it must be the master 169 return super.put(key,value); 170 } 171 172 @Override 173 public boolean containsKey(Object key) { 174 if (viewthis.containsKey(key)) 175 return true; 176 for (PropertySet child : satellites.values()) { 177 if (child.containsKey(key)) { 178 return true; 179 } 180 } 181 return false; 182 } 183 184 @Override 185 public boolean supports(Object key) { 186 // check satellites 187 for (PropertySet child : satellites.values()) { 188 if (child.supports(key)) { 189 return true; 190 } 191 } 192 193 return super.supports(key); 194 } 195 196 @Override 197 public Object remove(Object key) { 198 // check satellites 199 for (PropertySet child : satellites.values()) { 200 if (child.supports(key)) { 201 return child.remove(key); 202 } 203 } 204 205 return super.remove(key); 206 } 207 208 @Override 209 protected void createEntrySet(Set<Entry<String, Object>> core) { 210 super.createEntrySet(core); 211 for (PropertySet child : satellites.values()) { 212 ((BasePropertySet) child).createEntrySet(core); 213 } 214 } 215 216 protected Map<String, Object> asMapLocal() { 217 return viewthis; 218 } 219 220 protected boolean supportsLocal(Object key) { 221 return super.supports(key); 222 } 223 224 class DistributedMapView extends AbstractMap<String, Object> { 225 @Override 226 public Object get(Object key) { 227 for (PropertySet child : satellites.values()) { 228 if (child.supports(key)) { 229 return child.get(key); 230 } 231 } 232 233 return viewthis.get(key); 234 } 235 236 @Override 237 public int size() { 238 int size = viewthis.size(); 239 for (PropertySet child : satellites.values()) { 240 size += child.asMap().size(); 241 } 242 return size; 243 } 244 245 @Override 246 public boolean containsKey(Object key) { 247 if (viewthis.containsKey(key)) 248 return true; 249 for (PropertySet child : satellites.values()) { 250 if (child.asMap().containsKey(key)) 251 return true; 252 } 253 return false; 254 } 255 256 @Override 257 public Set<Entry<String, Object>> entrySet() { 258 Set<Entry<String, Object>> entries = new HashSet<Entry<String, Object>>(); 259 for (PropertySet child : satellites.values()) { 260 for (Entry<String,Object> entry : child.asMap().entrySet()) { 261 // the code below is here to avoid entries.addAll(child.asMap().entrySet()); which works differently on JDK6/7 262 // see DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS 263 entries.add(new SimpleImmutableEntry<String, Object>(entry.getKey(), entry.getValue())); 264 } 265 } 266 for (Entry<String,Object> entry : viewthis.entrySet()) { 267 // the code below is here to avoid entries.addAll(child.asMap().entrySet()); which works differently on JDK6/7 268 // see DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS 269 entries.add(new SimpleImmutableEntry<String, Object>(entry.getKey(), entry.getValue())); 270 } 271 272 return entries; 273 } 274 275 @Override 276 public Object put(String key, Object value) { 277 for (PropertySet child : satellites.values()) { 278 if (child.supports(key)) { 279 return child.put(key, value); 280 } 281 } 282 283 return viewthis.put(key, value); 284 } 285 286 @Override 287 public void clear() { 288 satellites.clear(); 289 viewthis.clear(); 290 } 291 292 @Override 293 public Object remove(Object key) { 294 for (PropertySet child : satellites.values()) { 295 if (child.supports(key)) { 296 return child.remove(key); 297 } 298 } 299 300 return viewthis.remove(key); 301 } 302 } 303 304 @Override 305 protected Map<String, Object> createView() { 306 return new DistributedMapView(); 307 } 308} 309