added client and server REST support

This commit is contained in:
2015-03-29 22:16:27 +02:00
parent 9b37788528
commit 4a9a01068f
15 changed files with 692 additions and 281 deletions

View File

@@ -0,0 +1,17 @@
package client;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
/**
* Created by walter on 29/03/15.
*/
public class MainClient
{
public static void main(String[] args)
{
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://www.myserver.com/book");
}
}

View File

@@ -0,0 +1,48 @@
package model;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Created by walter on 29/03/15.
*/
@XmlRootElement
@XmlSeeAlso(PkgData.class)
public class PkgList extends ArrayList<PkgData>
{
// ======================================
// = Constructors =
// ======================================
public PkgList()
{
super();
}
public PkgList(Collection<? extends PkgData> c)
{
super(c);
}
// ======================================
// = Getters & Setters =
// ======================================
@XmlElement(name = "PkgData")
public List<PkgData> getPackages()
{
return this;
}
public void setPackages(List<PkgData> pkgs)
{
this.clear();
this.addAll(pkgs);
}
}

View File

@@ -0,0 +1,55 @@
package persistence;
import java.util.ArrayList;
import java.util.List;
/**
* Created by walter on 29/03/15.
*/
public class QueryEngine
{
private String entityName;
private String query;
private List<String> where;
public QueryEngine(Class<?> cls)
{
query = String.format("SELECT e FROM %s e", cls.getSimpleName());
this.entityName = cls.getSimpleName();
where = new ArrayList<>();
}
public QueryEngine(String entityName)
{
query = String.format("SELECT e FROM %s e", entityName);
where = new ArrayList<>();
}
public QueryEngine select(String... fields)
{
String[] strarr = new String[fields.length];
for(int i=0; i<fields.length; i++)
{
strarr[i] = "e." + fields[i];
}
query = "SELECT " + String.join(",", strarr) + " FROM " + entityName + " e";
return this;
}
public QueryEngine select()
{
query = String.format("SELECT e FROM %s e", entityName);
return this;
}
public QueryEngine where(String field, String operator, String value)
{
where.add(String.format("e.%s %s '%s'", field, operator, value));
return this;
}
public String build()
{
return query + " WHERE " + String.join(" AND ", where);
}
}

View File

@@ -6,7 +6,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ApplicationPath("rs")
@ApplicationPath("rest")
public class ApplicationConfig extends Application
{
@@ -20,10 +20,10 @@ public class ApplicationConfig extends Application
// = Constructors =
// ======================================
public ApplicationConfig() {
public ApplicationConfig()
{
HashSet<Class<?>> c = new HashSet<>();
c.add(PacmanService.class);
c.add(PacmanWebService.class);
classes = Collections.unmodifiableSet(c);
}

View File

@@ -0,0 +1,51 @@
package service;
import javax.enterprise.context.ApplicationScoped;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Created by walter on 29/03/15.
*/
@ApplicationScoped
public class ApplicationContext
{
private Properties systemProperties;
public ApplicationContext()
{
systemProperties = new Properties();
InputStream input = null;
try
{
input = new FileInputStream("/etc/jpacrepo/config.properties");
// load a properties file
systemProperties.load(input);
// get the property value and print it out
} catch (IOException ex)
{
throw new RuntimeException(ex);
} finally
{
if (input != null)
{
try
{
input.close();
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
}
public Properties getSystemProperties()
{
return systemProperties;
}
}

View File

@@ -1,273 +1,14 @@
package service;
import model.PkgData;
import model.PkgName;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import pacbase.Parser;
import javax.ejb.Remote;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.*;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by walter on 28/03/15.
*/
@Startup
@Path("/pkg")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Singleton
public class PacmanService
@Remote
public interface PacmanService
{
private Properties prop;
@PersistenceContext(unitName = "jpacrepo_pu")
private EntityManager em;
@Context
private UriInfo uriInfo;
private Logger log = Logger.getLogger(PacmanService.class.getName());
private String nameQuery = "SELECT pname FROM PkgName pname WHERE id = :name";
private String fileQuery = "SELECT pdata FROM PkgData pdata WHERE name.id = :name AND version = :version AND arch = :arch";
private String fileNameQuery = "SELECT pdata FROM PkgData pdata WHERE fileName = :fileName";
private String hashQuery = "SELECT pdata FROM PkgData pdata WHERE md5sum = :md5sum";
private String idQuery = "SELECT pdata FROM PkgData pdata WHERE id = :id";
@PostConstruct
public void syncDB()
{
loadProperties();
//Elimina i pacchetti sul DB che non esistono più nel filesystem
List<PkgData> listaDB = em.createQuery("SELECT p FROM PkgData p", PkgData.class).getResultList();
for (PkgData p : listaDB)
{
File file = new File(p.filePath);
if (!file.exists())
{
log.log(Level.INFO, String.format("Removing package %s", file.getName()));
em.remove(p);
}
}
//Aggiunge sul DB i pacchetti presenti nel filesystem
Collection<File> ls = FileUtils.listFiles(new File(prop.getProperty("RepoFolder")), new RegexFileFilter(".*\\.pkg\\.tar\\.xz"), DirectoryFileFilter.DIRECTORY);
int i = 0;
TypedQuery<PkgData> fquery = em.createQuery(fileQuery, PkgData.class);
TypedQuery<PkgName> nquery = em.createQuery(nameQuery, PkgName.class);
for (File file : ls)
{
try
{
PkgData data = Parser.parseFile(file);
fquery.setParameter("name", data.name.id);
fquery.setParameter("version", data.version);
fquery.setParameter("arch", data.arch);
List<PkgData> savedFiles = fquery.getResultList();
if (savedFiles.size() > 0) continue;
nquery.setParameter("name", data.name.id);
List<PkgName> savedName = nquery.getResultList();
if (savedName.size() > 0)
{
data.name = savedName.get(0);
}
em.persist(data);
log.log(Level.INFO, String.format("Persisting package %s", file.getName()));
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
private void loadProperties()
{
prop = new Properties();
InputStream input = null;
try
{
input = new FileInputStream("/etc/jpacrepo/config.properties");
// load a properties file
prop.load(input);
// get the property value and print it out
} catch (IOException ex)
{
throw new RuntimeException(ex);
} finally
{
if (input != null)
{
try
{
input.close();
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
}
/**
* JSON : curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-service-1.0/rs/book/1 -v
* XML : curl -X GET -H "Accept: application/xml" http://localhost:8080/chapter15-service-1.0/rs/book/1 -v
*/
@GET
@Path("search")
public Response getPackage(@QueryParam("name") String name, @QueryParam("version") String version, @QueryParam("arch") String arch)
{
TypedQuery<PkgData> fquery = em.createQuery(fileQuery, PkgData.class);
if(name != null) fquery.setParameter("name", name);
if(version != null) fquery.setParameter("version", version);
if(arch != null) fquery.setParameter("arch", arch);
PkgData pkg = fquery.getSingleResult();
if (pkg == null)
{
throw new NotFoundException();
}
return Response.ok(pkg).build();
}
@GET
@Path("searchHash")
public Response getPackageByHash(@QueryParam("md5") String md5sum)
{
TypedQuery<PkgData> hquery = em.createQuery(hashQuery, PkgData.class);
if(md5sum != null) hquery.setParameter("md5sum", md5sum);
PkgData pkg = hquery.getSingleResult();
if (pkg == null)
{
throw new NotFoundException();
}
return Response.ok(pkg).build();
}
@GET
@Path("searchFilename")
public Response getPackageByFileName(@QueryParam("file") String file)
{
TypedQuery<PkgData> fnquery = em.createQuery(fileNameQuery, PkgData.class);
fnquery.setParameter("fileName", file);
PkgData pkg = fnquery.getSingleResult();
if (pkg == null)
{
throw new NotFoundException();
}
return Response.ok(pkg).build();
}
@POST
@Path("/pkg/upload")
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response createPackage(byte[] input, @MatrixParam("filename") String filename) throws Exception
{
if (filename == null)
throw new BadRequestException();
String hash = Parser.computeMD5(new ByteArrayInputStream(input));
TypedQuery<PkgData> hquery = em.createQuery(hashQuery, PkgData.class);
hquery.setParameter("md5sum", hash);
List<PkgData> savedFiles = hquery.getResultList();
if (savedFiles.size() > 0)
{
return null;
}
else
{
File file = new File(prop.getProperty("RepoFolder"), filename);
FileOutputStream fos = new FileOutputStream(file);
fos.write(input);
fos.close();
PkgData pkg = Parser.parseFile(file);
em.persist(pkg);
URI pkgUri = uriInfo.getAbsolutePathBuilder().path(pkg.filePath).build();
return Response.created(pkgUri).build();
}
}
public void syncDB();
}
// ======================================
// = Public Methods =
// ======================================
/**
* curl -X POST --data-binary "<book><description>Science fiction comedy book</description><illustrations>false</illustrations><isbn>1-84023-742-2</isbn><nbOfPage>354</nbOfPage><price>12.5</price><title>The Hitchhiker's Guide to the Galaxy</title></book>" -H "Content-Type: application/xml" http://localhost:8080/chapter15-service-1.0/rs/book -v
* curl -X POST --data-binary "{\"description\":\"Science fiction comedy book\",\"illustrations\":false,\"isbn\":\"1-84023-742-2\",\"nbOfPage\":354,\"price\":12.5,\"title\":\"The Hitchhiker's Guide to the Galaxy\"}" -H "Content-Type: application/json" http://localhost:8080/chapter15-service-1.0/rs/book -v
*/
// @POST
// public Response createBook(Book book) {
// if (book == null)
// throw new BadRequestException();
//
// em.persist(book);
// URI bookUri = uriInfo.getAbsolutePathBuilder().path(book.getId()).build();
// return Response.created(bookUri).build();
// }
// @PUT
// public Response updateBook(Book book) {
// if (book == null)
// throw new BadRequestException();
//
// em.merge(book);
// return Response.ok().build();
// }
/**
* curl -X DELETE http://localhost:8080/chapter15-service-1.0/rs/book/1 -v
*/
// @DELETE
// @Path("{id}")
// public Response deleteBook(@PathParam("id") String id) {
// Book book = em.find(Book.class, id);
//
// if (book == null)
// throw new NotFoundException();
//
// em.remove(book);
//
// return Response.noContent().build();
// }
/**
* JSON : curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-service-1.0/rs/book -v
* XML : curl -X GET -H "Accept: application/xml" http://localhost:8080/chapter15-service-1.0/rs/book -v
*/
// @GET
// public Response getAllBooks() {
// TypedQuery<Book> query = em.createNamedQuery(Book.FIND_ALL, Book.class);
// Books books = new Books(query.getResultList());
// return Response.ok(books).build();
// }

View File

@@ -0,0 +1,128 @@
package service;
import model.PkgData;
import model.PkgName;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import pacbase.Parser;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.transaction.*;
import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@Startup
@Singleton
@TransactionManagement(TransactionManagementType.BEAN)
public class PacmanServiceEJB implements PacmanService
{
@PersistenceContext(unitName = "jpacrepo_pu")
private EntityManager em;
@Inject
private ApplicationContext ctx;
private Logger log = Logger.getLogger(PacmanServiceEJB.class.getName());
private String nameQuery = "SELECT pname FROM PkgName pname WHERE id = :name";
private String fileQuery = "SELECT pdata FROM PkgData pdata WHERE name.id = :name AND version = :version AND arch = :arch";
private String fileNameQuery = "SELECT pdata FROM PkgData pdata WHERE fileName = :fileName";
private String hashQuery = "SELECT pdata FROM PkgData pdata WHERE md5sum = :md5sum";
private String idQuery = "SELECT pdata FROM PkgData pdata WHERE id = :id";
@PostConstruct
public void syncDB()
{
Set<String> knownPkg = new HashSet<>();
//Elimina i pacchetti sul DB che non esistono più nel filesystem
List<PkgData> listaDB = em.createQuery("SELECT p FROM PkgData p", PkgData.class).getResultList();
try
{
ut.setTransactionTimeout(1000);
for (PkgData p : listaDB)
{
knownPkg.add(p.filePath);
File file = new File(p.filePath);
if (!file.exists())
{
ut.begin();
log.log(Level.INFO, String.format("Removing package %s", file.getName()));
em.remove(p);
ut.commit();
}
}
}
catch (NotSupportedException | SystemException | HeuristicMixedException | RollbackException | HeuristicRollbackException e)
{
throw new RuntimeException(e);
}
//Aggiunge sul DB i pacchetti presenti nel filesystem
Collection<File> ls = FileUtils.listFiles(new File(ctx.getSystemProperties().getProperty("RepoFolder")), new RegexFileFilter(".*\\.pkg\\.tar\\.xz"), DirectoryFileFilter.DIRECTORY);
for (File file : ls)
{
if(!knownPkg.contains(file.getAbsolutePath()))
{
try
{
ut.setTransactionTimeout(1000);
ut.begin();
parseFile(file);
ut.commit();
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
}
@Resource
private UserTransaction ut;
private void parseFile(File file) throws Exception
{
TypedQuery<PkgData> fquery = em.createQuery(fileQuery, PkgData.class);
TypedQuery<PkgName> nquery = em.createQuery(nameQuery, PkgName.class);
PkgData data = Parser.parseFile(file);
fquery.setParameter("name", data.name.id);
fquery.setParameter("version", data.version);
fquery.setParameter("arch", data.arch);
List<PkgData> savedFiles = fquery.getResultList();
if (savedFiles.size() > 0)
{
return;
}
nquery.setParameter("name", data.name.id);
List<PkgName> savedName = nquery.getResultList();
if (savedName.size() > 0)
{
data.name = savedName.get(0);
}
em.persist(data);
log.log(Level.INFO, String.format("Persisting package %s", file.getName()));
}
}

View File

@@ -0,0 +1,206 @@
package service;
import model.PkgData;
import model.PkgList;
import pacbase.Parser;
import persistence.QueryEngine;
import javax.ejb.Singleton;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URI;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@Path("/pkg")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Singleton
public class PacmanWebService
{
@PersistenceContext(unitName = "jpacrepo_pu")
private EntityManager em;
@Context
private UriInfo uriInfo;
private Logger log = Logger.getLogger(PacmanWebService.class.getName());
private String nameQuery = "SELECT pname FROM PkgName pname WHERE id = :name";
private String fileQuery = "SELECT pdata FROM PkgData pdata";
private String fileNameQuery = "SELECT pdata FROM PkgData pdata WHERE fileName = :fileName";
private String hashQuery = "SELECT pdata FROM PkgData pdata WHERE md5sum = :md5sum";
private String idQuery = "SELECT pdata FROM PkgData pdata WHERE id = :id";
@Inject
private ApplicationContext ctx;
/**
* JSON : curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-service-1.0/rs/book/1 -v
* XML : curl -X GET -H "Accept: application/xml" http://localhost:8080/chapter15-service-1.0/rs/book/1 -v
*/
@GET
@Path("search")
public Response getPackage(@QueryParam("name") String name,
@QueryParam("version") String version,
@QueryParam("arch") String arch,
@QueryParam("filename") String filename,
@QueryParam("md5sum") String md5sum)
{
if(md5sum != null)
{
return getPackageByHash(md5sum);
}
else if(filename != null)
{
return getPackageByFileName(filename);
}
else if( name != null || arch != null || version != null)
{
QueryEngine qe = new QueryEngine(PkgData.class);
qe.select();
TypedQuery<PkgData> fquery = em.createQuery(fileQuery, PkgData.class);
if (name != null) qe.where("name", "=", name);
if (version != null) qe.where("version", "=", version);
if (arch != null) qe.where("arch", "=", arch);
String query = qe.build();
// log.log(Level.INFO, query);
return manageQueryResult(em.createQuery(query).getResultList());
}
else
{
throw new NotFoundException();
}
}
private Response getPackageByHash(String md5sum)
{
TypedQuery<PkgData> hquery = em.createQuery(hashQuery, PkgData.class);
if(md5sum != null) hquery.setParameter("md5sum", md5sum);
return manageQueryResult(hquery.getResultList());
}
private Response getPackageByFileName(String file)
{
TypedQuery<PkgData> fnquery = em.createQuery(fileNameQuery, PkgData.class);
fnquery.setParameter("fileName", file);
return manageQueryResult(fnquery.getResultList());
}
@POST
@Path("/pkg/upload")
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response createPackage(byte[] input, @MatrixParam("filename") String filename) throws Exception
{
if (filename == null)
throw new BadRequestException();
String hash = Parser.computeMD5(new ByteArrayInputStream(input));
TypedQuery<PkgData> hquery = em.createQuery(hashQuery, PkgData.class);
hquery.setParameter("md5sum", hash);
List<PkgData> savedFiles = hquery.getResultList();
if (savedFiles.size() > 0)
{
return null;
}
else
{
File file = new File(ctx.getSystemProperties().getProperty("RepoFolder"), filename);
FileOutputStream fos = new FileOutputStream(file);
fos.write(input);
fos.close();
PkgData pkg = Parser.parseFile(file);
em.persist(pkg);
URI pkgUri = uriInfo.getAbsolutePathBuilder().path(pkg.filePath).build();
return Response.created(pkgUri).build();
}
}
private Response manageQueryResult(List<PkgData> list)
{
// log.log(Level.INFO, "size: " + list.size());
PkgList pkgList = new PkgList(list);
if (pkgList.size() == 0)
{
throw new NotFoundException();
}
else if(pkgList.size()==1)
{
return Response.ok(pkgList.get(0)).build();
}
else
{
return Response.ok(pkgList).build();
}
}
}
// ======================================
// = Public Methods =
// ======================================
/**
* curl -X POST --data-binary "<book><description>Science fiction comedy book</description><illustrations>false</illustrations><isbn>1-84023-742-2</isbn><nbOfPage>354</nbOfPage><price>12.5</price><title>The Hitchhiker's Guide to the Galaxy</title></book>" -H "Content-Type: application/xml" http://localhost:8080/chapter15-service-1.0/rs/book -v
* curl -X POST --data-binary "{\"description\":\"Science fiction comedy book\",\"illustrations\":false,\"isbn\":\"1-84023-742-2\",\"nbOfPage\":354,\"price\":12.5,\"title\":\"The Hitchhiker's Guide to the Galaxy\"}" -H "Content-Type: application/json" http://localhost:8080/chapter15-service-1.0/rs/book -v
*/
// @POST
// public Response createBook(Book book) {
// if (book == null)
// throw new BadRequestException();
//
// em.persist(book);
// URI bookUri = uriInfo.getAbsolutePathBuilder().path(book.getId()).build();
// return Response.created(bookUri).build();
// }
// @PUT
// public Response updateBook(Book book) {
// if (book == null)
// throw new BadRequestException();
//
// em.merge(book);
// return Response.ok().build();
// }
/**
* curl -X DELETE http://localhost:8080/chapter15-service-1.0/rs/book/1 -v
*/
// @DELETE
// @Path("{id}")
// public Response deleteBook(@PathParam("id") String id) {
// Book book = em.find(Book.class, id);
//
// if (book == null)
// throw new NotFoundException();
//
// em.remove(book);
//
// return Response.noContent().build();
// }
/**
* JSON : curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-service-1.0/rs/book -v
* XML : curl -X GET -H "Accept: application/xml" http://localhost:8080/chapter15-service-1.0/rs/book -v
*/
// @GET
// public Response getAllBooks() {
// TypedQuery<Book> query = em.createNamedQuery(Book.FIND_ALL, Book.class);
// Books books = new Books(query.getResultList());
// return Response.ok(books).build();
// }

View File

@@ -8,8 +8,8 @@
<jta-data-source>java:/ejb/postgres/wildfly</jta-data-source>
<properties>
<!--<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>-->
<!--<property name="javax.persistence.schema-generation.database.action" value="none"/>-->
<property name="javax.persistence.schema-generation.database.action" value="create"/>
<property name="javax.persistence.schema-generation.database.action" value="none"/>
<!--<property name="javax.persistence.schema-generation.database.action" value="create"/>-->
<property name="eclipselink.logging.level" value="INFO"/>
<property name="hibernate.default_schema" value="jpacrepo"/>
</properties>