
me.tfeng.playmods.avro.d2.AvroD2Component Maven / Gradle / Ivy
/**
* Copyright 2016 Thomas Feng
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.tfeng.playmods.avro.d2;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.avro.Protocol;
import org.apache.avro.specific.SpecificData;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import me.tfeng.playmods.avro.d2.factories.ClientFactory;
import me.tfeng.toolbox.avro.AvroHelper;
import me.tfeng.toolbox.spring.ApplicationManager;
import me.tfeng.toolbox.spring.ExtendedStartable;
import play.Logger;
import play.Logger.ALogger;
/**
* @author Thomas Feng ([email protected])
*/
@Component("play-mods.avro-d2.component")
public class AvroD2Component implements ExtendedStartable, InitializingBean, Watcher, ZooKeeperProvider {
public static final String PROTOCOL_PATHS_KEY = "play-mods.avro-d2.protocol-paths";
private static final ALogger LOG = Logger.of(AvroD2Component.class);
@Autowired
@Qualifier("play-mods.spring.application-manager")
private ApplicationManager applicationManager;
@Autowired
@Qualifier("play-mods.avro-d2.client-factory")
private ClientFactory clientFactory;
@Value("${play-mods.avro-d2.client-refresh-retry-delay:1000}")
private long clientRefreshRetryDelay;
@Value("${play-mods.avro-d2.enable-server:true}")
private boolean enableServer;
private boolean expired;
private Map, String> protocolPaths;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
@Value("${play-mods.avro-d2.server-host:localhost}")
private String serverHost;
@Value("${play-mods.avro-d2.server-port:0}")
private int serverPort;
@Value("${play-mods.avro-d2.server-register-retry-delay:1000}")
private long serverRegisterRetryDelay;
private List servers;
private ZooKeeper zk;
@Value("${play-mods.avro-d2.zk-connect-string:}")
private String zkConnectString;
@Value("${play-mods.avro-d2.zk-session-timeout:10000}")
private int zkSessionTimeout;
@Override
public void afterPropertiesSet() throws Exception {
try {
protocolPaths = applicationManager.getBean(PROTOCOL_PATHS_KEY, Map.class);
} catch (NoSuchBeanDefinitionException e) {
protocolPaths = Collections.emptyMap();
}
}
@Override
public void afterStart() {
if (enableServer) {
connect();
}
}
@Override
public void afterStop() {
}
@Override
public void beforeStart() {
}
@Override
public void beforeStop() {
if (enableServer) {
stopServers();
}
}
public T client(Class interfaceClass) {
return client(interfaceClass, new SpecificData(interfaceClass.getClassLoader()));
}
public T client(Class interfaceClass, SpecificData data) {
return interfaceClass.cast(Proxy.newProxyInstance(interfaceClass.getClassLoader(),
new Class>[] { interfaceClass }, clientFactory.create(interfaceClass, data, false)));
}
public long getClientRefreshRetryDelay() {
return clientRefreshRetryDelay;
}
public ScheduledExecutorService getScheduler() {
return scheduler;
}
public ZooKeeper getZooKeeper() {
return zk;
}
@Override
public void onStart() throws Throwable {
}
@Override
public void onStop() throws Throwable {
}
@Override
public void process(WatchedEvent event) {
LOG.info(event.toString());
switch (event.getState()) {
case SyncConnected:
if (expired) {
expired = false;
servers.forEach(AvroD2Server::register);
}
break;
case Expired:
expired = true;
try {
zk.close();
} catch (InterruptedException e) {
// Ignore.
}
connect();
default:
}
}
public void startServers() {
servers = new ArrayList<>(protocolPaths.size());
for (Entry, String> entry : protocolPaths.entrySet()) {
Protocol protocol = AvroHelper.getProtocol(entry.getKey());
String path = entry.getValue();
if (!path.startsWith("/")) {
path = "/" + path;
}
URL url;
try {
url = new URL("http", serverHost, serverPort, path);
} catch (Exception e) {
throw new RuntimeException("Unable to initialize server", e);
}
AvroD2Server server = new AvroD2Server(protocol, url, zk, scheduler, serverRegisterRetryDelay);
server.register();
servers.add(server);
}
}
public void stopServers() {
servers.stream().forEach(server -> {
try {
server.close();
} catch (Exception e) {
LOG.error("Unable to close server for " + server.getProtocol().getName() + " at " + server.getUrl()
+ "; ignoring");
}
});
servers.clear();
}
protected void connect() {
try {
zk = new ZooKeeper(zkConnectString, zkSessionTimeout, this);
startServers();
} catch (IOException e) {
if (zk != null) {
try {
zk.close();
} catch (InterruptedException e1) {
// Ignore.
}
}
getScheduler().schedule(this::connect, clientRefreshRetryDelay, TimeUnit.MILLISECONDS);
LOG.warn("Unable to connect to ZooKeeper; retry later", e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy