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