I am using zookeeper for configuration management for my java microservices. For that I use apache curator and java zookeeper client.
How can I import a configuration file(properties or json) to zookeeper when the microservice initializes?
You should use curator framework if you wanted to load your config in zookeeper. See the post about how you may use curator framework.
There is a some base code example for yml files (for spring config):
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.BoundedExponentialBackoffRetry;
import org.springframework.core.io.ClassPathResource;
import org.yaml.snakeyaml.Yaml;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
#Slf4j
public class LoadConfigsInZoo {
// base path of your config
// /CONFIG_PATH/APP_NAME,CONTEXT_NAME
private static final String BASE_PATH = "/configuration/myApp";
private static final String ZOO_URL = "localhost:2181";
private static final String CONFIG_FILE = "bootstrap.yml";
private final ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) throws IOException {
new LoadConfigsInZoo().loadConfig();
}
private void loadConfig() throws IOException {
BoundedExponentialBackoffRetry retryPolicy =
new BoundedExponentialBackoffRetry(100, 300, 10);
Map<String, String> config = flattenInnerProperties("", getContentOfYaml(CONFIG_FILE));
try (CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(ZOO_URL)
.retryPolicy(retryPolicy)
.build()) {
client.start();
for (Map.Entry<String, String> entry : config.entrySet()) {
String path = createPath(BASE_PATH, entry.getKey());
try {
log.info("Try add node with name '{}'", path);
client.create()
.creatingParentsIfNeeded()
.forPath(path, entry.getValue().getBytes());
log.info("Node with name '{}' was created", path);
} catch (Exception e) {
log.warn("Unable to create node by path: {}, exception: {}", path, e.getMessage());
}
}
}
}
// need your own implementation for properties/json files
#SuppressWarnings("unchecked")
private Map<String, Object> getContentOfYaml(String path) throws IOException {
Yaml yaml = new Yaml();
try (InputStream in = new ClassPathResource(path).getInputStream()) {
return yaml.loadAs(in, Map.class);
}
}
#SuppressWarnings("unchecked")
private Map<String, String> getContentOfProperties(String path) throws IOException {
try (InputStream in = new ClassPathResource(path).getInputStream()) {
Properties properties = new Properties();
properties.load(in);
return (Map) (properties);
}
}
#SuppressWarnings("unchecked")
private Map<String, String> getContentOfJson(String path) throws IOException {
try (InputStream in = new ClassPathResource(path).getInputStream()) {
return new ObjectMapper().readValue(in, HashMap.class);
}
}
#SuppressWarnings("unchecked")
private Map<String, String> flattenInnerProperties(String prefix, Map<String, Object> rootMap) {
Map<String, String> result = new HashMap<>();
for (Map.Entry<String, Object> entry : rootMap.entrySet()) {
String newPrefix = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey();
Object value = entry.getValue();
if (value instanceof Map) {
result.putAll(flattenInnerProperties(newPrefix, (Map<String, Object>) value));
} else if (value instanceof List) {
result.putAll(flattenInnerListInProperties(newPrefix, (List) value));
} else {
result.put(newPrefix, String.valueOf(value));
}
}
return result;
}
#SuppressWarnings("unchecked")
private Map<String, String> flattenInnerListInProperties(String prefix, List value) {
int i = 0;
Map<String, String> result = new HashMap<>();
for (Object v : value) {
String listKey = prefix + "[" + i + "]";
if (v instanceof Map) {
result.putAll(flattenInnerProperties(listKey, (Map) v));
} else if (v instanceof List) {
result.putAll(flattenInnerListInProperties(listKey, (List) v));
} else {
result.put(listKey, String.valueOf(v));
}
i++;
}
return result;
}
private String createPath(String basePath, String configName) {
return basePath + "/" + configName.replaceAll("\\.", "/");
}
}
I had a similar problem, where we were using zookeeper as the config server and the configuration was stored in yml format in file.
Instead of writing code for creating the znode hierarchy dynamically as per yml file, I found a groovy based tool which basically does the same.
You need to have groovy installed and run as below
zookeeperdump.groovy -s localhost:2181 -c /config/application < dump.yml
Related
I'm making an application with Google SpeechClient that has the requirements to set a GOOGLE_APPLICATION_CREDENTIALS environment variable that, once set, you can use the voice to text api.
My application is required to run in linux and windows. In linux it runs perfectly, however, on windows, when running the project, it throws an exception com.google.api.gax.rpc.UnavailableException: "io.grpc.StatusRuntimeException: UNAVAILABLE: Credentials failed to obtain metadata" when trying to run this thread
package Controller.Runnables;
import Controller.GUI.VoxSpeechGUIController;
import Model.SpokenTextHistory;
import com.google.api.gax.rpc.ClientStream;
import com.google.api.gax.rpc.ResponseObserver;
import com.google.api.gax.rpc.StreamController;
import com.google.cloud.speech.v1.*;
import com.google.protobuf.ByteString;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import java.io.IOException;
import java.util.ArrayList;
public class SpeechRecognizerRunnable implements Runnable{
private VoxSpeechGUIController controller;
public SpeechRecognizerRunnable(VoxSpeechGUIController voxSpeechGUIController) {
this.controller = voxSpeechGUIController;
}
#Override
public void run() {
MicrofoneRunnable micrunnable = MicrofoneRunnable.getInstance();
Thread micThread = new Thread(micrunnable);
ResponseObserver<StreamingRecognizeResponse> responseObserver = null;
try (SpeechClient client = SpeechClient.create()) {
ClientStream<StreamingRecognizeRequest> clientStream;
responseObserver =
new ResponseObserver<StreamingRecognizeResponse>() {
ArrayList<StreamingRecognizeResponse> responses = new ArrayList<>();
public void onStart(StreamController controller) {}
public void onResponse(StreamingRecognizeResponse response) {
try {
responses.add(response);
StreamingRecognitionResult result = response.getResultsList().get(0);
// There can be several alternative transcripts for a given chunk of speech. Just
// use the first (most likely) one here.
SpeechRecognitionAlternative alternative = result.getAlternativesList().get(0);
String transcript = alternative.getTranscript();
System.out.printf("Transcript : %s\n", transcript);
String newText = SpokenTextHistory.getInstance().getActualSpeechString() + " " + transcript;
SpokenTextHistory.getInstance().setActualSpeechString(newText);
controller.setLabelText(newText);
}
catch (Exception ex){
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
public void onComplete() {
}
public void onError(Throwable t) {
System.out.println(t);
}
};
clientStream = client.streamingRecognizeCallable().splitCall(responseObserver);
RecognitionConfig recognitionConfig =
RecognitionConfig.newBuilder()
.setEncoding(RecognitionConfig.AudioEncoding.LINEAR16)
.setLanguageCode("pt-BR")
.setSampleRateHertz(16000)
.build();
StreamingRecognitionConfig streamingRecognitionConfig =
StreamingRecognitionConfig.newBuilder().setConfig(recognitionConfig).build();
StreamingRecognizeRequest request =
StreamingRecognizeRequest.newBuilder()
.setStreamingConfig(streamingRecognitionConfig)
.build(); // The first request in a streaming call has to be a config
clientStream.send(request);
try {
// SampleRate:16000Hz, SampleSizeInBits: 16, Number of channels: 1, Signed: true,
// bigEndian: false
AudioFormat audioFormat = new AudioFormat(16000, 16, 1, true, false);
DataLine.Info targetInfo =
new DataLine.Info(
TargetDataLine.class,
audioFormat); // Set the system information to read from the microphone audio
// stream
if (!AudioSystem.isLineSupported(targetInfo)) {
System.out.println("Microphone not supported");
System.exit(0);
}
// Target data line captures the audio stream the microphone produces.
micrunnable.targetDataLine = (TargetDataLine) AudioSystem.getLine(targetInfo);
micrunnable.targetDataLine.open(audioFormat);
micThread.start();
long startTime = System.currentTimeMillis();
while (!micrunnable.stopFlag) {
long estimatedTime = System.currentTimeMillis() - startTime;
if (estimatedTime >= 55000) {
clientStream.closeSend();
clientStream = client.streamingRecognizeCallable().splitCall(responseObserver);
request =
StreamingRecognizeRequest.newBuilder()
.setStreamingConfig(streamingRecognitionConfig)
.build();
startTime = System.currentTimeMillis();
} else {
request =
StreamingRecognizeRequest.newBuilder()
.setAudioContent(ByteString.copyFrom(micrunnable.sharedQueue.take()))
.build();
}
clientStream.send(request);
}
} catch (Exception e) {
System.out.println(e);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
I've been working hard for hours and have not found a solution that solves my problem.
It is worth mentioning that the environment variable is being set correctly.
Has anyone ever had this problem with Google? What should I do to fix this?
This is my envirounment variable creator:
PS: I`ve already tried use all google alternatives to validate credentials, but all return me errors.
package Controller.Autentication;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class GoogleAuthentication {
private static final String GOOGLE_APPLICATION_CREDENTIALS = "GOOGLE_APPLICATION_CREDENTIALS";
private static final String VoxSpeechFolder = ".vox";
private static final String GoogleAuthenticationJsonFile = "VoxAuthentication.json";
public static void setupGoogleCredentials() {
String directory = defaultDirectory();
directory += File.separator+VoxSpeechFolder;
File voxPath = new File(directory);
if (!voxPath.exists()) {
voxPath.mkdirs();
}
ClassLoader classLoader = new GoogleAuthentication().getClass().getClassLoader();
File srcFile = new File(classLoader.getResource(GoogleAuthenticationJsonFile).getFile());
if(srcFile.exists()){
try {
String voxDestPath = defaultDirectory() + File.separator + VoxSpeechFolder +File.separator+ GoogleAuthenticationJsonFile;
File destFile = new File(voxDestPath);
copyFile(srcFile,destFile);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Map<String,String> googleEnv = new HashMap<>();
String path = defaultDirectory() +File.separator+ VoxSpeechFolder +File.separator+ GoogleAuthenticationJsonFile;
googleEnv.put(GOOGLE_APPLICATION_CREDENTIALS, path);
setGoogleEnv(googleEnv);
} catch (Exception e) {
e.printStackTrace();
}
}
static void copyFile(File sourceFile, File destFile)
throws IOException {
InputStream inStream ;
OutputStream outStream ;
System.out.println(destFile.getPath());
if(destFile.createNewFile()){
inStream = new FileInputStream(sourceFile);
outStream = new FileOutputStream(destFile);
byte[] buffer = new byte[1024];
int length;
while ((length = inStream.read(buffer)) > 0){
outStream.write(buffer, 0, length);
}
inStream.close();
outStream.close();
}
}
static String defaultDirectory()
{
String OS = getOperationSystem();
if (OS.contains("WIN"))
return System.getenv("APPDATA");
else if (OS.contains("MAC"))
return System.getProperty("user.home") + "/Library/Application "
+ "Support";
else if (OS.contains("LINUX")) {
return System.getProperty("user.home");
}
return System.getProperty("user.dir");
}
static String getOperationSystem() {
return System.getProperty("os.name").toUpperCase();
}
protected static void setGoogleEnv(Map<String, String> newenv) throws Exception {
try {
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
theEnvironmentField.setAccessible(true);
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
env.putAll(newenv);
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
theCaseInsensitiveEnvironmentField.setAccessible(true);
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
cienv.putAll(newenv);
} catch (NoSuchFieldException e) {
Class[] classes = Collections.class.getDeclaredClasses();
Map<String, String> env = System.getenv();
for(Class cl : classes) {
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Object obj = field.get(env);
Map<String, String> map = (Map<String, String>) obj;
map.clear();
map.putAll(newenv);
}
}
}
String genv = System.getenv(GOOGLE_APPLICATION_CREDENTIALS);
System.out.println(genv);
}
}
How to obtain Spring actuator /health metrics that already working?
For example, to push them to Grafana. Thus I need them as objects, not text.
You can inject properties which are exposed through actuator endpoints (like /health) by injecting instance of endpoint class using actuator API.
This stackoverflow answer explains about it in detail:
Does Spring Boot Actuator have a Java API?
Take a look on this example
package ru.formatko;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.prometheus.client.Collector;
import io.prometheus.client.exporter.common.TextFormat;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
/**
* Example:
* # HELP health_status HealthCheck result in prometheus's response format
* # TYPE health_status gauge
* health_status{application="java-service",type="main",} 1.0
* health_status{application="java-service",type="db",database="PostgreSQL",validationQuery="isValid()",} 1.0
* health_status{application="java-service",type="diskSpace",total="506332180480",exists="true",threshold="10485760",free="412188921856",} 1.0
* health_status{application="java-service",type="ping",} 1.0
*/
#Component
#Endpoint(id = "health-check")
public class HeathPrometheusEndpoint {
private static final String APPLICATION = "application";
private static final String TYPE = "type";
public static final String SAMPLE_HEALTH_STATUS = "health_status";
private final HealthEndpoint healthEndpoint;
private final String appName;
private final ObjectMapper mapper;
private final HttpCodeStatusMapper httpCodeStatusMapper;
public HeathPrometheusEndpoint(HealthEndpoint healthEndpoint,
ObjectMapper mapper,
#Value("${spring.application.name:}") String appName,
HttpCodeStatusMapper httpCodeStatusMapper) {
this.healthEndpoint = healthEndpoint;
this.mapper = mapper;
this.appName = appName;
this.httpCodeStatusMapper = httpCodeStatusMapper;
}
#ReadOperation(produces = TextFormat.CONTENT_TYPE_004)
public WebEndpointResponse<String> healthPrometheus() {
StatusDto status = createStatusDto();
List<Collector.MetricFamilySamples.Sample> samples = new ArrayList<>();
samples.add(createMainSample(status));
samples.addAll(createComponentSamples(status));
return createStringWebEndpointResponse(status, createMetricFamily(samples));
}
#SneakyThrows
private StatusDto createStatusDto() {
return mapper.readValue(mapper.writeValueAsString(healthEndpoint.health()), StatusDto.class);
}
private Collector.MetricFamilySamples.Sample createMainSample(StatusDto status) {
Labels labels = new Labels();
labels.add(APPLICATION, appName);
labels.add(TYPE, "main");
return createSample(SAMPLE_HEALTH_STATUS, labels, status.getStatus());
}
private List<Collector.MetricFamilySamples.Sample> createComponentSamples(StatusDto status) {
List<Collector.MetricFamilySamples.Sample> list = new ArrayList<>();
for (Map.Entry<String, StatusDto> entry : status.components.entrySet()) {
Labels labels = new Labels();
labels.add(APPLICATION, appName);
labels.add(TYPE, entry.getKey());
StatusDto statusDto = entry.getValue();
Map<String, Object> details = statusDto.getDetails();
if (details != null && !details.isEmpty()) {
details.forEach((k, v) -> labels.add(k, String.valueOf(v)));
}
list.add(createSample(SAMPLE_HEALTH_STATUS, labels, statusDto.getStatus()));
}
return list;
}
private Collector.MetricFamilySamples.Sample createSample(String name, Labels labels, Status status) {
double v = Status.UP.equals(status) ? 1 : 0;
return new Collector.MetricFamilySamples.Sample(name, labels.getLabels(), labels.getValues(), v);
}
private Collector.MetricFamilySamples createMetricFamily(List<Collector.MetricFamilySamples.Sample> s) {
return new Collector.MetricFamilySamples(
"health_status", Collector.Type.GAUGE,
"HealthCheck result in prometheus's response format", s);
}
private WebEndpointResponse<String> createStringWebEndpointResponse(
StatusDto status, Collector.MetricFamilySamples metricFamilySamples
) {
try {
Writer writer = new StringWriter();
TextFormat.write004(writer,
Collections.enumeration(Collections.singletonList(metricFamilySamples)));
return wrapResponse(writer.toString(), status);
} catch (IOException ex) {
// This actually never happens since StringWriter::write() doesn't throw any
// IOException
throw new RuntimeException("Writing metrics failed", ex);
}
}
private WebEndpointResponse<String> wrapResponse(String body, StatusDto status) {
if (body == null || body.isEmpty()) {
return new WebEndpointResponse<>("", 500);
} else {
int statusCode = httpCodeStatusMapper.getStatusCode(status.getStatus());
return new WebEndpointResponse<>(body, statusCode);
}
}
public static class Labels {
private final Map<String, String> map = new HashMap<>();
public void add(String label, String value) {
if (value != null && !value.isEmpty()) {
map.put(label, value);
}
}
public List<String> getLabels() {
return new ArrayList<>(map.keySet());
}
public List<String> getValues() {
return new ArrayList<>(map.values());
}
}
#Data
public static class StatusDto {
private Status status;
private Map<String, StatusDto> components;
private Map<String, Object> details;
}
}
I am using yamlbeans to get data from yaml file. i am getting following response
{x1=[{y1=z1}, {y2=z2}], x2=[{y1 =z1}, {y2=z2]}
Now i want to get data y1 of x1 but i am not able to do this. I am using following code for read operation
package com.mobileapp;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import net.sourceforge.yamlbeans.YamlReader;
public class ReadDataWithYaml {
public static void main(String[] args) {
try {
YamlReader reader = new YamlReader(new FileReader("C:\\Users\\5521\\Desktop\\test.yml"));
Object object = reader.read();
System.out.println(object);
Map<String, ArrayList<String>> map = (Map<String, ArrayList<String>>) object;
System.out.println(map.get("x1"));
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
Yaml yaml1 = new Yaml();
InputStream inputStream1 = Main.class.getClassLoader().getResourceAsStream("YourYaml.yaml");
Map< String, Object> result = (Map< String, Object>) yaml1.load(inputStream1);
for (Object name : result.keySet()) {
System.out.println(result.get(name).toString());
}
String x= result.get("userInput").toString();
System .out.println(""+x);
Here's two ways of doing string substitution:
name = "Tshepang"
"my name is {}".format(name)
"my name is " + name
How do I do something similar to the first method, using Java?
name = "PaĆlo";
MessageFormat f = new MessageFormat("my name is {0}");
f.format(new Object[]{name});
Or shorter:
MessageFormat.format("my name is {0}", name);
String s = String.format("something %s","name");
Underscore-java has a format() static method. Live example
import com.github.underscore.Underscore;
public class Main {
public static void main(String[] args) {
String name = "Tshepang";
String formatted = Underscore.format("my name is {}", name);
// my name is Tshepang
}
}
You can try this
package template.fstyle;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static java.util.Objects.nonNull;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FStyleFinal {
private static final String PLACEHOLDERS_KEY = "placeholders";
private static final String VARIABLE_NAMES_KEY = "variableNames";
private static final String PLACEHOLDER_PREFIX = "{{";
private static final String PLACEHOLDER_SUFFIX = "}}";
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{\\{\\s*([\\S]+)\\s*}}");
private static Map<String, List<String>> toPlaceholdersAndVariableNames(String rawTemplate) {
List<String> placeholders = newArrayList();
List<String> variableNames = newArrayList();
Matcher matcher = PLACEHOLDER_PATTERN.matcher(rawTemplate);
while (matcher.find()) {
for (int j = 0; j <= matcher.groupCount(); j++) {
String s = matcher.group(j);
if (StringUtils.startsWith(s, PLACEHOLDER_PREFIX) && StringUtils.endsWith(s, PLACEHOLDER_SUFFIX)) {
placeholders.add(s);
} else if (!StringUtils.startsWith(s, PLACEHOLDER_PREFIX) && !StringUtils.endsWith(s, PLACEHOLDER_SUFFIX)) {
variableNames.add(s);
}
}
}
checkArgument(CollectionUtils.size(placeholders) == CollectionUtils.size(variableNames), "template engine error");
Map<String, List<String>> map = newHashMap();
map.put(PLACEHOLDERS_KEY, placeholders);
map.put(VARIABLE_NAMES_KEY, variableNames);
return map;
}
private static String toJavaTemplate(String rawTemplate, List<String> placeholders) {
String javaTemplate = rawTemplate;
for (String placeholder : placeholders) {
javaTemplate = StringUtils.replaceOnce(javaTemplate, placeholder, "%s");
}
return javaTemplate;
}
private static Object[] toJavaTemplateRenderValues(Map<String, String> context, List<String> variableNames, boolean allowNull) {
return variableNames.stream().map(name -> {
String value = context.get(name);
if (!allowNull) {
checkArgument(nonNull(value), name + " should not be null");
}
return value;
}).toArray();
}
private static Map<String, String> fromBeanToMap(Object bean, List<String> variableNames) {
return variableNames.stream().distinct().map(name -> {
String value = null;
try {
value = BeanUtils.getProperty(bean, name);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
log.debug("fromBeanToMap error", e);
}
return Pair.of(name, value);
}).filter(p -> nonNull(p.getRight())).collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
}
public static String render(String rawTemplate, Map<String, String> context, boolean allowNull) {
Map<String, List<String>> templateMeta = toPlaceholdersAndVariableNames(rawTemplate);
List<String> placeholders = templateMeta.get(PLACEHOLDERS_KEY);
List<String> variableNames = templateMeta.get(VARIABLE_NAMES_KEY);
// transform template to java style template
String javaTemplate = toJavaTemplate(rawTemplate, placeholders);
Object[] renderValues = toJavaTemplateRenderValues(context, variableNames, allowNull);
return String.format(javaTemplate, renderValues);
}
public static String render(String rawTemplate, Object bean, boolean allowNull) {
Map<String, List<String>> templateMeta = toPlaceholdersAndVariableNames(rawTemplate);
List<String> variableNames = templateMeta.get(VARIABLE_NAMES_KEY);
Map<String, String> mapContext = fromBeanToMap(bean, variableNames);
return render(rawTemplate, mapContext, allowNull);
}
public static void main(String[] args) {
String template = "hello, my name is {{ name }}, and I am {{age}} years old, a null value {{ not_exists }}";
Map<String, String> context = newHashMap();
context.put("name", "felix");
context.put("age", "18");
String s = render(template, context, true);
log.info("{}", s);
try {
render(template, context, false);
} catch (IllegalArgumentException e) {
log.error("error", e);
}
}
}
Sample output:
[main] INFO template.fstyle.FStyleFinal - hello, my name is felix, and I am 18 years old, a null value null
[main] ERROR template.fstyle.FStyleFinal - error
java.lang.IllegalArgumentException: not_exists should not be null
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:142)
at template.fstyle.FStyleFinal.lambda$toJavaTemplateRenderValues$0(FStyleFinal.java:69)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:545)
at java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:438)
at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:444)
at template.fstyle.FStyleFinal.toJavaTemplateRenderValues(FStyleFinal.java:72)
at template.fstyle.FStyleFinal.render(FStyleFinal.java:93)
at template.fstyle.FStyleFinal.main(FStyleFinal.java:113)
I am wanting to take a screenshot of a page using HtmlUnitDriver I came across this Link where this guy has made a custom HTML unit driver to take the screenshot.
But unfortunately, while implementing that I am getting an exception.
"Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to java.io.File
at Test.main(Test.java:39)"
My code is as follows-
import java.io.File;
import java.io.IOException;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebDriver;
import com.gargoylesoftware.htmlunit.BrowserVersion;
public class Test extends ScreenCaptureHtmlUnitDriver {
public static void main(String[] args) throws InterruptedException, IOException {
WebDriver driver = new ScreenCaptureHtmlUnitDriver(BrowserVersion.FIREFOX_38);
driver.get("https://www.google.com/?gws_rd=ssl");
try{
File scrFile = ((ScreenCaptureHtmlUnitDriver) driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(scrFile, new File("D:\\TEMP.PNG"));
}catch (Exception e) {
e.printStackTrace();
}
}
}
HtmlUnit driver which I am using(the one which is in the link) is this-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.internal.Base64Encoder;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebWindow;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
public class ScreenCaptureHtmlUnitDriver extends HtmlUnitDriver implements TakesScreenshot {
private static Map<String, byte[]> imagesCache = Collections.synchronizedMap(new HashMap<String, byte[]>());
private static Map<String, String> cssjsCache = Collections.synchronizedMap(new HashMap<String, String>());
// http://stackoverflow.com/questions/4652777/java-regex-to-get-the-urls-from-css
private final static Pattern cssUrlPattern = Pattern.compile("background(-image)?[\\s]*:[^url]*url[\\s]*\\([\\s]*([^\\)]*)[\\s]*\\)[\\s]*");// ?<url>
public ScreenCaptureHtmlUnitDriver() {
super();
}
public ScreenCaptureHtmlUnitDriver(boolean enableJavascript) {
super(enableJavascript);
}
public ScreenCaptureHtmlUnitDriver(Capabilities capabilities) {
super(capabilities);
}
public ScreenCaptureHtmlUnitDriver(BrowserVersion version) {
super(version);
DesiredCapabilities var = ((DesiredCapabilities) getCapabilities());
var.setCapability(CapabilityType.TAKES_SCREENSHOT, true);
}
//#Override
#SuppressWarnings("unchecked")
public <X> X getScreenshotAs(OutputType<X> target) throws WebDriverException {
byte[] archive = new byte[0];
try {
archive = downloadCssAndImages(getWebClient(), (HtmlPage) getCurrentWindow().getEnclosedPage());
} catch (Exception e) {
}
if(target.equals(OutputType.BASE64)){
return target.convertFromBase64Png(new Base64Encoder().encode(archive));
}
if(target.equals(OutputType.BYTES)){
return (X) archive;
}
return (X) archive;
}
// http://stackoverflow.com/questions/2244272/how-can-i-tell-htmlunits-webclient-to-download-images-and-css
protected byte[] downloadCssAndImages(WebClient webClient, HtmlPage page) throws Exception {
WebWindow currentWindow = webClient.getCurrentWindow();
Map<String, String> urlMapping = new HashMap<String, String>();
Map<String, byte[]> files = new HashMap<String, byte[]>();
WebWindow window = null;
try {
window = webClient.getWebWindowByName(page.getUrl().toString()+"_screenshot");
webClient.getPage(window, new WebRequest(page.getUrl()));
} catch (Exception e) {
window = webClient.openWindow(page.getUrl(), page.getUrl().toString()+"_screenshot");
}
String xPathExpression = "//*[name() = 'img' or name() = 'link' and (#type = 'text/css' or #type = 'image/x-icon') or #type = 'text/javascript']";
List<?> resultList = page.getByXPath(xPathExpression);
Iterator<?> i = resultList.iterator();
while (i.hasNext()) {
try {
HtmlElement el = (HtmlElement) i.next();
String resourceSourcePath = el.getAttribute("src").equals("") ? el.getAttribute("href") : el
.getAttribute("src");
if (resourceSourcePath == null || resourceSourcePath.equals(""))
continue;
URL resourceRemoteLink = page.getFullyQualifiedUrl(resourceSourcePath);
String resourceLocalPath = mapLocalUrl(page, resourceRemoteLink, resourceSourcePath, urlMapping);
urlMapping.put(resourceSourcePath, resourceLocalPath);
if (!resourceRemoteLink.toString().endsWith(".css")) {
byte[] image = downloadImage(webClient, window, resourceRemoteLink);
files.put(resourceLocalPath, image);
} else {
String css = downloadCss(webClient, window, resourceRemoteLink);
for (String cssImagePath : getLinksFromCss(css)) {
URL cssImagelink = page.getFullyQualifiedUrl(cssImagePath.replace("\"", "").replace("\'", "")
.replace(" ", ""));
String cssImageLocalPath = mapLocalUrl(page, cssImagelink, cssImagePath, urlMapping);
files.put(cssImageLocalPath, downloadImage(webClient, window, cssImagelink));
}
files.put(resourceLocalPath, replaceRemoteUrlsWithLocal(css, urlMapping)
.replace("resources/", "./").getBytes());
}
} catch (Exception e) {
}
}
String pagesrc = replaceRemoteUrlsWithLocal(page.getWebResponse().getContentAsString(), urlMapping);
files.put("page.html", pagesrc.getBytes());
webClient.setCurrentWindow(currentWindow);
return createZip(files);
}
String downloadCss(WebClient webClient, WebWindow window, URL resourceUrl) throws Exception {
if (cssjsCache.get(resourceUrl.toString()) == null) {
cssjsCache.put(resourceUrl.toString(), webClient.getPage(window, new WebRequest(resourceUrl))
.getWebResponse().getContentAsString());
}
return cssjsCache.get(resourceUrl.toString());
}
byte[] downloadImage(WebClient webClient, WebWindow window, URL resourceUrl) throws Exception {
if (imagesCache.get(resourceUrl.toString()) == null) {
imagesCache.put(
resourceUrl.toString(),
IOUtils.toByteArray(webClient.getPage(window, new WebRequest(resourceUrl)).getWebResponse()
.getContentAsStream()));
}
return imagesCache.get(resourceUrl.toString());
}
public static byte[] createZip(Map<String, byte[]> files) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zipfile = new ZipOutputStream(bos);
Iterator<String> i = files.keySet().iterator();
String fileName = null;
ZipEntry zipentry = null;
while (i.hasNext()) {
fileName = i.next();
zipentry = new ZipEntry(fileName);
zipfile.putNextEntry(zipentry);
zipfile.write(files.get(fileName));
}
zipfile.close();
return bos.toByteArray();
}
List<String> getLinksFromCss(String css) {
List<String> result = new LinkedList<String>();
Matcher m = cssUrlPattern.matcher(css);
while (m.find()) { // find next match
result.add( m.group(2));
}
return result;
}
String replaceRemoteUrlsWithLocal(String source, Map<String, String> replacement) {
for (String object : replacement.keySet()) {
// background:url(http://org.com/images/image.gif)
source = source.replace(object, replacement.get(object));
}
return source;
}
String mapLocalUrl(HtmlPage page, URL link, String path, Map<String, String> replacementToAdd) throws Exception {
String resultingFileName = "resources/" + FilenameUtils.getName(link.getFile());
replacementToAdd.put(path, resultingFileName);
return resultingFileName;
}
}
UPDATE
Code provided by Andrew works- but I wanted to know if there is a way by which we can download only selected resources. For eg this website I would like to download only captcha image those id is "//*[#id='cimage']" because downloading all the resources will take a long time. Is there a way by which we can download only the specific resource. Because with the existing code provided
below all the resources get downloaded.
byte[] zipFileBytes = ((ScreenCaptureHtmlUnitDriver) driver).getScreenshotAs(OutputType.BYTES);
FileUtils.writeByteArrayToFile(new File("D:\\TEMP.PNG"), zipFileBytes);
The error says that the code is trying to convert a byte[] to a File. It's easy to see why if you just strip out the unused paths from getScreenshotAs:
public <X> X getScreenshotAs(OutputType<X> target) throws WebDriverException {
byte[] archive = new byte[0];
try {
archive = downloadCssAndImages(getWebClient(), (HtmlPage) getCurrentWindow().getEnclosedPage());
} catch (Exception e) {
}
return (X) archive;
}
There's no way you can get a File out of that. OutputType.FILE is not supported, so you have to handle file output yourself. Luckily, that's easy. You can change your code to:
byte[] zipFileBytes = ((ScreenCaptureHtmlUnitDriver) driver).getScreenshotAs(OutputType.BYTES);
FileUtils.writeByteArrayToFile(new File("D:\\TEMP.PNG"), zipFileBytes);
See FileUtils.writeByteArrayToFile() for more.
Check this out this may helpful for you
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(scrFile, new File("C:/Users/home/Desktop/screenshot.png"));// copy it somewhere