blob: a99358940e5c6987bfe2a92ebd6d43d964dd5e75 [file] [log] [blame]
/*
* 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 org.netbeans.modules.websvc.rest;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeParameterTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WildcardTree;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.element.Modifier;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.ModificationResult;
import org.netbeans.api.java.source.Task;
import org.netbeans.api.java.source.TreeMaker;
import org.netbeans.api.java.source.TreeUtilities;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.modules.j2ee.metadata.model.api.MetadataModelAction;
import org.netbeans.modules.j2ee.metadata.model.api.MetadataModelException;
import org.netbeans.modules.websvc.rest.model.api.RestApplication;
import org.netbeans.modules.websvc.rest.model.api.RestApplicationModel;
import org.netbeans.modules.websvc.rest.model.api.RestApplications;
import org.netbeans.modules.websvc.rest.model.api.RestConstants;
import org.netbeans.modules.websvc.rest.model.api.RestProviderDescription;
import org.netbeans.modules.websvc.rest.model.api.RestServiceDescription;
import org.netbeans.modules.websvc.rest.model.api.RestServices;
import org.netbeans.modules.websvc.rest.model.api.RestServicesMetadata;
import org.netbeans.modules.websvc.rest.model.api.RestServicesModel;
import org.netbeans.modules.websvc.rest.spi.MiscUtilities;
import org.netbeans.modules.websvc.rest.spi.RestSupport;
import org.openide.cookies.SaveCookie;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.util.RequestProcessor;
/**
* This class contains everything related to generation of Application subclass which used
* to be defined directly in RestSupport or one of its subclasses. I tried to move
* it here as a logical piece of functionality. The methods itself in this class
* were never reviewed - I just moved them from somewhere else. See also
* WebXmlUpdater class which has similar role for everything related to web.xml update.
*/
public class ApplicationSubclassGenerator {
static final String GET_REST_RESOURCE_CLASSES = "getRestResourceClasses";//NOI18N
private RequestProcessor.Task refreshTask = null;
private static RequestProcessor RP = new RequestProcessor(ApplicationSubclassGenerator.class);
private RestSupport restSupport;
public ApplicationSubclassGenerator(RestSupport restSupport) {
this.restSupport = restSupport;
}
public void refreshApplicationSubclass() {
getRefreshTask().schedule(1000);
}
private synchronized RequestProcessor.Task getRefreshTask() {
if (refreshTask == null) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
doReconfigure();
}
catch(IOException e ){
Logger.getLogger(RestSupport.class.getName()).log(
Level.INFO, e.getLocalizedMessage(), e);
}
}
};
refreshTask = RP.create(runnable);
}
return refreshTask;
}
private void doReconfigure() throws IOException {
RestApplicationModel restAppModel = restSupport.getRestApplicationsModel();
RestServicesModel model = restSupport.getRestServicesModel();
String clazz = null;
Collection<String> classNames = Collections.emptyList();
try {
clazz = restAppModel.runReadAction(
new MetadataModelAction<RestApplications, String>() {
@Override
public String run(final RestApplications metadata)
throws IOException {
List<RestApplication> applications =
metadata.getRestApplications();
if (applications != null
&& !applications.isEmpty()) {
RestApplication application =
applications.get(0);
String clazz = application.
getApplicationClass();
return clazz;
}
return null;
}
});
if (clazz != null) {
classNames = model.runReadAction(new MetadataModelAction<RestServicesMetadata, Collection<String>>() {
@Override
public Collection<String> run(RestServicesMetadata metadata) throws Exception {
Collection<String> classes = new TreeSet<String>();
RestServices services = metadata.getRoot();
for (RestServiceDescription description : services.getRestServiceDescription()) {
// ignore REST services for which we do not have sources (#216168, #229168):
if (description.getFile() != null) {
classes.add(description.getClassName());
}
}
for (RestProviderDescription provider : services.getProviders()) {
// ignore REST providers for which we do not have sources (#216168, #229168):
if (provider.getFile() != null) {
classes.add(provider.getClassName());
}
}
return classes;
}
});
}
} catch (MetadataModelException ex) {
Logger.getLogger(RestSupport.class.getName()).log(
Level.INFO, ex.getLocalizedMessage(), ex);
}
if (clazz != null) {
reconfigApplicationClass(clazz, classNames);
}
}
protected void reconfigApplicationClass(String appClassFqn, final Collection<String> classNames) throws IOException {
JavaSource javaSource = MiscPrivateUtilities.getJavaSourceFromClassName(restSupport.getProject(), appClassFqn);
if ( javaSource == null ){
return;
}
ModificationResult res = javaSource.runModificationTask( new Task<WorkingCopy>() {
@Override
public void run(WorkingCopy workingCopy) throws Exception {
workingCopy.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
CompilationUnitTree tree = workingCopy.getCompilationUnit();
for (Tree typeDeclaration : tree.getTypeDecls()){
if (TreeUtilities.CLASS_TREE_KINDS.contains(typeDeclaration.getKind())){
MethodTree getClasses = null;
MethodTree restResources = null;
MethodTree restResources2 = null;
ClassTree classTree = (ClassTree) typeDeclaration;
List<? extends Tree> members = classTree.getMembers();
for (Tree member : members) {
if ( member.getKind().equals(Tree.Kind.METHOD)){
MethodTree method = (MethodTree)member;
String name = method.getName().toString();
if ( name.equals(RestConstants.GET_CLASSES)){
getClasses = method;
}
else if ( name.equals(GET_REST_RESOURCE_CLASSES)){
restResources = method;
} else if ( name.equals(RestConstants.GET_REST_RESOURCE_CLASSES2)){
restResources2 = method;
}
}
}
TreeMaker maker = workingCopy.getTreeMaker();
ClassTree modified = classTree;
if (getClasses != null && restResources != null) {
// this is old code generator replaced in NB 7.3.1
// as part of EE7 upgrade:
modified = removeResourcesMethod( restResources,
maker, modified);
modified = createMethodsOlderVersion(classNames,
maker, modified, workingCopy);
} else {
if (restResources2 != null) {
modified = removeResourcesMethod( restResources2,
maker, modified);
modified = createMethods(classNames, getClasses,
maker, modified, workingCopy);
}
}
workingCopy.rewrite(classTree, modified);
}
}
}
});
res.commit();
Collection<FileObject> files = javaSource.getFileObjects();
if ( files.isEmpty() ){
return;
}
FileObject fileObject = files.iterator().next();
DataObject dataObject = DataObject.find(fileObject);
if ( dataObject!= null){
SaveCookie cookie = dataObject.getLookup().lookup(SaveCookie.class);
if ( cookie!= null ){
cookie.save();
}
}
}
private ClassTree removeResourcesMethod( MethodTree restResources,
TreeMaker maker, ClassTree modified )
{
return maker.removeClassMember(modified, restResources);
}
private ClassTree createMethods(Collection<String> classNames, MethodTree getClasses,
TreeMaker maker, ClassTree modified,
CompilationController controller) throws IOException
{
WildcardTree wildCard = maker.Wildcard(Tree.Kind.UNBOUNDED_WILDCARD,
null);
ParameterizedTypeTree wildClass = maker.ParameterizedType(
maker.QualIdent(Class.class.getCanonicalName()),
Collections.singletonList(wildCard));
ParameterizedTypeTree wildSet = maker.ParameterizedType(
maker.QualIdent(Set.class.getCanonicalName()),
Collections.singletonList(wildClass));
String methodBody = MiscPrivateUtilities.collectRestResources(classNames, restSupport, false);
return MiscUtilities.createAddResourceClasses(maker, modified, controller, methodBody, false);
}
private ClassTree createMethodsOlderVersion(Collection<String> classNames,
TreeMaker maker,ClassTree modified,
CompilationController controller) throws IOException
{
WildcardTree wildCard = maker.Wildcard(Tree.Kind.UNBOUNDED_WILDCARD,
null);
ParameterizedTypeTree wildClass = maker.ParameterizedType(
maker.QualIdent(Class.class.getCanonicalName()),
Collections.singletonList(wildCard));
ParameterizedTypeTree wildSet = maker.ParameterizedType(
maker.QualIdent(Set.class.getCanonicalName()),
Collections.singletonList(wildClass));
//StringBuilder builder = new StringBuilder();
String methodBody = MiscPrivateUtilities.collectRestResources(classNames, restSupport, true);
ModifiersTree modifiersTree = maker.Modifiers(EnumSet
.of(Modifier.PRIVATE));
MethodTree methodTree = maker.Method(modifiersTree,
GET_REST_RESOURCE_CLASSES, wildSet,
Collections.<TypeParameterTree> emptyList(),
Collections.<VariableTree> emptyList(),
Collections.<ExpressionTree> emptyList(), methodBody,
null);
modified = maker.addClassMember(modified, methodTree);
return modified;
}
public static String getApplicationPathFromAnnotations(RestSupport restSupport, final String applPathFromDD) {
List<RestApplication> restApplications = restSupport.getRestApplications();
if (applPathFromDD == null) {
if (restApplications.isEmpty()) {
return null;
} else {
return restSupport.getApplicationPathFromDialog(restApplications);
}
} else {
if (restApplications.isEmpty()) {
return applPathFromDD;
} else {
boolean found = false;
for (RestApplication appl: restApplications) {
if (applPathFromDD.equals(appl.getApplicationPath())) {
found = true;
break;
}
}
if (!found) {
restApplications.add(new RestApplication() {
public String getApplicationPath() {
return applPathFromDD;
}
public String getApplicationClass() {
return "web.xml"; //NOI18N
}
});
}
return restSupport.getApplicationPathFromDialog(restApplications);
}
}
}
}