Bug 5540 - ConvertorManager optimization, concurrency
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / ConvertorManager.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Objects;
16 import java.util.Optional;
17 import java.util.concurrent.ConcurrentHashMap;
18 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.action.ActionConvertor;
19 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.action.ActionResponseConvertor;
20 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.Convertor;
21 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.ConvertorData;
22 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.ParametrizedConvertor;
23 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.FlowConvertor;
24 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.FlowInstructionResponseConvertor;
25 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.FlowStatsResponseConvertor;
26 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchResponseConvertor;
27 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchV10ResponseConvertor;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * Manages various convertors and allows to use them all in one generic way
33  */
34 public class ConvertorManager implements ConvertorRegistrator, ConvertorExecutor {
35     private static final Logger LOG = LoggerFactory.getLogger(ConvertorManager.class);
36     private static ConvertorManager INSTANCE;
37
38     static {
39         INSTANCE = new ConvertorManager();
40         // All convertors are registered here
41         INSTANCE.registerConvertor(new TableFeaturesConvertor());
42         INSTANCE.registerConvertor(new TableFeaturesResponseConvertor());
43         INSTANCE.registerConvertor(new MeterConvertor());
44         INSTANCE.registerConvertor(new MeterStatsResponseConvertor());
45         INSTANCE.registerConvertor(new MeterConfigStatsResponseConvertor());
46         INSTANCE.registerConvertor(new PortConvertor());
47         // TODO: Add MatchConvertor
48         INSTANCE.registerConvertor(new MatchResponseConvertor());
49         INSTANCE.registerConvertor(new MatchV10ResponseConvertor());
50         INSTANCE.registerConvertor(new ActionConvertor());
51         INSTANCE.registerConvertor(new ActionResponseConvertor());
52         INSTANCE.registerConvertor(new GroupConvertor());
53         INSTANCE.registerConvertor(new GroupDescStatsResponseConvertor());
54         INSTANCE.registerConvertor(new GroupStatsResponseConvertor());
55         INSTANCE.registerConvertor(new PacketOutConvertor());
56         INSTANCE.registerConvertor(new FlowConvertor());
57         INSTANCE.registerConvertor(new FlowInstructionResponseConvertor());
58         INSTANCE.registerConvertor(new FlowStatsResponseConvertor());
59     }
60
61     // Actual convertor keys
62     private List<Class<?>> convertorKeys = new ArrayList<>();
63     private List<Class<?>> parametrizedConvertorKeys = new ArrayList<>();
64
65     // Cache, that holds all registered convertors, but they can have multiple keys,
66     // based on instanceof checks in the convert method
67     private Map<Class<?>, Convertor> convertors = new ConcurrentHashMap<>();
68     private Map<Class<?>, ParametrizedConvertor> parametrizedConvertors = new ConcurrentHashMap<>();
69
70     private ConvertorManager() {
71         // Hiding implicit constructor
72     }
73
74     /**
75      * Gets instance of Convertor Manager.
76      *
77      * @return the instance
78      */
79     public static ConvertorManager getInstance() {
80         return INSTANCE;
81     }
82
83     @Override
84     public Convertor registerConvertor(final Convertor convertor) {
85         final Class<?> type = convertor.getType();
86         final Convertor result = convertors.get(type);
87
88         if (Objects.isNull(result)) {
89             convertorKeys.add(type);
90             convertors.put(type, convertor);
91             LOG.debug("{} is now converted by {}", type, convertor);
92         } else {
93             LOG.warn("Convertor for type {} is already registered", type);
94         }
95
96         return result;
97     }
98
99     @Override
100     public ParametrizedConvertor registerConvertor(final ParametrizedConvertor convertor) {
101         final Class<?> type = convertor.getType();
102         final ParametrizedConvertor result = parametrizedConvertors.get(type);
103
104         if (Objects.isNull(result)) {
105             parametrizedConvertorKeys.add(type);
106             parametrizedConvertors.put(type, convertor);
107             LOG.debug("{} is now converted by {}", type, convertor);
108         } else {
109             LOG.warn("Convertor for type {} is already registered", type);
110         }
111
112         return result;
113     }
114
115     @Override
116     @SuppressWarnings("unchecked")
117     public <FROM, TO> Optional<TO> convert(final FROM source) {
118         final Optional<Class<?>> optionalType = extractType(source);
119
120         if (!optionalType.isPresent()) {
121             LOG.trace("Cannot convert {}", source);
122             return Optional.empty();
123         }
124
125         final Class<?> type = optionalType.get();
126         Convertor convertor = convertors.get(type);
127
128         if (Objects.isNull(convertor)) {
129             for (final Class<?> key : convertorKeys) {
130                 if (key.isAssignableFrom(type)) {
131                     convertor = convertors.get(key);
132                     convertors.put(type, convertor);
133                     LOG.debug("{} is now converted by {}", type, convertor);
134                     break;
135                 }
136             }
137
138             if (Objects.isNull(convertor)) {
139                 LOG.warn("Convertor for {} not found", type);
140                 return Optional.empty();
141             }
142         }
143
144         return Optional.of((TO) convertor.convert(source));
145     }
146
147     @Override
148     @SuppressWarnings("unchecked")
149     public <FROM, TO, DATA extends ConvertorData> Optional<TO> convert(final FROM source, final DATA data) {
150         final Optional<Class<?>> optionalType = extractType(source);
151
152         if (!optionalType.isPresent()) {
153             LOG.trace("Cannot convert {}", source);
154             return Optional.empty();
155         }
156
157         final Class<?> type = optionalType.get();
158         ParametrizedConvertor convertor = parametrizedConvertors.get(type);
159
160         if (Objects.isNull(convertor)) {
161             for (final Class<?> key : parametrizedConvertorKeys) {
162                 if (key.isAssignableFrom(type)) {
163                     convertor = parametrizedConvertors.get(key);
164                     parametrizedConvertors.put(type, convertor);
165                     LOG.debug("{} is now converted by {}", type, convertor);
166                     break;
167                 }
168             }
169
170             if (Objects.isNull(convertor)) {
171                 LOG.warn("Convertor for {} not found", type);
172                 return Optional.empty();
173             }
174         }
175
176         return Optional.of((TO) convertor.convert(source, data));
177     }
178
179     private <FROM> Optional<Class<?>> extractType(FROM source) {
180         if (Objects.isNull(source)) {
181             LOG.trace("Cannot extract type from source, because it is null");
182             return Optional.empty();
183         }
184
185         Class<?> type = source.getClass();
186
187         if (source instanceof Collection) {
188             final Optional first = ((Collection) source).stream().findFirst();
189
190             if (!first.isPresent()) {
191                 LOG.trace("Cannot extract type {}, because it is empty collection", type);
192                 return Optional.empty();
193             }
194
195             type = first.get().getClass();
196         }
197
198         return Optional.of(type);
199     }
200 }