Skip to content

Commit

Permalink
Support microprofile-config.properties on JDT LS side
Browse files Browse the repository at this point in the history
See redhat-developer/vscode-quarkus#181

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Jan 24, 2020
1 parent 9713bc6 commit f3109ec
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public Integer getLocalServerPort() {
*/
public void setLocalServerPort(Integer localServerPort) {
this.localServerPort = localServerPort;
this.localBaseURL = null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
import static com.redhat.microprofile.jdt.core.utils.AnnotationUtils.getAnnotation;
import static com.redhat.microprofile.jdt.core.utils.AnnotationUtils.getAnnotationMemberValue;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
Expand All @@ -27,18 +24,13 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
Expand All @@ -49,7 +41,6 @@
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Position;
Expand All @@ -60,6 +51,7 @@
import com.redhat.microprofile.commons.MicroProfileJavaHoverInfo;
import com.redhat.microprofile.commons.MicroProfileJavaHoverParams;
import com.redhat.microprofile.jdt.core.utils.IJDTUtils;
import com.redhat.microprofile.jdt.internal.core.project.JDTMicroProfileProject;

/**
* JDT quarkus manager for Java files.
Expand All @@ -69,8 +61,6 @@
*/
public class PropertiesManagerForJava {

private static final Logger LOGGER = Logger.getLogger(PropertiesManagerForJava.class.getName());

private static final String LOCALHOST = "localhost";

private static final int PING_TIMEOUT = 2000;
Expand All @@ -83,131 +73,11 @@ public class PropertiesManagerForJava {

private static final PropertiesManagerForJava INSTANCE = new PropertiesManagerForJava();

private static class JDTQuarkusProjectInfo {

private static final String APPLICATION_PROPERTIES_FILE = "application.properties";

private static final int DEFAULT_PORT = 8080;

private Integer serverPort;

private long lastModified;

private File applicationPropertiesFile;

private Properties properties;

private final IJavaProject javaProject;

public JDTQuarkusProjectInfo(IJavaProject javaProject) {
this.javaProject = javaProject;
}

/**
* Returns the target/classes/application.properties and null otherwise.
*
* <p>
* Using this file instead of using src/main/resources/application.properties
* gives the capability to get the filtered value.
* </p>
*
* @return the target/classes/application.properties and null otherwise.
*/
private File getApplicationPropertiesFile() {
if (applicationPropertiesFile != null && applicationPropertiesFile.exists()) {
return applicationPropertiesFile;
}
try {
List<IPath> outputs = Stream.of(((JavaProject) javaProject).getResolvedClasspath(true)) //
.filter(entry -> !entry.isTest()) //
.filter(entry -> entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) //
.map(entry -> entry.getOutputLocation()) //
.filter(output -> output != null) //
.distinct() //
.collect(Collectors.toList());
for (IPath output : outputs) {
File file = javaProject.getProject().getLocation().append(output.removeFirstSegments(1))
.append(APPLICATION_PROPERTIES_FILE).toFile();
if (file.exists()) {
applicationPropertiesFile = file;

return applicationPropertiesFile;
}
}
return null;
} catch (JavaModelException e) {
return null;
}
}

/**
* Returns the loaded application.properties and null otherwise.
*
* @return the loaded application.properties and null otherwise
* @throws JavaModelException
*/
public Properties getApplicationProperties() throws JavaModelException {
File applicationPropertiesFile = getApplicationPropertiesFile();
if (applicationPropertiesFile == null) {
reset();
return null;
}
if (applicationPropertiesFile.lastModified() != lastModified) {
reset();
try (InputStream input = new FileInputStream(applicationPropertiesFile)) {
properties = new Properties();
// load a properties file
properties.load(input);
lastModified = applicationPropertiesFile.lastModified();
} catch (IOException e) {
reset();
LOGGER.log(Level.SEVERE,
"Error while loading properties from '" + applicationPropertiesFile.getPath() + "'.", e);
}
}
return properties;
}

private void reset() {
properties = null;
serverPort = null;
}

/**
* Returns the server port.
*
* @return the server port.
*/
public int getServerPort() {
try {
try {
// Get application properties and reset the cache if needs
Properties properties = getApplicationProperties();
if (serverPort == null) {
if (properties != null) {
String port = properties.getProperty("quarkus.http.port", "");
if (!port.trim().isEmpty()) {
serverPort = Integer.parseInt(port.trim());
}
}
}
} catch (JavaModelException e) {
LOGGER.log(Level.SEVERE, "Error while getting 'quarkus.http.port", e);
}
} finally {
if (serverPort == null) {
serverPort = DEFAULT_PORT;
}
}
return serverPort;
}
}

public static PropertiesManagerForJava getInstance() {
return INSTANCE;
}

private final Map<IJavaProject, JDTQuarkusProjectInfo> infos;
private final Map<IJavaProject, JDTMicroProfileProject> infos;

private PropertiesManagerForJava() {
this.infos = new HashMap<>();
Expand Down Expand Up @@ -268,10 +138,10 @@ private static ITypeRoot resolveTypeRoot(String uri, IJDTUtils utils, IProgressM
return unit != null ? unit : classFile;
}

private JDTQuarkusProjectInfo getJDTQuarkusProjectInfo(IJavaProject project) throws JavaModelException {
JDTQuarkusProjectInfo info = infos.get(project);
private JDTMicroProfileProject getJDTQuarkusProjectInfo(IJavaProject project) throws JavaModelException {
JDTMicroProfileProject info = infos.get(project);
if (info == null) {
info = new JDTQuarkusProjectInfo(project);
info = new JDTMicroProfileProject(project);
infos.put(project, info);
}
return info;
Expand Down Expand Up @@ -444,13 +314,7 @@ public MicroProfileJavaHoverInfo hover(MicroProfileJavaHoverParams params, IJDTU
return null;
}

Properties properties = getJDTQuarkusProjectInfo(javaProject).getApplicationProperties();

if (properties == null) {
return null;
}

String propertyValue = properties.getProperty(propertyKey);
String propertyValue = getJDTQuarkusProjectInfo(javaProject).getProperty(propertyKey, null);
if (propertyValue == null) {
propertyValue = getAnnotationMemberValue(annotation,
MicroProfileConstants.CONFIG_PROPERTY_ANNOTATION_DEFAULT_VALUE);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*******************************************************************************
* Copyright (c) 2020 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.microprofile.jdt.internal.core.project;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaProject;

/**
* Abstract class for config file.
*
* @author Angelo ZERR
*
* @param <T> the config model (ex: Properties for *.properties file)
*/
public abstract class AbstractConfigSource<T> implements IConfigSource {

private static final Logger LOGGER = Logger.getLogger(AbstractConfigSource.class.getName());

private final String configFileName;
private final IJavaProject javaProject;
private Path configFile;

private FileTime lastModified;

private T config;

public AbstractConfigSource(String configFileName, IJavaProject javaProject) {
this.configFileName = configFileName;
this.javaProject = javaProject;
}

/**
* Returns the target/classes/$configFile and null otherwise.
*
* <p>
* Using this file instead of using src/main/resources/$configFile gives the
* capability to get the filtered value.
* </p>
*
* @return the target/classes/$configFile and null otherwise.
*/
private Path getConfigFile() {
if (configFile != null && Files.exists(configFile)) {
return configFile;
}
try {
List<IPath> outputs = Stream.of(((JavaProject) javaProject).getResolvedClasspath(true)) //
.filter(entry -> !entry.isTest()) //
.filter(entry -> entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) //
.map(entry -> entry.getOutputLocation()) //
.filter(output -> output != null) //
.distinct() //
.collect(Collectors.toList());
for (IPath output : outputs) {
File file = javaProject.getProject().getLocation().append(output.removeFirstSegments(1))
.append(configFileName).toFile();
if (file.exists()) {
configFile = file.toPath();
return configFile;
}
}
return null;
} catch (JavaModelException e) {
LOGGER.log(Level.SEVERE, "Error while getting configuration", e);
return null;
}
}

/**
* Returns the loaded config and null otherwise.
*
* @return the loaded config and null otherwise
*/
private T getConfig() {
Path configFile = getConfigFile();
if (configFile == null) {
reset();
return null;
}
try {
FileTime currentLastModified = Files.getLastModifiedTime(configFile);
if (!currentLastModified.equals(lastModified)) {
reset();
try (InputStream input = new FileInputStream(configFile.toFile())) {
config = loadConfig(input);
lastModified = Files.getLastModifiedTime(configFile);
} catch (IOException e) {
reset();
LOGGER.log(Level.SEVERE, "Error while loading properties from '" + configFile + "'.", e);
}
}
} catch (IOException e1) {
LOGGER.log(Level.SEVERE, "Error while getting last modified time for '" + configFile + "'.", e1);
}
return config;
}

@Override
public final String getProperty(String key) {
T config = getConfig();
if (config == null) {
return null;
}
return getProperty(key, config);
}

@Override
public Integer getPropertyAsInt(String key) {
String property = getProperty(key);
if (property != null && !property.trim().isEmpty()) {
try {
return Integer.parseInt(property.trim());
} catch (NumberFormatException e) {
LOGGER.log(Level.SEVERE,
"Error while converting '" + property.trim() + "' as Integer for key '" + key + "'", e);
return null;
}
}
return null;
}

private void reset() {
config = null;
}

/**
* Load the config model from the given input stream <code>input</code>.
*
* @param input the input stream
* @return he config model from the given input stream <code>input</code>.
* @throws IOException
*/
protected abstract T loadConfig(InputStream input) throws IOException;

/**
* Returns the property from the given <code>key</code> and null otherwise.
*
* @param key
* @param config
* @return the property from the given <code>key</code> and null otherwise.
*/
protected abstract String getProperty(String key, T config);

}
Loading

0 comments on commit f3109ec

Please sign in to comment.