diff --git a/build.gradle b/build.gradle index 4050554..6b8c9e4 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,11 @@ dependencies { compile 'javax:javaee-api:7.0' compile 'commons-io:commons-io:2.4' compile 'commons-codec:commons-codec:1.10' + compile 'eu.webtoolkit:jwt:3.3.4' + compile 'org.projectlombok:lombok:1.16.4' + compile 'commons-fileupload:commons-fileupload:1.3.1' + + testCompile 'com.thoughtworks.xstream:xstream:1.4.8' testCompile 'org.jboss.resteasy:resteasy-jaxrs:3.0.11.Final' testCompile 'org.jboss.resteasy:resteasy-client:3.0.11.Final' @@ -37,7 +42,17 @@ dependencies { } task deployWildfly(dependsOn: 'war') << { - '/opt/wildfly/bin/jboss-cli.sh --connect --user=admin --password=qwerty --command="deploy build/libs/jpacrepo-1.0.war --force"'.execute().waitFor() + def username = 'admin', password = '123456' + def sout = new StringBuffer(), serr = new StringBuffer() + def cmd = sprintf('/opt/wildfly/bin/jboss-cli.sh --connect --user=%s --password=%s --command="deploy %s --force"', username, password, tasks['war'].archivePath) + def proc = cmd.execute() + proc.consumeProcessOutput(sout, serr) + proc.waitFor() + if(proc.exitValue() != 0) + { + println "$serr" + throw new RuntimeException("Error occurred during deployment") + } } // client.jar diff --git a/jpacrepo.iml b/jpacrepo.iml index ceb1dc1..fdc2763 100644 --- a/jpacrepo.iml +++ b/jpacrepo.iml @@ -55,5 +55,7 @@ + + \ No newline at end of file diff --git a/src/main/java/org/jpacrepo/context/ApplicationContext.java b/src/main/java/org/jpacrepo/context/ApplicationContext.java index 3451bd0..1733ba9 100644 --- a/src/main/java/org/jpacrepo/context/ApplicationContext.java +++ b/src/main/java/org/jpacrepo/context/ApplicationContext.java @@ -1,8 +1,12 @@ package org.jpacrepo.context; +import lombok.Getter; +import lombok.Setter; import org.jpacrepo.model.PkgData; +import org.jpacrepo.service.PacmanService; import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; import javax.inject.Qualifier; import java.io.File; import java.io.FileInputStream; @@ -28,6 +32,9 @@ public class ApplicationContext private String repoFolder; + @Getter @Setter + private PacmanService pacmanService; + public ApplicationContext(String propertyFile) { systemProperties = new Properties(); diff --git a/src/main/java/org/jpacrepo/context/ContextProducer.java b/src/main/java/org/jpacrepo/context/ContextProducer.java index ed03eb8..f59c5f1 100644 --- a/src/main/java/org/jpacrepo/context/ContextProducer.java +++ b/src/main/java/org/jpacrepo/context/ContextProducer.java @@ -1,5 +1,8 @@ package org.jpacrepo.context; +import org.jpacrepo.service.PacmanService; + +import javax.ejb.EJB; import javax.enterprise.inject.Produces; /** @@ -9,10 +12,15 @@ import javax.enterprise.inject.Produces; public class ContextProducer { + @EJB + PacmanService service; + @Produces @DefaultConfiguration public ApplicationContext produce() { - return new ApplicationContext("/etc/jpacrepo/server.properties"); + ApplicationContext ctx = new ApplicationContext("/etc/jpacrepo/server.properties"); + ctx.setPacmanService(service); + return ctx; } } diff --git a/src/main/java/org/jpacrepo/frontend/component/JPacRepoApp.java b/src/main/java/org/jpacrepo/frontend/component/JPacRepoApp.java new file mode 100644 index 0000000..70e8967 --- /dev/null +++ b/src/main/java/org/jpacrepo/frontend/component/JPacRepoApp.java @@ -0,0 +1,218 @@ +package org.jpacrepo.frontend.component; + +/** + * Created by walter on 06/06/15. + */ + + +import eu.webtoolkit.jwt.*; +import org.jpacrepo.context.ApplicationContext; +import org.jpacrepo.model.PkgName; +import org.jpacrepo.util.Utility; + +import java.util.*; + +/** + * Created by walter on 03/01/15. + */ +public class JPacRepoApp extends WApplication +{ + private ApplicationContext ctx; + private PackageTable table; + private WPushButton searchButton; + private WMenu pageMenu; + private WMenu pagerMenu; + private WComboBox pageCombo; + + private String lastName; + + private WSuggestionPopup nameSuggestion, versionSuggestion, archSuggestion; + + private int pageSize = 10; + + private WLineEdit packageNameEdit, packageVersionEdit, packageArchEdit; + + public JPacRepoApp(WEnvironment env, ApplicationContext ctx) + { + super(env); + this.ctx = ctx; + init(); + } + + private void init() + { + WBootstrapTheme theme = new WBootstrapTheme(); + theme.setVersion(WBootstrapTheme.Version.Version3); + setTheme(theme); + useStyleSheet(new WLink(WApplication.getRelativeResourcesUrl() + "/themes/bootstrap/3/bootstrap-theme.min.css")); + useStyleSheet(new WLink("css/jpacrepo-web.css")); +// useStyleSheet(new WLink("css/everywidget.css")); + createView(); + } + + public void createView() + { + WTemplate jumbotron = new WTemplate(Utility.getTemplate("jumbotron.html")); + packageArchEdit = new WLineEdit(); + packageArchEdit.setWidth(new WLength("100%")); + packageNameEdit = new WLineEdit(); + packageNameEdit.setWidth(new WLength("100%")); + packageVersionEdit = new WLineEdit(); + packageVersionEdit.setWidth(new WLength("100%")); + searchButton = new WPushButton("Search"); + pageCombo = new WComboBox(); + for (int i = 1; i <= 10; i++) + { + pageCombo.addItem(Integer.toString(i * 10)); + } + + + searchButton.clicked().addListener(this, () -> + { + pageSize = Integer.parseInt(pageCombo.getCurrentText().toString()); + while (pageMenu.getCount() > 0) + { + pageMenu.removeItem(pageMenu.itemAt(0)); + } + while (pagerMenu.getCount() > 0) + { + pagerMenu.removeItem(pagerMenu.itemAt(0)); + } + + long resultNumber = ctx.getPacmanService().countResults(packageNameEdit.getDisplayText(), packageVersionEdit.getDisplayText(), packageArchEdit.getDisplayText()); + if (resultNumber < pageSize) + { + table.showPackages(ctx.getPacmanService().searchPackage(packageNameEdit.getDisplayText(), packageVersionEdit.getDisplayText(), packageArchEdit.getDisplayText(), -1, -1)); + } + else + { + int remaining = (int) resultNumber; + int i = 1; + while (remaining > 0) + { + final int logicalPageNumber = i - 1; + WMenuItem menuItem = new WMenuItem(Integer.toString(i++)); + pageMenu.addItem(menuItem); + menuItem.clicked().addListener(this, () -> + { + table.showPackages(ctx.getPacmanService().searchPackage(packageNameEdit.getText(), packageVersionEdit.getText(), packageArchEdit.getText(), logicalPageNumber, pageSize)); + }); + remaining -= pageSize; + } + pageMenu.select(0); + + WMenuItem previousItem = new WMenuItem("Previous"), nextItem = new WMenuItem("Next"); + previousItem.setStyleClass("previous"); + nextItem.setStyleClass("next"); + + previousItem.clicked().addListener(this, () -> + { + int newIndex = pageMenu.getCurrentIndex() - 1; + if(newIndex>=0) + { + pageMenu.select(newIndex); + pageMenu.itemAt(newIndex).clicked().trigger(new WMouseEvent()); + } + }); + + nextItem.clicked().addListener(this, () -> + { + int newIndex = pageMenu.getCurrentIndex() + 1; + if(newIndex nameList = ctx.getPacmanService().listProperty("name", new HashMap<>(), PkgName.class); + List nameTxtList = new ArrayList<>(); + nameList.forEach((p) -> nameTxtList.add(p.id)); + nameSuggestion = getSuggestionPopup(nameTxtList, res, packageNameEdit); + nameSuggestion.setFilterLength(3); + nameSuggestion.setMaximumSize(WLength.Auto, new WLength("70%")); + + versionSuggestion = getSuggestionPopup(new ArrayList<>(), res, packageVersionEdit); + versionSuggestion.setMaximumSize(WLength.Auto, new WLength("70%")); + + packageNameEdit.blurred().addListener(this, () -> + { + if(!packageNameEdit.getDisplayText().equals(lastName)) + { + lastName = packageNameEdit.getDisplayText(); + Map predicateMap = new HashMap<>(); + predicateMap.put("name", packageNameEdit.getDisplayText()); + List versions = ctx.getPacmanService().listProperty("version", predicateMap, String.class); + + versionSuggestion.getModel().removeRows(0, versionSuggestion.getModel().getRowCount()); + versions.forEach((e) -> versionSuggestion.addSuggestion(e)); + packageVersionEdit.setText(""); + packageArchEdit.setText(""); + } + }); + + List archs = ctx.getPacmanService().listProperty("arch", new HashMap<>(), String.class); + archSuggestion = getSuggestionPopup(archs, res, packageArchEdit); + + res.addWidget(jumbotron); + res.addWidget(inputTemplate); + WContainerWidget containerWidget = new WContainerWidget(); + containerWidget.addWidget(table); + containerWidget.setStyleClass("row"); + res.addWidget(containerWidget); + res.setStyleClass("container main-container"); + + pageMenu = new WMenu(); + pageMenu.setStyleClass("pagination"); + res.addWidget(pageMenu); + + pagerMenu = new WMenu(); + pageCombo.setWidth(new WLength("100%")); + pagerMenu.setStyleClass("pager"); + res.addWidget(pagerMenu); + + getRoot().addWidget(res); + } + + WSuggestionPopup getSuggestionPopup(List suggestions, WWidget parent, WFormWidget linkedFormWidget) + { + WSuggestionPopup.Options contactOptions = new WSuggestionPopup.Options(); + contactOptions.highlightBeginTag = ""; + contactOptions.highlightEndTag = ""; + contactOptions.listSeparator = ','; + contactOptions.whitespace = " \\n"; + contactOptions.wordSeparators = "-., \"@\\n;"; + //contactOptions.appendReplacedText = ", "; + WSuggestionPopup popup = new WSuggestionPopup(WSuggestionPopup + .generateMatcherJS(contactOptions), WSuggestionPopup + .generateReplacerJS(contactOptions), parent); + popup.forEdit(linkedFormWidget, EnumSet.of(WSuggestionPopup.PopupTrigger.Editing, WSuggestionPopup.PopupTrigger.DropDownIcon)); + List wstrings = new ArrayList<>(); + for(String suggestion :suggestions) + { + wstrings.add(new WString(suggestion)); +// popup.addSuggestion(suggestion); + } + popup.setModel(new WStringListModel(wstrings)); + return popup; + } + + +} diff --git a/src/main/java/org/jpacrepo/frontend/component/PackageTable.java b/src/main/java/org/jpacrepo/frontend/component/PackageTable.java new file mode 100644 index 0000000..ea9fc6f --- /dev/null +++ b/src/main/java/org/jpacrepo/frontend/component/PackageTable.java @@ -0,0 +1,76 @@ +package org.jpacrepo.frontend.component; + +import eu.webtoolkit.jwt.*; +import org.jpacrepo.model.PkgData; + +import java.text.DecimalFormat; +import java.util.List; + +/** + * Created by walter on 06/06/15. + */ +public class PackageTable extends WTable +{ + public PackageTable() + { + toggleStyleClass("table-bordered", true); + toggleStyleClass("table-condensed", true); + toggleStyleClass("table-striped", true); + + } + + public void showPackages(List pkgs) + { + clear(); + setHeaderCount(1); + setWidth(new WLength("100%")); + + getElementAt(0, 0).addWidget(new WText("Name")); + getElementAt(0, 1).addWidget(new WText("Version")); + getElementAt(0, 2).addWidget(new WText("Arch")); + getElementAt(0, 3).addWidget(new WText("Description")); + getElementAt(0, 4).addWidget(new WText("Size")); + + + for(int row =1 ; row fileName; + public static volatile ListAttribute backup; + public static volatile ListAttribute makeopkgopt; + public static volatile ListAttribute depend; + public static volatile ListAttribute replaces; + public static volatile SingularAttribute updTimestamp; + public static volatile SingularAttribute description; + public static volatile SingularAttribute buildDate; + public static volatile SingularAttribute version; + public static volatile SingularAttribute packager; + public static volatile SingularAttribute url; + public static volatile SingularAttribute license; + public static volatile SingularAttribute size; + public static volatile ListAttribute makedepend; + public static volatile ListAttribute optdepend; + public static volatile SingularAttribute md5sum; + public static volatile ListAttribute provides; + public static volatile SingularAttribute name; + public static volatile SingularAttribute id; + public static volatile SingularAttribute arch; + public static volatile SingularAttribute base; + public static volatile ListAttribute conflict; + +} + diff --git a/src/main/java/org/jpacrepo/model/PkgName_.java b/src/main/java/org/jpacrepo/model/PkgName_.java new file mode 100644 index 0000000..393d74d --- /dev/null +++ b/src/main/java/org/jpacrepo/model/PkgName_.java @@ -0,0 +1,14 @@ +package org.jpacrepo.model; + +import javax.annotation.Generated; +import javax.persistence.metamodel.SingularAttribute; +import javax.persistence.metamodel.StaticMetamodel; + +@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor") +@StaticMetamodel(PkgName.class) +public abstract class PkgName_ { + + public static volatile SingularAttribute id; + +} + diff --git a/src/main/java/org/jpacrepo/service/PacmanService.java b/src/main/java/org/jpacrepo/service/PacmanService.java index ac589ed..e82098d 100644 --- a/src/main/java/org/jpacrepo/service/PacmanService.java +++ b/src/main/java/org/jpacrepo/service/PacmanService.java @@ -1,14 +1,24 @@ package org.jpacrepo.service; -import javax.ejb.Remote; +import org.jpacrepo.model.PkgData; + +import javax.ejb.Local; +import java.util.List; +import java.util.Map; /** * Created by walter on 28/03/15. */ -@Remote +@Local public interface PacmanService { public void deletePackage(String filename) throws Exception; + public long countResults(String name, String version, String arch); + + public List searchPackage(String name, String version, String arch, int page, int pageSize); + + public List listProperty(String property, Map equalityConditions, Class cls); + } diff --git a/src/main/java/org/jpacrepo/service/PacmanServiceEJB.java b/src/main/java/org/jpacrepo/service/PacmanServiceEJB.java index fc77e3b..ba1d3ae 100644 --- a/src/main/java/org/jpacrepo/service/PacmanServiceEJB.java +++ b/src/main/java/org/jpacrepo/service/PacmanServiceEJB.java @@ -17,6 +17,10 @@ import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import javax.transaction.*; import java.io.File; import java.io.IOException; @@ -238,6 +242,135 @@ public class PacmanServiceEJB implements PacmanService throw new RuntimeException(e); } }); + } + + @Override + public long countResults(String name, String version, String arch) + { + CriteriaBuilder builder; + CriteriaQuery criteriaQuery; + Root entity; + + builder = em.getCriteriaBuilder(); + criteriaQuery = builder.createQuery(Long.class); + entity = criteriaQuery.from(PkgData.class); + Predicate finalPredicate=null, p; + + if(name != null && !name.isEmpty()) + { + p = builder.equal(entity.get("name").get("id"), name); + finalPredicate = p; + } + if(version != null && !version.isEmpty()) + { + p=builder.equal(entity.get("version"), version); + finalPredicate = finalPredicate != null ? builder.and(finalPredicate, p) : p; + } + if(arch != null && !arch.isEmpty()) + { + p=builder.equal(entity.get("arch"), arch); + finalPredicate = finalPredicate != null ? builder.and(finalPredicate, p) : p; + } + + if(finalPredicate != null) + { + criteriaQuery.select(builder.count(entity)).where(finalPredicate); + } + else + { + criteriaQuery.select(builder.count(entity)); + } + TypedQuery query = em.createQuery(criteriaQuery); + return em.createQuery(criteriaQuery).getSingleResult(); + } + + @Override + public List listProperty(String property, Map equalityConditions, Class cls) + { + CriteriaBuilder builder = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = builder.createQuery(cls); + Root entity = criteriaQuery.from(PkgData.class); + + Predicate finalPredicate=null, p; + String key = equalityConditions.get("name"); + if(key != null && !key.isEmpty()) + { + p = builder.equal(entity.get("name").get("id"), key); + finalPredicate = p; + } + + key = equalityConditions.get("version"); + if(key != null && !key.isEmpty()) + { + p = builder.equal(entity.get("version"), key); + finalPredicate = p; + } + + key = equalityConditions.get("arch"); + if(key != null && !key.isEmpty()) + { + p = builder.equal(entity.get("arch"), key); + finalPredicate = p; + } + + if(finalPredicate != null) + { + criteriaQuery.select(entity.get(property)).distinct(true).where(finalPredicate); + } + else + { + criteriaQuery.select(entity.get(property)).distinct(true); + } + return em.createQuery(criteriaQuery).getResultList(); + } + + + @Override + public List searchPackage(String name, String version, String arch, int pageNumber, int pageSize) + { + CriteriaBuilder builder; + CriteriaQuery criteriaQuery; + Root entity; + + builder = em.getCriteriaBuilder(); + criteriaQuery = builder.createQuery(PkgData.class); + entity = criteriaQuery.from(PkgData.class); + Predicate finalPredicate=null, p; + + if(name != null && !name.isEmpty()) + { + p = builder.equal(entity.get("name").get("id"), name); + finalPredicate = p; + } + if(version != null && !version.isEmpty()) + { + p=builder.equal(entity.get("version"), version); + finalPredicate = finalPredicate != null ? builder.and(finalPredicate, p) : p; + } + if(arch != null && !arch.isEmpty()) + { + p=builder.equal(entity.get("arch"), arch); + finalPredicate = finalPredicate != null ? builder.and(finalPredicate, p) : p; + } + + if(finalPredicate != null) + { + criteriaQuery.select(entity).where(finalPredicate).orderBy(builder.asc(entity.get("fileName"))); + } + else + { + criteriaQuery.select(entity).orderBy(builder.asc(entity.get("fileName"))); + } + TypedQuery query = em.createQuery(criteriaQuery); + if(pageNumber>=0) + { + query.setFirstResult(pageNumber*pageSize); + } + if(pageSize>0) + { + query.setMaxResults(pageSize); + } + return query.getResultList(); } } \ No newline at end of file diff --git a/src/main/java/org/jpacrepo/util/Utility.java b/src/main/java/org/jpacrepo/util/Utility.java new file mode 100644 index 0000000..7985edf --- /dev/null +++ b/src/main/java/org/jpacrepo/util/Utility.java @@ -0,0 +1,35 @@ +package org.jpacrepo.util; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by walter on 06/06/15. + */ +public class Utility +{ + + public static String getTemplate(String filename) + { + return new String(readResource("/template/" + filename)); + } + + public static byte[] readResource(String resourcePath) + { + try + { + InputStream is = Utility.class.getClassLoader().getResourceAsStream(resourcePath); + byte[] res = new byte[is.available()]; + if(is.read(res,0, res.length)!= res.length) + { + throw new IOException("Uncompleted read"); + } + is.close(); + return res; + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/resources/WEB-INF/web.xml b/src/main/resources/WEB-INF/web.xml new file mode 100644 index 0000000..e39c8f0 --- /dev/null +++ b/src/main/resources/WEB-INF/web.xml @@ -0,0 +1,17 @@ + + + + eu.webtoolkit.jwt.ServletInit + + + jpacrepo + jpacrepo + + + + + + diff --git a/src/main/resources/template/jumbotron.html b/src/main/resources/template/jumbotron.html new file mode 100644 index 0000000..1e40846 --- /dev/null +++ b/src/main/resources/template/jumbotron.html @@ -0,0 +1,4 @@ +
+

JPacRepo

+

The personal archlinux package repository

+
diff --git a/src/main/resources/template/package-search-form.html b/src/main/resources/template/package-search-form.html new file mode 100644 index 0000000..4f527a1 --- /dev/null +++ b/src/main/resources/template/package-search-form.html @@ -0,0 +1,46 @@ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+
+ ${nameInput} +
+
+ ${versionInput} +
+
+ ${archInput} +
+
+ ${pageInput} +
+
+ ${searchButton} +
+
+
+
diff --git a/src/main/webapp/css/jpacrepo-web.css b/src/main/webapp/css/jpacrepo-web.css new file mode 100644 index 0000000..36cb492 --- /dev/null +++ b/src/main/webapp/css/jpacrepo-web.css @@ -0,0 +1,28 @@ +div.input-control { + margin-bottom: 2%; + background-color: #ccc; + padding: 1% 2%; + border-radius: 4px; +} + +html body.Wt-ltr div.Wt-domRoot div div.container div.row table.table-bordered.table-condensed.table-striped tbody tr td { + text-align: center; +} +html body.Wt-ltr div.Wt-domRoot div div.container div.row table.table-bordered.table-condensed.table-striped thead tr th { + text-align: center; +} + +div.search-input form.form-inline div.form-group +{ + text-align: center; +} + +div.container.main-container +{ + text-align: center; +} + +td a img.download-icon +{ + margin-right: 5px; +} \ No newline at end of file diff --git a/src/main/webapp/img/download.png b/src/main/webapp/img/download.png new file mode 100644 index 0000000..9a2d9fe Binary files /dev/null and b/src/main/webapp/img/download.png differ diff --git a/src/main/webapp/img/download.svg b/src/main/webapp/img/download.svg new file mode 100644 index 0000000..377ae33 --- /dev/null +++ b/src/main/webapp/img/download.svg @@ -0,0 +1,15 @@ + + + + + + + + +