001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.impl.cluster; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.concurrent.locks.StampedLock; 025 026import org.apache.camel.CamelContext; 027import org.apache.camel.Ordered; 028import org.apache.camel.cluster.CamelClusterMember; 029import org.apache.camel.cluster.CamelClusterService; 030import org.apache.camel.cluster.CamelClusterView; 031import org.apache.camel.support.ServiceSupport; 032import org.apache.camel.util.ReferenceCount; 033import org.apache.camel.util.concurrent.LockHelper; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037public abstract class AbstractCamelClusterService<T extends CamelClusterView> extends ServiceSupport implements CamelClusterService { 038 private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCamelClusterService.class); 039 040 private final Map<String, ViewHolder<T>> views; 041 private final Map<String, Object> attributes; 042 private final StampedLock lock; 043 private int order; 044 private String id; 045 private CamelContext camelContext; 046 047 protected AbstractCamelClusterService() { 048 this(null, null); 049 } 050 051 protected AbstractCamelClusterService(String id) { 052 this(id, null); 053 } 054 055 protected AbstractCamelClusterService(String id, CamelContext camelContext) { 056 this.order = Ordered.LOWEST; 057 this.id = id; 058 this.camelContext = camelContext; 059 this.views = new HashMap<>(); 060 this.lock = new StampedLock(); 061 this.attributes = new HashMap<>(); 062 } 063 064 @Override 065 public int getOrder() { 066 return order; 067 } 068 069 public void setOrder(int order) { 070 this.order = order; 071 } 072 073 @Override 074 public void setId(String id) { 075 this.id = id; 076 } 077 078 @Override 079 public String getId() { 080 return id; 081 } 082 083 @Override 084 public void setCamelContext(CamelContext camelContext) { 085 this.camelContext = camelContext; 086 087 LockHelper.doWithWriteLock( 088 lock, 089 () -> { 090 for (ViewHolder<T> holder : views.values()) { 091 holder.get().setCamelContext(camelContext); 092 } 093 } 094 ); 095 } 096 097 @Override 098 public CamelContext getCamelContext() { 099 return camelContext; 100 } 101 102 public void setAttributes(Map<String, Object> attributes) { 103 this.attributes.clear(); 104 this.attributes.putAll(attributes); 105 } 106 107 public void setAttribute(String key, Object value) { 108 this.attributes.put(key, value); 109 } 110 111 @Override 112 public Map<String, Object> getAttributes() { 113 return Collections.unmodifiableMap(attributes); 114 } 115 116 @Override 117 protected void doStart() throws Exception { 118 LockHelper.doWithReadLockT( 119 lock, 120 () -> { 121 for (ViewHolder<T> holder : views.values()) { 122 holder.get().start(); 123 } 124 } 125 ); 126 } 127 128 @Override 129 protected void doStop() throws Exception { 130 LockHelper.doWithReadLockT( 131 lock, 132 () -> { 133 for (ViewHolder<T> holder : views.values()) { 134 holder.get().stop(); 135 } 136 } 137 ); 138 } 139 140 @Override 141 public CamelClusterView getView(String namespace) throws Exception { 142 return LockHelper.callWithWriteLock( 143 lock, 144 () -> { 145 ViewHolder<T> holder = views.get(namespace); 146 147 if (holder == null) { 148 T view = createView(namespace); 149 view.setCamelContext(this.camelContext); 150 151 holder = new ViewHolder<>(view); 152 153 views.put(namespace, holder); 154 } 155 156 // Add reference and eventually start the route. 157 return holder.retain(); 158 } 159 ); 160 } 161 162 @Override 163 public void releaseView(CamelClusterView view) throws Exception { 164 LockHelper.doWithWriteLock( 165 lock, 166 () -> { 167 ViewHolder<T> holder = views.get(view.getNamespace()); 168 169 if (holder != null) { 170 holder.release(); 171 } 172 } 173 ); 174 } 175 176 @Override 177 public Collection<String> getNamespaces() { 178 return LockHelper.supplyWithReadLock( 179 lock, 180 () -> { 181 // copy the key set so it is not modifiable and thread safe 182 // thus a little inefficient. 183 return new HashSet<>(views.keySet()); 184 } 185 ); 186 } 187 188 @Override 189 public void startView(String namespace) throws Exception { 190 LockHelper.doWithWriteLockT( 191 lock, 192 () -> { 193 ViewHolder<T> holder = views.get(namespace); 194 195 if (holder != null) { 196 LOGGER.info("Force start of view {}", namespace); 197 holder.startView(); 198 } else { 199 LOGGER.warn("Error forcing start of view {}: it does not exist", namespace); 200 } 201 } 202 ); 203 } 204 205 @Override 206 public void stopView(String namespace) throws Exception { 207 LockHelper.doWithWriteLockT( 208 lock, 209 () -> { 210 ViewHolder<T> holder = views.get(namespace); 211 212 if (holder != null) { 213 LOGGER.info("Force stop of view {}", namespace); 214 holder.stopView(); 215 } else { 216 LOGGER.warn("Error forcing stop of view {}: it does not exist", namespace); 217 } 218 } 219 ); 220 } 221 222 @Override 223 public boolean isLeader(String namespace) { 224 return LockHelper.supplyWithReadLock( 225 lock, 226 () -> { 227 ViewHolder<T> holder = views.get(namespace); 228 if (holder != null) { 229 CamelClusterMember member = holder.get().getLocalMember(); 230 if (member != null) { 231 return member.isLeader(); 232 } 233 } 234 235 return false; 236 } 237 ); 238 } 239 240 // ********************************** 241 // Implementation 242 // ********************************** 243 244 protected abstract T createView(String namespace) throws Exception; 245 246 // ********************************** 247 // Helpers 248 // ********************************** 249 250 private final class ViewHolder<V extends CamelClusterView> { 251 private final V view; 252 private final ReferenceCount count; 253 254 ViewHolder(V view) { 255 this.view = view; 256 this.count = ReferenceCount.on( 257 () -> { 258 try { 259 this.startView(); 260 } catch (Exception e) { 261 throw new RuntimeException(e); 262 } 263 }, 264 () -> { 265 try { 266 this.stopView(); 267 } catch (Exception e) { 268 throw new RuntimeException(e); 269 } 270 }); 271 } 272 273 V get() { 274 return view; 275 } 276 277 V retain() { 278 LOGGER.debug("Retain view {}, old-refs={}", view.getNamespace(), count.get()); 279 280 count.retain(); 281 282 return get(); 283 } 284 285 void release() { 286 LOGGER.debug("Release view {}, old-refs={}", view.getNamespace(), count.get()); 287 288 count.release(); 289 } 290 291 void startView() throws Exception { 292 if (AbstractCamelClusterService.this.isRunAllowed()) { 293 LOGGER.debug("Start view {}", view.getNamespace()); 294 view.start(); 295 } else { 296 LOGGER.debug("Can't start view {} as cluster service is not running, view will be started on service start-up", view.getNamespace()); 297 } 298 } 299 300 void stopView() throws Exception { 301 LOGGER.debug("Stop view {}", view.getNamespace()); 302 view.stop(); 303 } 304 } 305}