/*
* Copyright 2017-2020 original authors
*
* Licensed 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
*
* https://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 io.micronaut.inject.writer;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.util.StringUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Abstract implementation of the ClassWriterOutputVisitor
interface that deals with service descriptors in a common way across Java and Groovy. Author: graemerocher Since: 1.0
/**
* Abstract implementation of the {@link ClassWriterOutputVisitor} interface that deals with service descriptors in a
* common way across Java and Groovy.
*
* @author graemerocher
* @since 1.0
*/
@Internal
public abstract class AbstractClassWriterOutputVisitor implements ClassWriterOutputVisitor {
private final Map<String, Set<String>> serviceDescriptors = new HashMap<>();
private final boolean isWriteOnFinish;
Default constructor.
Params: - isWriteOnFinish – Is this the eclipse compiler
/**
* Default constructor.
* @param isWriteOnFinish Is this the eclipse compiler
*/
protected AbstractClassWriterOutputVisitor(boolean isWriteOnFinish) {
this.isWriteOnFinish = isWriteOnFinish;
}
Compatibility constructor.
/**
* Compatibility constructor.
*/
public AbstractClassWriterOutputVisitor() {
this.isWriteOnFinish = false;
}
@Override
public final Map<String, Set<String>> getServiceEntries() {
return serviceDescriptors;
}
@Override
public final void visitServiceDescriptor(String type, String classname) {
if (StringUtils.isNotEmpty(type) && StringUtils.isNotEmpty(classname)) {
serviceDescriptors.computeIfAbsent(type, s -> new HashSet<>()).add(classname);
}
}
@Override
public final void finish() {
// for Java we only write out service entries for the Eclipse compiler because
// for javac we support incremental compilation via ServiceDescriptionProcessor
// this approach doesn't work in Eclipse.
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=567116
// If the above issue is fixed then this workaround can be removed
// for Groovy writing service entries is also required as ServiceDescriptionProcessor
// is not triggered. See DirectoryClassWriterOutputVisitor
if (isWriteOnFinish) {
Map<String, Set<String>> serviceEntries = getServiceEntries();
writeServiceEntries(serviceEntries);
}
}
Writes the service entries.
Params: - serviceEntries – The service entries
/**
* Writes the service entries.
*
* @param serviceEntries The service entries
*/
public void writeServiceEntries(Map<String, Set<String>> serviceEntries) {
for (Map.Entry<String, Set<String>> entry : serviceEntries.entrySet()) {
String serviceName = entry.getKey();
Set<String> serviceTypes = entry.getValue();
Optional<GeneratedFile> serviceFile = visitMetaInfFile("services/" + serviceName);
if (serviceFile.isPresent()) {
GeneratedFile generatedFile = serviceFile.get();
// add the existing definitions
try (BufferedReader bufferedReader = new BufferedReader(generatedFile.openReader())) {
String line = bufferedReader.readLine();
while (line != null) {
serviceTypes.add(line);
line = bufferedReader.readLine();
}
} catch (FileNotFoundException | java.nio.file.NoSuchFileException x) {
// doesn't exist
} catch (IOException x) {
Throwable cause = x.getCause();
if (isNotEclipseNotFound(cause)) {
throw new ClassGenerationException("Failed to load existing service definition files: " + x, x);
}
} catch (Throwable e) {
// horrible hack to support Eclipse
if (isNotEclipseNotFound(e)) {
throw new ClassGenerationException("Failed to load existing service definition files: " + e, e);
}
}
// write out new definitions
try (BufferedWriter writer = new BufferedWriter(generatedFile.openWriter())) {
for (String serviceType : serviceTypes) {
writer.write(serviceType);
writer.newLine();
}
} catch (IOException x) {
throw new ClassGenerationException("Failed to open writer for service definition files: " + x);
}
}
}
}
private boolean isNotEclipseNotFound(Throwable e) {
if (isWriteOnFinish) {
return false;
}
String message = e.getMessage();
return !message.contains("does not exist") || !e.getClass().getName().startsWith("org.eclipse");
}
}