1

テスト目的で、NetBeans アシスタントを使用して単純な RESTful アプリケーションを開発しました。このために、次のビデオをフォローしました: https://www.youtube.com/watch?v=RNJynlcqHNs . 最新バージョンの NetBeans を使用しているため、プロセスは少し異なりましたが、同じ結果になるはずでした。履歴書では、次のツールを使用しました。

  • NetBeans 8.1
  • Glassfish サーバー 4.1.1
  • MySQL (今のところ問題なし)

アシスタント:

  • データベースからのエンティティ クラス
  • エンティティ クラスからの RESTful サービス
  • RESTful Javascript クライアント (Backbone+HTML5 を使用して「完全に機能する」クライアントを構築する)

私の知る限り、このプロジェクトには EclipseLink (JPA 2.1) と Jersey が含まれています。

プロジェクトは正しくデプロイされ、ブラウザからサービスに直接アクセスできますが、自動生成された Javascript クライアントを使用してサービスにアクセスしようとすると、サービスが完全にロードされません。さらに、ブラウザのコンソールに次のエラーが表示されます。

リソースのロードに失敗しました: サーバーは 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 や Backbone についてあまり知らないので、エラーは不可解です。誰かが以前にこの問題を抱えていましたか? 解決策はありますか?

4

2 に答える 2

0

私は同じ問題を抱えており、私が見つけた独自の解決策は、glassfish 4.1.1 を 4.1 にダウングレードすることでした。私のバージョンのグラスフィッシュは eclipselink 2.6.1 を使用していたので、私の前に提案された解決策はうまくいきませんでした。

于 2016-03-23T23:26:10.063 に答える