Java tutorial
/***************************************************************************** * Copyright (c) 2016 Diamond Light Source Ltd. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Torkild U. Resheim - initial API and implementation ****************************************************************************/ package org.dawnsci.marketplace.services; import java.io.InputStream; import java.net.URL; import java.net.UnknownHostException; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.inject.Inject; import javax.naming.NamingException; import org.dawnsci.marketplace.Catalog; import org.dawnsci.marketplace.Catalogs; import org.dawnsci.marketplace.Featured; import org.dawnsci.marketplace.ForbiddenException; import org.dawnsci.marketplace.InternalErrorException; import org.dawnsci.marketplace.Marketplace; import org.dawnsci.marketplace.MarketplaceFactory; import org.dawnsci.marketplace.MarketplacePackage; import org.dawnsci.marketplace.Node; import org.dawnsci.marketplace.Recent; import org.dawnsci.marketplace.Search; import org.dawnsci.marketplace.SearchTab; import org.dawnsci.marketplace.Wizard; import org.dawnsci.marketplace.social.account.Account; import org.dawnsci.marketplace.social.account.AccountRepository; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.hibernate.Query; import org.hibernate.SessionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class MarketplaceDAO { private static final Logger logger = LoggerFactory.getLogger(MarketplaceDAO.class); @Inject private AccountRepository accountRepository; @Inject private SessionFactory sessionFactory; @Inject private Environment environment; @Inject private FileService fileService; @Autowired(required = false) @Value("${marketplace.featured.solutions:1}") List<Long> featuredItems; @Autowired(required = false) @Value("${marketplace.featured.maximum:3}") private int maxFeaturedItems; @Autowired(required = false) @Value("${marketplace.max-recent-solutions:12}") private int maxRecentItems; @Autowired(required = false) @Value("${marketplace.base-url:http://localhost:8080}") private String marketplaceBaseUrl; private boolean catalogsUnavailable; /** * HQL query to use when searching for a solution using a simple substring * search. */ private static final String HQL_SEARCH = "select node from Node node where " + "lower(node.name) like :term or " + "lower(node.owner) like :term or " + "lower(str(node.shortdescription)) like :term or " + "lower(str(node.body)) like :term " + "order by node.changed desc"; /** * Creates and returns the catalog representing this particular marketplace * instance. * * @return this marketplace's catalog */ private Catalog getCatalog() { Catalog catalog = MarketplaceFactory.eINSTANCE.createCatalog(); catalog.setTitle(environment.getProperty("marketplace.title")); catalog.setUrl(marketplaceBaseUrl + "/mpc"); catalog.setIcon(environment.getProperty("marketplace.catalog-icon")); catalog.setDescription(environment.getProperty("marketplace.description")); Wizard wizard = MarketplaceFactory.eINSTANCE.createWizard(); wizard.setTitle(environment.getProperty("marketplace.wizard-title")); wizard.setIcon(environment.getProperty("marketplace.wizard-icon")); SearchTab st = MarketplaceFactory.eINSTANCE.createSearchTab(); FeatureMapUtil.addText(st.getMixed(), "Search"); st.setEnabled(1); wizard.setSearchtab(st); catalog.setWizard(wizard); return catalog; } @Transactional public Marketplace getContent(Long identifier) { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession() .createQuery("SELECT node FROM Node node WHERE node.id='" + identifier + "'"); query.setMaxResults(1); if (!query.list().isEmpty()) { marketplace.setNode((EcoreUtil.copy((Node) query.list().get(0)))); } sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return marketplace; } private Marketplace loadCatalogs(URL url) { ResourceSet rs = new ResourceSetImpl(); rs.getPackageRegistry().put(null, MarketplacePackage.eINSTANCE); try { Resource resource = rs.createResource(URI.createURI(url.toExternalForm())); InputStream is = url.openStream(); resource.load(is, rs.getLoadOptions()); return (Marketplace) resource.getContents().get(0); } catch (UnknownHostException e) { // marketplace is unavailable flagging it as such catalogsUnavailable = true; logger.warn("Marketplace at " + url + " is unavailable"); } catch (Exception e) { e.printStackTrace(); } return null; } @SuppressWarnings("unchecked") @Transactional public Marketplace getCatalogs() { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); Catalogs catalogs = MarketplaceFactory.eINSTANCE.createCatalogs(); marketplace.setCatalogs(catalogs); try { sessionFactory.getCurrentSession().beginTransaction(); catalogs.getItems().add(getCatalog()); Query query = sessionFactory.getCurrentSession().createQuery("SELECT catalog FROM Catalog catalog"); catalogs.getItems().addAll(EcoreUtil.copyAll(query.list())); sessionFactory.getCurrentSession().getTransaction().commit(); // also obtain catalogs from the Eclipse Marketplace if (!catalogsUnavailable) { Marketplace em = loadCatalogs(new URL("http://marketplace.eclipse.org/catalogs/api/p")); if (em != null) { catalogs.getItems().addAll(EcoreUtil.copyAll(em.getCatalogs().getItems())); } } } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return marketplace; } @SuppressWarnings("unchecked") @Transactional public Marketplace getMarkets() { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession().createQuery("SELECT market FROM Market market"); marketplace.getMarkets().addAll(EcoreUtil.copyAll(query.list())); sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return marketplace; } /** * Returns an empty news section for now. */ @Transactional public Marketplace getNews() { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); return marketplace; } @SuppressWarnings("unchecked") @Transactional public Marketplace getFeatured() { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); Featured featured = MarketplaceFactory.eINSTANCE.createFeatured(); marketplace.setFeatured(featured); try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession() .createQuery("SELECT node FROM Node node WHERE node.id IN (:f)"); query.setParameterList("f", featuredItems); query.setMaxResults(maxFeaturedItems); featured.getNodes().addAll(EcoreUtil.copyAll(query.list())); featured.setCount(featured.getNodes().size()); sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return marketplace; } @SuppressWarnings("unchecked") @Transactional public Marketplace getRecent() { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); Recent recent = MarketplaceFactory.eINSTANCE.createRecent(); marketplace.setRecent(recent); try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession() .createQuery("SELECT node FROM Node node ORDER BY node.changed DESC"); query.setMaxResults(maxRecentItems); recent.getNodes().addAll(EcoreUtil.copyAll(query.list())); recent.setCount(recent.getNodes().size()); sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return marketplace; } @SuppressWarnings("unchecked") @Transactional public Marketplace getSearchResult(String term) { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); Search search = MarketplaceFactory.eINSTANCE.createSearch(); term = term.toLowerCase(); search.setTerm(term); search.setUrl(marketplaceBaseUrl + "/search?term=" + term); marketplace.setSearch(search); try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession().createQuery(HQL_SEARCH).setString("term", "%" + term + "%"); List<Node> list = query.list(); search.getNodes().addAll(EcoreUtil.copyAll(list)); search.setCount(search.getNodes().size()); sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return marketplace; } @SuppressWarnings("unchecked") @Transactional public List<String> getTags() { List<String> tags = Collections.EMPTY_LIST; try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession().createQuery("select distinct name from Tag"); query.setMaxResults(50); tags = query.list(); sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return tags; } @SuppressWarnings("unchecked") @Transactional public Marketplace getSolutionsWithTag(String tag) { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); Search search = MarketplaceFactory.eINSTANCE.createSearch(); search.setTerm(tag); search.setUrl(marketplaceBaseUrl + "/search?tag=" + tag); marketplace.setSearch(search); try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession().createQuery( "SELECT node " + "FROM Node node " + "JOIN node.tags.items nti " + "WHERE nti.name=:term") .setString("term", tag); search.getNodes().addAll(EcoreUtil.copyAll(query.list())); sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return marketplace; } /** * Returns a <b>detached</b> copy of the solution with the specified * identifier. * * @param identifier * the solution identifier * @return a detached {@link Node} instance */ @Transactional public Node getSolution(Long identifier) { Node node = null; try { sessionFactory.getCurrentSession().beginTransaction(); Query query = sessionFactory.getCurrentSession() .createQuery("SELECT node FROM Node node WHERE node.id='" + identifier + "'"); if (!query.list().isEmpty()) { node = EcoreUtil.copy((Node) query.list().get(0)); } sessionFactory.getCurrentSession().getTransaction().commit(); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } return node; } /** * Deletes the specified solution from the marketplace or throws a * {@link ForbiddenException} if the solution does not belong to the * specified account. * * @param account * the account that executes the operation * @param id * identifier of the solution */ @Transactional public void deleteSolution(Account account, Long id) { try { sessionFactory.getCurrentSession().beginTransaction(); if (!canEdit(id)) { throw new ForbiddenException(); } Object object = sessionFactory.getCurrentSession().get("Node", id); sessionFactory.getCurrentSession().delete(object); sessionFactory.getCurrentSession().getTransaction().commit(); fileService.deleteSolution(id); } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw new InternalErrorException(e); } } /** * <p> * If clones are serialized/deserialized as different object instances then * we need to have an explicit version attribute in the model itself (which * stays with the object). Since we don't want to keep multiple version, the * <i>created</i> field on <code>node</code> is annotated with "Version". Using * the <i>changed</i> field opens a can of worms. * </p> * * @param solution the solution as XML * @param account the user identifier * @return * @throws NamingException * @see https://docs.jboss.org/hibernate/core/3.3/reference/en/html/objectstate.html#objectstate-detached */ @Transactional public Object saveOrUpdateSolution(Node s, Account account) { try { sessionFactory.getCurrentSession().beginTransaction(); boolean isNewSolution = s.getId() == null ? true : false; // if the solution has an assigned identifier, but does not exist // we'll clear the identifier and create a fresh instance. The new // identifier will be returned so that the client can update it's // copy. Also make sure we have the same "created" value as this // is used for identity. if (!isNewSolution) { Object object = sessionFactory.getCurrentSession().get("Node", s.getId()); if (object == null) { s.setId(null); isNewSolution = true; logger.info("Reset identifier for solution " + s.getName()); } else { // make sure we have the same value as the database s.setCreated(((Node) object).getCreated()); sessionFactory.getCurrentSession().evict(object); } } // verify that we have the correct owner if (!isNewSolution) { if (!canEdit(s.getId())) { throw new ForbiddenException(); } logger.info("Updating solution #" + s.getId()); } s.setChanged(System.currentTimeMillis()); // set a few values for new entities if (isNewSolution) { logger.info("Creating a new solution instance in database"); s.setCreated(System.currentTimeMillis()); s.setOwner(account.getFirstName() + " " + account.getLastName()); } // do the update in the databse sessionFactory.getCurrentSession().saveOrUpdate(s); sessionFactory.getCurrentSession().getTransaction().commit(); if (isNewSolution) { // associate the new solution with the user account accountRepository.createAccountSolutionMapping(account, s.getId()); } logger.info("Saved solution #" + s.getId() + " - " + s.getName()); return s; } catch (Exception e) { sessionFactory.getCurrentSession().getTransaction().rollback(); e.printStackTrace(); throw new InternalErrorException(e); } } /** * Creates a new node or solution, ready for editing. * * @return the new node */ public Marketplace getNewNode() { Marketplace marketplace = MarketplaceFactory.eINSTANCE.createMarketplace(); marketplace.setBaseUrl(marketplaceBaseUrl); Node node = MarketplaceFactory.eINSTANCE.createNode(); marketplace.setNode(node); return marketplace; } /** * Tests whether or not the current user have access to edit the solution * with the given identifier. The user must be an administrator or own the * solution. * * @param identifier * the identifier of the solution * @return <code>true</code> if editable */ public boolean canEdit(Long identifier) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || authentication instanceof AnonymousAuthenticationToken) { return false; } Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for (GrantedAuthority grantedAuthority : authorities) { if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) { return true; } } // new solution if (identifier == null) { return true; } Account account = accountRepository.findOne(authentication.getName()); Account a = accountRepository.findAccountBySolutionId(identifier); if (account.getUsername().equals(a.getUsername())) { return true; } return false; } }