| 1 | package org.jtoolkit.essence.data; |
| 2 | |
| 3 | import org.jetbrains.annotations.NotNull; |
| 4 | import org.jetbrains.annotations.Nullable; |
| 5 | import org.jtoolkit.essence.app.ComponentBuilder; |
| 6 | import org.jtoolkit.essence.app.Container; |
| 7 | import org.jtoolkit.essence.app.Runs; |
| 8 | import org.jtoolkit.essence.app.pojo.DataValue; |
| 9 | import org.jtoolkit.essence.data.impl.ClusterImpl; |
| 10 | import static org.jtoolkit.essence.utils.StringUtils.isBlank; |
| 11 | import org.jtoolkit.essence.utils.impl.MapOfMap; |
| 12 | |
| 13 | import javax.cache.Cache; |
| 14 | import java.io.Closeable; |
| 15 | import java.util.*; |
| 16 | import java.util.concurrent.ConcurrentMap; |
| 17 | |
| 18 | /* |
| 19 | Copyright 2006 Peter Lawrey |
| 20 | |
| 21 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 22 | you may not use this file except in compliance with the License. |
| 23 | You may obtain a copy of the License at |
| 24 | |
| 25 | http://www.apache.org/licenses/LICENSE-2.0 |
| 26 | |
| 27 | Unless required by applicable law or agreed to in writing, software |
| 28 | distributed under the License is distributed on an "AS IS" BASIS, |
| 29 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 30 | See the License for the specific language governing permissions and |
| 31 | limitations under the License. |
| 32 | */ |
| 33 | /** |
| 34 | * @author Peter Lawrey |
| 35 | */ |
| 36 | @SuppressWarnings({"unchecked"}) |
| 37 | public class ClusterComponentBuilder implements ComponentBuilder, Runs, Closeable { |
| 38 | private final String name; |
| 39 | private final Container container; |
| 40 | private final MapOfMap<String, String, ClusterData> clusterDataMap = new MapOfMap<String, String, ClusterData>(); |
| 41 | private final MapOfMap<String, String, ClusterCollectionData> collectionDataMap = new MapOfMap<String, String, ClusterCollectionData>(); |
| 42 | private final Map<String, ClusterImpl> clusters = new LinkedHashMap<String, ClusterImpl>(); |
| 43 | |
| 44 | public ClusterComponentBuilder(@NotNull String name, @NotNull Container container) throws InstantiationException { |
| 45 | this.name = name; |
| 46 | this.container = container; |
| 47 | String logicalHostname = container.getProperties().getValue("logicalHostname"); |
| 48 | if (isBlank(logicalHostname)) |
| 49 | logicalHostname = container.getProperties().getValue(Container.SERVER_HOSTNAME); |
| 50 | if (isBlank(logicalHostname) || logicalHostname == null) |
| 51 | logicalHostname = name; |
| 52 | |
| 53 | for (ClusterData cd : container.removeConfigAs("Clusters", ClusterData.class)) |
| 54 | clusterDataMap.put(cd.cluster, cd.logicalHostname, cd); |
| 55 | |
| 56 | for (ClusterCollectionData cd : container.removeConfigAs("ClusterCollections", ClusterCollectionData.class)) |
| 57 | collectionDataMap.put(cd.cluster, cd.collection, cd); |
| 58 | |
| 59 | Set<String> clusterNames = new LinkedHashSet<String>(); |
| 60 | clusterNames.addAll(clusterDataMap.keySet()); |
| 61 | clusterNames.addAll(collectionDataMap.keySet()); |
| 62 | for (String clusterName : clusterNames) { |
| 63 | clusters.put(clusterName, new ClusterImpl(clusterName, logicalHostname, clusterDataMap.get(clusterName), collectionDataMap.get(clusterName))); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | @NotNull public String getName() { |
| 68 | return name; |
| 69 | } |
| 70 | |
| 71 | @Nullable |
| 72 | public <T> T resolveComponent(@NotNull String componentName, @Nullable Class<T> type) |
| 73 | throws IllegalStateException, IllegalArgumentException { |
| 74 | String[] parts = componentName.split("\\.", 2); |
| 75 | String clusterName = parts[0]; |
| 76 | ClusterImpl cluster = clusters.get(clusterName); |
| 77 | if (cluster == null) return null; |
| 78 | if (parts.length == 1) { |
| 79 | if (type == null || type == Cluster.class) |
| 80 | return (T) cluster; |
| 81 | throw new IllegalArgumentException(container.getName() + ATTEMPTED_TO_GET_COMPONENT + componentName + |
| 82 | " as " + type + " however ClusterComponentBuilder only supports Cluster"); |
| 83 | } |
| 84 | String collectionName = parts[1]; |
| 85 | if (type == Cluster.class) |
| 86 | return (T) cluster; |
| 87 | if (type == Store.class || type == null) |
| 88 | return (T) cluster.getClusterStore(collectionName); |
| 89 | if (type == Map.class || type == ConcurrentMap.class || type == ClusterMap.class || type == Cache.class) |
| 90 | return (T) cluster.getMapView(collectionName); |
| 91 | if (type == Queue.class || type == Collection.class) |
| 92 | return (T) cluster.getQueueView(collectionName); |
| 93 | throw new IllegalArgumentException(container.getName() + ATTEMPTED_TO_GET_COMPONENT + componentName + |
| 94 | " as " + type + " however ClusterComponentBuilder only supports Cluster,Map,Queue or Collection"); |
| 95 | } |
| 96 | |
| 97 | public void start() throws IllegalStateException, IllegalArgumentException { |
| 98 | for (ClusterImpl cluster : clusters.values()) |
| 99 | cluster.start(); |
| 100 | } |
| 101 | |
| 102 | public void stop() { |
| 103 | for (ClusterImpl cluster : clusters.values()) |
| 104 | cluster.stop(); |
| 105 | } |
| 106 | |
| 107 | public void close() { |
| 108 | for (ClusterImpl cluster : clusters.values()) |
| 109 | cluster.close(); |
| 110 | } |
| 111 | |
| 112 | public static class ClusterData { |
| 113 | public final String cluster; |
| 114 | public final String logicalHostname; |
| 115 | public final RoleType role; |
| 116 | public final String connectUrl; |
| 117 | public final Integer retries; |
| 118 | |
| 119 | @SuppressWarnings("UnusedDeclaration") |
| 120 | ClusterData(@NotNull String cluster, @NotNull String logicalHostname, @NotNull RoleType role, @NotNull String connectUrl, Integer retries) { |
| 121 | this.cluster = cluster; |
| 122 | this.logicalHostname = logicalHostname; |
| 123 | this.role = role; |
| 124 | this.connectUrl = connectUrl; |
| 125 | this.retries = retries; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | public enum RoleType { |
| 130 | MASTER, |
| 131 | SLAVE |
| 132 | } |
| 133 | |
| 134 | public static class ClusterCollectionData extends DataValue { |
| 135 | private static final int DEFAULT_CACHE_SIZE = 1024; |
| 136 | public final String cluster; |
| 137 | public final String collection; |
| 138 | public final Store.CollectionType collectionType; |
| 139 | public final Store.StoreType storeType; |
| 140 | public final Class valueClass; |
| 141 | public final String location; |
| 142 | public final Store.PersistMode persistMode; |
| 143 | public final EvictionStrategy evictionStrategy; |
| 144 | public final Integer cacheSize; |
| 145 | |
| 146 | @SuppressWarnings({"UnusedDeclaration"}) |
| 147 | ClusterCollectionData(@NotNull String cluster, @NotNull String collection, Store.CollectionType collectionType, |
| 148 | @NotNull Store.StoreType storeType, @NotNull Class valueClass, |
| 149 | @NotNull String location, @Nullable Store.PersistMode persistMode, |
| 150 | @Nullable EvictionStrategy evictionStrategy, @Nullable Integer cacheSize) { |
| 151 | this.cluster = cluster; |
| 152 | this.collection = collection; |
| 153 | this.collectionType = collectionType; |
| 154 | this.storeType = storeType; |
| 155 | this.valueClass = valueClass; |
| 156 | if (valueClass == void.class) throw new IllegalArgumentException(collection + ": valueClass cannot be void."); |
| 157 | this.location = location; |
| 158 | this.persistMode = persistMode == null ? Store.PersistMode.ANY : persistMode; |
| 159 | this.evictionStrategy = evictionStrategy == null ? EvictionStrategy.NONE : evictionStrategy; |
| 160 | this.cacheSize = cacheSize == null ? DEFAULT_CACHE_SIZE : cacheSize; |
| 161 | } |
| 162 | } |
| 163 | } |