2015-11-13 108 views
1

测试自动生成的REST应用程序时出错我使用NetBeans助手开发了一个简单的RESTful应用程序用于测试目的。为此,我跟着这个视频:https://www.youtube.com/watch?v=RNJynlcqHNs。这个过程有点不同,因为我使用的是最新版本的NetBeans,但它应该具有相同的结果。在简历我所使用的工具:使用NetBeans + Glassfish + JAX-RS + JPA

  • 的NetBeans 8.1
  • GlassFish服务器4.1.1
  • MySQL的(没有任何问题,到现在为止)

的助手:

  • 实体来自数据库的类
  • 来自实体类的RESTful服务
  • REST风格的JavaScript客户端(使用骨干+ HTML5打造一个“功能齐全”的客户端)

据我所知,这个项目包括的EclipseLink(JPA 2.1)和新泽西州。

该项目部署正确,我可以直接从浏览器访问服务,但是当我尝试使用自动生成的Javascript客户端访问服务时,它无法完全加载。 Additionaly,我得到这个错误在浏览器的控制台:

无法加载资源:服务器500(内部服务器错误)的状态

回应,我得到了NetBeans/Glassfish的这个错误“控制台”:

Advertencia: StandardWrapperValve[service.ApplicationConfig]: Servlet.service() for servlet service.ApplicationConfig threw exception 

java.lang.NoClassDefFoundError: Could not initialize class org.eclipse.persistence.jaxb.BeanValidationHelper 

at org.eclipse.persistence.jaxb.JAXBBeanValidator.isConstrainedObject(JAXBBeanValidator.java:257) 

at org.eclipse.persistence.jaxb.JAXBBeanValidator.shouldValidate(JAXBBeanValidator.java:208) 

etc... 

这是JPA实体类代码:

package model; 

import java.io.Serializable; 
import java.util.List; 
import javax.persistence.Basic; 
import javax.persistence.CascadeType; 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.Id; 
import javax.persistence.NamedQueries; 
import javax.persistence.NamedQuery; 
import javax.persistence.OneToMany; 
import javax.persistence.Table; 
import javax.validation.constraints.NotNull; 
import javax.validation.constraints.Size; 
import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.bind.annotation.XmlTransient; 

/** 
* 
* @author USER 
*/ 
@Entity 
@Table(name = "oficina") 
@XmlRootElement 
@NamedQueries({ 
    @NamedQuery(name = "Oficina.findAll", query = "SELECT o FROM Oficina o"), 
    @NamedQuery(name = "Oficina.findByIdOficina", query = "SELECT o FROM Oficina o WHERE o.idOficina = :idOficina"), 
    @NamedQuery(name = "Oficina.findByNumero", query = "SELECT o FROM Oficina o WHERE o.numero = :numero"), 
    @NamedQuery(name = "Oficina.findByDescripcion", query = "SELECT o FROM Oficina o WHERE o.descripcion = :descripcion")}) 
public class Oficina implements Serializable { 

    private static final long serialVersionUID = 1L; 
    @Id 
    @Basic(optional = false) 
    @NotNull 
    @Column(name = "id_oficina") 
    private Integer idOficina; 
    @Basic(optional = false) 
    @NotNull 
    @Size(min = 1, max = 10) 
    @Column(name = "numero") 
    private String numero; 
    @Size(max = 100) 
    @Column(name = "descripcion") 
    private String descripcion; 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idOficina") 
    private List<Investigador> investigadorList; 

    public Oficina() { 
    } 

    public Oficina(Integer idOficina) { 
     this.idOficina = idOficina; 
    } 

    public Oficina(Integer idOficina, String numero) { 
     this.idOficina = idOficina; 
     this.numero = numero; 
    } 

    public Integer getIdOficina() { 
     return idOficina; 
    } 

    public void setIdOficina(Integer idOficina) { 
     this.idOficina = idOficina; 
    } 

    public String getNumero() { 
     return numero; 
    } 

    public void setNumero(String numero) { 
     this.numero = numero; 
    } 

    public String getDescripcion() { 
     return descripcion; 
    } 

    public void setDescripcion(String descripcion) { 
     this.descripcion = descripcion; 
    } 

    @XmlTransient 
    public List<Investigador> getInvestigadorList() { 
     return investigadorList; 
    } 

    public void setInvestigadorList(List<Investigador> investigadorList) { 
     this.investigadorList = investigadorList; 
    } 

    @Override 
    public int hashCode() { 
     int hash = 0; 
     hash += (idOficina != null ? idOficina.hashCode() : 0); 
     return hash; 
    } 

    @Override 
    public boolean equals(Object object) { 
     // TODO: Warning - this method won't work in the case the id fields are not set 
     if (!(object instanceof Oficina)) { 
      return false; 
     } 
     Oficina other = (Oficina) object; 
     if ((this.idOficina == null && other.idOficina != null) || (this.idOficina != null && !this.idOficina.equals(other.idOficina))) { 
      return false; 
     } 
     return true; 
    } 

    @Override 
    public String toString() { 
     return "model.Oficina[ idOficina=" + idOficina + " ]"; 
    } 

} 

这是服务代码:

package service; 

import java.util.List; 
import javax.ejb.Stateless; 
import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 
import javax.ws.rs.Consumes; 
import javax.ws.rs.DELETE; 
import javax.ws.rs.GET; 
import javax.ws.rs.POST; 
import javax.ws.rs.PUT; 
import javax.ws.rs.Path; 
import javax.ws.rs.PathParam; 
import javax.ws.rs.Produces; 
import javax.ws.rs.core.MediaType; 
import model.Oficina; 

/** 
* 
* @author USER 
*/ 
@Stateless 
@Path("oficinas") 
public class OficinaFacadeREST extends AbstractFacade<Oficina> { 

    @PersistenceContext(unitName = "grupoItosWSPU") 
    private EntityManager em; 

    public OficinaFacadeREST() { 
     super(Oficina.class); 
    } 

    @POST 
    @Override 
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
    public void create(Oficina entity) { 
     super.create(entity); 
    } 

    @PUT 
    @Path("{id}") 
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
    public void edit(@PathParam("id") Integer id, Oficina entity) { 
     super.edit(entity); 
    } 

    @DELETE 
    @Path("{id}") 
    public void remove(@PathParam("id") Integer id) { 
     super.remove(super.find(id)); 
    } 

    @GET 
    @Path("{id}") 
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
    public Oficina find(@PathParam("id") Integer id) { 
     return super.find(id); 
    } 

    @GET 
    @Override 
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
    public List<Oficina> findAll() { 
     return super.findAll(); 
    } 

    @GET 
    @Path("{from}/{to}") 
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
    public List<Oficina> findRange(@PathParam("from") Integer from, @PathParam("to") Integer to) { 
     return super.findRange(new int[]{from, to}); 
    } 

    @GET 
    @Path("count") 
    @Produces(MediaType.TEXT_PLAIN) 
    public String countREST() { 
     return String.valueOf(super.count()); 
    } 

    @Override 
    protected EntityManager getEntityManager() { 
     return em; 
    } 

} 

最后,这是Javascript代码:

var app = { 
    // Create this closure to contain the cached modules 
    module: function() { 
     // Internal module cache. 
     var modules = {}; 

     // Create a new module reference scaffold or load an 
     // existing module. 
     return function (name) { 
      // If this module has already been created, return it. 
      if (modules[name]) { 
       return modules[name]; 
      } 

      // Create a module and save it under this name 
      return modules[name] = {Views: {}}; 
     }; 
    }() 
}; 

(function (models) { 

// Model for Oficina entity 
    models.Oficina = Backbone.Model.extend({ 
     urlRoot: "http://localhost:8080/grupoItosWS/api/oficinas/", 
     idAttribute: 'idOficina', 
     defaults: { 
      descripcion: "", 
      numero: "" 
     }, 
     toViewJson: function() { 
      var result = this.toJSON(); // displayName property is used to render item in the list 
      result.displayName = this.get('idOficina'); 
      return result; 
     }, 
     isNew: function() { 
      // default isNew() method imlementation is 
      // based on the 'id' initialization which 
      // sometimes is required to be initialized. 
      // So isNew() is rediefined here 
      return this.notSynced; 
     }, 
     sync: function (method, model, options) { 
      options || (options = {}); 
      var errorHandler = { 
       error: function (jqXHR, textStatus, errorThrown) { 
        // TODO: put your error handling code here 
        // If you use the JS client from the different domain 
        // (f.e. locally) then Cross-origin resource sharing 
        // headers has to be set on the REST server side. 
        // Otherwise the JS client has to be copied into the 
        // some (f.e. the same) Web project on the same domain 
        alert('Unable to fulfil the request'); 
       } 
      }; 

      if (method === 'create') { 
       options.url = 'http://localhost:8080/grupoItosWS/api/oficinas/'; 
      } 
      var result = Backbone.sync(method, model, _.extend(options, errorHandler)); 
      return result; 
     } 


    }); 


    // Collection class for Oficina entities 
    models.OficinaCollection = Backbone.Collection.extend({ 
     model: models.Oficina, 
     url: "http://localhost:8080/grupoItosWS/api/oficinas/", 
     sync: function (method, model, options) { 
      options || (options = {}); 
      var errorHandler = { 
       error: function (jqXHR, textStatus, errorThrown) { 
        // TODO: put your error handling code here 
        // If you use the JS client from the different domain 
        // (f.e. locally) then Cross-origin resource sharing 
        // headers has to be set on the REST server side. 
        // Otherwise the JS client has to be copied into the 
        // some (f.e. the same) Web project on the same domain 
        alert('Unable to fulfil the request'); 
       } 
      }; 

      var result = Backbone.sync(method, model, _.extend(options, errorHandler)); 
      return result; 
     } 
    }); 


})(app.module("models")); 

(function (views) { 

    views.ListView = Backbone.View.extend({ 
     tagName: 'tbody', 
     initialize: function (options) { 
      this.options = options || {}; 
      this.model.bind("reset", this.render, this); 
      var self = this; 
      this.model.bind("add", function (modelName) { 
       var row = new views.ListItemView({ 
        model: modelName, 
        templateName: self.options.templateName 
       }).render().el; 
       $(self.el).append($(row)); 
       $(self.el).parent().trigger('addRows', [$(row)]); 
      }); 
     }, 
     render: function (eventName) { 
      var self = this; 
      _.each(this.model.models, function (modelName) { 
       $(this.el).append(new views.ListItemView({ 
        model: modelName, 
        templateName: self.options.templateName 
       }).render().el); 
      }, this); 
      return this; 
     } 
    }); 

    views.ListItemView = Backbone.View.extend({ 
     tagName: 'tr', 
     initialize: function (options) { 
      this.options = options || {}; 
      this.model.bind("change", this.render, this); 
      this.model.bind("destroy", this.close, this); 
     }, 
     template: function (json) { 
      /* 
      * templateName is element identifier in HTML 
      * $(this.options.templateName) is element access to the element 
      * using jQuery 
      */ 
      return _.template($(this.options.templateName).html())(json); 
     }, 
     render: function (eventName) { 
      $(this.el).html(this.template(this.model.toJSON())); 
      return this; 
     }, 
     close: function() { 
      var table = $(this.el).parent().parent(); 
      table.trigger('disable.pager'); 
      $(this.el).unbind(); 
      $(this.el).remove(); 
      table.trigger('enable.pager'); 
     } 

    }); 

    views.ModelView = Backbone.View.extend({ 
     initialize: function (options) { 
      this.options = options || {}; 
      this.model.bind("change", this.render, this); 
     }, 
     render: function (eventName) { 
      $(this.el).html(this.template(this.model.toJSON())); 
      return this; 
     }, 
     template: function (json) { 
      /* 
      * templateName is element identifier in HTML 
      * $(this.options.templateName) is element access to the element 
      * using jQuery 
      */ 
      return _.template($(this.options.templateName).html())(json); 
     }, 
     /* 
     * Classes "save" and "delete" are used on the HTML controls to listen events. 
     * So it is supposed that HTML has controls with these classes. 
     */ 
     events: { 
      "change input": "change", 
      "click .save": "save", 
      "click .delete": "drop" 
     }, 
     change: function (event) { 
      var target = event.target; 
      console.log('changing ' + target.id + ' from: ' + target.defaultValue + ' to: ' + target.value); 
     }, 
     save: function() { 
      // TODO : put save code here 
      var hash = this.options.getHashObject(); 
      this.model.set(hash); 
      if (this.model.isNew() && this.collection) { 
       var self = this; 
       this.collection.create(this.model, { 
        success: function() { 
         // see isNew() method implementation in the model 
         self.model.notSynced = false; 
         self.options.navigate(self.model.id); 
        } 
       }); 
      } else { 
       this.model.save(); 
       this.model.el.parent().parent().trigger("update"); 
      } 
      return false; 
     }, 
     drop: function() { 
      this.model.destroy({ 
       success: function() { 
        /* 
        * TODO : put your code here 
        * f.e. alert("Model is successfully deleted"); 
        */ 
        window.history.back(); 
       } 
      }); 
      return false; 
     }, 
     close: function() { 
      $(this.el).unbind(); 
      $(this.el).empty(); 
     } 
    }); 

    // This view is used to create new model element 
    views.CreateView = Backbone.View.extend({ 
     initialize: function (options) { 
      this.options = options || {}; 
      this.render(); 
     }, 
     render: function (eventName) { 
      $(this.el).html(this.template()); 
      return this; 
     }, 
     template: function (json) { 
      /* 
      * templateName is element identifier in HTML 
      * $(this.options.templateName) is element access to the element 
      * using jQuery 
      */ 
      return _.template($(this.options.templateName).html())(json); 
     }, 
     /* 
     * Class "new" is used on the control to listen events. 
     * So it is supposed that HTML has a control with "new" class. 
     */ 
     events: { 
      "click .new": "create" 
     }, 
     create: function (event) { 
      this.options.navigate(); 
      return false; 
     } 
    }); 

})(app.module("views")); 


$(function() { 
    var models = app.module("models"); 
    var views = app.module("views"); 

    var AppRouter = Backbone.Router.extend({ 
     routes: { 
      '': 'list', 
      'new': 'create' 
      , 
      ':id': 'details' 
     }, 
     initialize: function() { 
      var self = this; 
      $('#create').html(new views.CreateView({ 
       // tpl-create is template identifier for 'create' block 
       templateName: '#tpl-create', 
       navigate: function() { 
        self.navigate('new', true); 
       } 
      }).render().el); 
     }, 
     list: function() { 
      this.collection = new models.OficinaCollection(); 
      var self = this; 
      this.collection.fetch({ 
       success: function() { 
        self.listView = new views.ListView({ 
         model: self.collection, 
         // tpl-oficina-list-itemis template identifier for item 
         templateName: '#tpl-oficina-list-item' 
        }); 
        $('#datatable').html(self.listView.render().el).append(_.template($('#thead').html())()); 
        if (self.requestedId) { 
         self.details(self.requestedId); 
        } 
        var pagerOptions = { 
         // target the pager markup 
         container: $('.pager'), 
         // output string - default is '{page}/{totalPages}'; possiblevariables: {page}, {totalPages},{startRow}, {endRow} and {totalRows} 
         output: '{startRow} to {endRow} ({totalRows})', 
         // starting page of the pager (zero based index) 
         page: 0, 
         // Number of visible rows - default is 10 
         size: 10 
        }; 
        $('#datatable').tablesorter({widthFixed: true, 
         widgets: ['zebra']}). 
          tablesorterPager(pagerOptions); 
       } 
      }); 
     }, 
     details: function (id) { 
      if (this.collection) { 
       this.oficina = this.collection.get(id); 
       if (this.view) { 
        this.view.close(); 
       } 
       var self = this; 
       this.view = new views.ModelView({ 
        model: this.oficina, 
        // tpl-oficina-details is template identifier for chosen model element 
        templateName: '#tpl-oficina-details', 
        getHashObject: function() { 
         return self.getData(); 
        } 
       }); 
       $('#details').html(this.view.render().el); 
      } else { 
       this.requestedId = id; 
       this.list(); 
      } 
     }, 
     create: function() { 
      if (this.view) { 
       this.view.close(); 
      } 
      var self = this; 
      var dataModel = new models.Oficina(); 
      // see isNew() method implementation in the model 
      dataModel.notSynced = true; 
      this.view = new views.ModelView({ 
       model: dataModel, 
       collection: this.collection, 
       // tpl-oficina-details is a template identifier for chosen model element 
       templateName: '#tpl-oficina-details', 
       navigate: function (id) { 
        self.navigate(id, false); 
       }, 
       getHashObject: function() { 
        return self.getData(); 
       } 
      }); 
      $('#details').html(this.view.render().el); 
     }, 
     getData: function() { 
      return { 
       idOficina: $('#idOficina').val(), 
       descripcion: $('#descripcion').val(), 
       numero: $('#numero').val() 
      }; 
     } 
    }); 
    new AppRouter(); 


    Backbone.history.start(); 
}); 

我一直在寻找了一段时间的解决方案,但我还没有找到它,此外,由于我对JAX-RS或骨干不太了解,所以对我来说这个错误是神秘的。有没有人有过这个问题?有没有解决方法?

回答

1

我不知道你是否已经解决了您的问题,和我有类似的问题突然让我所有的网页SERV

自从我解决我的问题,我想我会在这里分享。

这不完全是您的代码问题,因为它是Eclipselink 2.6.0的问题。您可以修复是撞了你的Eclipse的链接版本2.6.1 RC2或者简单地通过添加以下代码:

,org.xml.sax.helpers,javax.xml.parsers;resolution:=optional,javax.naming;resolution:=optional 

到位于的GlassFish /模块/ org.eclipse.persistence.moxy进口包装。罐/ META-INF /清单。MF

+0

非常感谢!当我试图在netbeans和glassfish中做一些基本的REST API时,这让我疯狂。如果有人想知道,manifest.mf文件已经设置了换行符,但是我发现我可以直接将Ken的代码示例粘贴到清单中,而不用担心将换行匹配。 –

+0

有人可以提供一些关于如何更新manifest.mf文件的指导吗?每次我尝试更新时,都会收到“未指定的错误”。我正在使用7-zip和记事本 –

0

我有同样的问题,我发现的独特解决方案是将glassfish 4.1.1降级到4.1。我发现我的glassfish版本正在使用eclipselink 2.6.1,所以在我之前提出的解决方案并不适合我。