CCM NG: Todays results of getting login and admin apps running. Login should work now, but there are some problems with lazily fetched collections...

Also this commits adds the DefaultEntityGraph annotation which allows it to name a (named) EntityGraph which should be applied by default to when the entity is loaded from the database. 


git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3859 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2016-02-12 20:56:22 +00:00
parent 5bb5bf7ac6
commit 3ac09f8510
19 changed files with 462 additions and 107 deletions

View File

@ -9,6 +9,12 @@
<Root level="info"> <Root level="info">
<AppenderRef ref="Console"/> <AppenderRef ref="Console"/>
</Root> </Root>
<Logger name="com.arsdigita.ui.admin.AdminServlet"
level="debug">
</Logger>
<Logger name="com.arsdigita.ui.login.UserLoginForm"
level="debug">
</Logger>
<Logger name="com.arsdigita.packaging.Config" <Logger name="com.arsdigita.packaging.Config"
level="debug"> level="debug">
</Logger> </Logger>
@ -19,6 +25,9 @@
level="debug"> level="debug">
</Logger> </Logger>
<Logger name="org.libreccm.core.AbstractEntityRepository"
level="debug">
</Logger>
<Logger name="org.libreccm.security.Shiro" <Logger name="org.libreccm.security.Shiro"
level="debug"> level="debug">

View File

@ -7,6 +7,12 @@
<display-name>LibreCCM Devel Bundle for Wildfly</display-name> <display-name>LibreCCM Devel Bundle for Wildfly</display-name>
<!-- No JSESSIONID!!! -->
<session-config>
<tracking-mode>COOKIE</tracking-mode>
<!--<tracking-mode>URL</tracking-mode>-->
</session-config>
<filter> <filter>
<filter-name>ShiroFilter</filter-name> <filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
@ -15,10 +21,10 @@
<filter-mapping> <filter-mapping>
<filter-name>ShiroFilter</filter-name> <filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern> <url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher> <!--<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher> <dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher> <dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher> <dispatcher>ERROR</dispatcher>-->
</filter-mapping> </filter-mapping>
<listener> <listener>

View File

@ -28,7 +28,6 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.StringJoiner;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.Configuration; import org.libreccm.configuration.Configuration;
@ -83,9 +82,8 @@ public final class BebopConfig {
private Boolean showClassName = false; private Boolean showClassName = false;
public static BebopConfig getConfig() { public static BebopConfig getConfig() {
final CdiUtil cdiUtil = new CdiUtil(); final ConfigurationManager confManager = CdiUtil.createCdiUtil()
final ConfigurationManager confManager = cdiUtil.findBean( .findBean(ConfigurationManager.class);
ConfigurationManager.class);
return confManager.findConfiguration(BebopConfig.class); return confManager.findConfiguration(BebopConfig.class);
} }

View File

@ -25,7 +25,9 @@ import com.arsdigita.util.Assert;
import com.arsdigita.web.BaseApplicationServlet; import com.arsdigita.web.BaseApplicationServlet;
import com.arsdigita.xml.Document; import com.arsdigita.xml.Document;
import org.apache.log4j.Logger; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.subject.Subject;
import org.libreccm.web.CcmApplication; import org.libreccm.web.CcmApplication;
import java.io.IOException; import java.io.IOException;
@ -34,6 +36,7 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.enterprise.inject.spi.CDI;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -61,7 +64,7 @@ public class BebopApplicationServlet extends BaseApplicationServlet {
private static final long serialVersionUID = -6004503025521189639L; private static final long serialVersionUID = -6004503025521189639L;
private static final Logger s_log = Logger.getLogger( private static final Logger LOGGER = LogManager.getLogger(
BebopApplicationServlet.class); BebopApplicationServlet.class);
/** /**
@ -149,6 +152,11 @@ public class BebopApplicationServlet extends BaseApplicationServlet {
final String pathInfo = sreq.getPathInfo(); final String pathInfo = sreq.getPathInfo();
Assert.exists(pathInfo, "String pathInfo"); Assert.exists(pathInfo, "String pathInfo");
final Subject subject = CDI.current().select(Subject.class).get();
LOGGER.debug("Current session is: {}", sreq.getSession().getId());
LOGGER.debug("Current Shiro session is {}",
subject.getSession().getId().toString());
final Page page = (Page) m_pages.get(pathInfo); final Page page = (Page) m_pages.get(pathInfo);
if (page == null) { if (page == null) {

View File

@ -70,9 +70,11 @@ public final class KernelConfig {
private String defaultLanguage = "en"; private String defaultLanguage = "en";
public static KernelConfig getConfig() { public static KernelConfig getConfig() {
final CdiUtil cdiUtil = new CdiUtil(); // final CdiUtil cdiUtil = new CdiUtil();
final ConfigurationManager confManager = cdiUtil.findBean( // final ConfigurationManager confManager = cdiUtil.findBean(
ConfigurationManager.class); // ConfigurationManager.class);
final ConfigurationManager confManager = CdiUtil.createCdiUtil()
.findBean(ConfigurationManager.class);
return confManager.findConfiguration(KernelConfig.class); return confManager.findConfiguration(KernelConfig.class);
} }
@ -259,7 +261,8 @@ public final class KernelConfig {
if (supportedLanguages == null) { if (supportedLanguages == null) {
languages = ""; languages = "";
} else { } else {
languages = supportedLanguages.stream().collect(Collectors.joining(", ")); languages = supportedLanguages.stream().collect(Collectors.joining(
", "));
} }
return String.format( return String.format(

View File

@ -56,9 +56,8 @@ public final class SecurityConfig {
private Integer hashIterations = 50000; private Integer hashIterations = 50000;
public static SecurityConfig getConfig() { public static SecurityConfig getConfig() {
final CdiUtil cdiUtil = new CdiUtil(); final ConfigurationManager confManager = CdiUtil.createCdiUtil()
final ConfigurationManager confManager = cdiUtil.findBean( .findBean(ConfigurationManager.class);
ConfigurationManager.class);
return confManager.findConfiguration(SecurityConfig.class); return confManager.findConfiguration(SecurityConfig.class);
} }

View File

@ -30,6 +30,8 @@ import com.arsdigita.web.BaseApplicationServlet;
import com.arsdigita.web.LoginSignal; import com.arsdigita.web.LoginSignal;
import com.arsdigita.xml.Document; import com.arsdigita.xml.Document;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.Subject;
import org.libreccm.security.PermissionChecker; import org.libreccm.security.PermissionChecker;
import org.libreccm.web.CcmApplication; import org.libreccm.web.CcmApplication;
@ -37,15 +39,16 @@ import org.libreccm.web.CcmApplication;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.enterprise.inject.spi.CDI; import javax.enterprise.inject.spi.CDI;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import import javax.servlet.http.HttpServletResponse;
javax.servlet.http.HttpServletResponse;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager;
import static com.arsdigita.ui.admin.AdminConstants.*; import static com.arsdigita.ui.admin.AdminConstants.*;
@ -71,6 +74,8 @@ public class AdminServlet
private static final long serialVersionUID = -3912367600768871630L; private static final long serialVersionUID = -3912367600768871630L;
private static final Logger LOGGER = LogManager.getLogger(AdminServlet.class);
/** /**
* Logger instance for debugging * Logger instance for debugging
*/ */
@ -111,21 +116,40 @@ public class AdminServlet
ServletException, IOException { ServletException, IOException {
// /////// Some preparational steps /////////////// // /////// Some preparational steps ///////////////
/* Determine access privilege: only logged in users may access */ /* Determine access privilege: only logged in users may access */
final CdiUtil cdiUtil = new CdiUtil(); // final CdiUtil cdiUtil = new CdiUtil();
// final Subject subject = cdiUtil.findBean(Subject.class); // final Subject subject = cdiUtil.findBean(Subject.class);
final Subject subject = CDI.current().select(Subject.class).get(); final Subject subject = CDI.current().select(Subject.class).get();
final PermissionChecker permissionChecker = cdiUtil.findBean( // final PermissionChecker permissionChecker = cdiUtil.findBean(
PermissionChecker.class); // PermissionChecker.class);
final PermissionChecker permissionChecker = CDI.current().select(
PermissionChecker.class).get();
final ConfigurationManager confManager = CDI.current().select(ConfigurationManager.class).get();
if (confManager == null) {
throw new IllegalStateException();
}
LOGGER.debug("Checking if subject {} is authenticated...",
subject.toString());
LOGGER.debug("Current session is: {}", sreq.getSession().getId());
LOGGER.debug("Current Shiro session is {}",
subject.getSession().getId().toString());
if (!subject.isAuthenticated()) { if (!subject.isAuthenticated()) {
LOGGER.debug("Subject {} is not authenticated, redirecting to login...",
subject.toString());
throw new LoginSignal(sreq); throw new LoginSignal(sreq);
} }
/* Determine access privilege: Admin privileges must be granted */ /* Determine access privilege: Admin privileges must be granted */
LOGGER.debug("Subject is loggedin, checking if subject has required permissions...");
if (!permissionChecker.isPermitted("admin")) { if (!permissionChecker.isPermitted("admin")) {
LOGGER.debug("Subject does *not* have required permissions. "
+ "Access denied.");
throw new AccessDeniedException("User is not an administrator"); throw new AccessDeniedException("User is not an administrator");
} }
LOGGER.debug("Serving admin page...");
/* Want admin to always show the latest stuff... */ /* Want admin to always show the latest stuff... */
DispatcherHelper.cacheDisable(sresp); DispatcherHelper.cacheDisable(sresp);

View File

@ -122,7 +122,7 @@ public class ChangePasswordForm extends Form
m_returnURL.setPassIn(true); m_returnURL.setPassIn(true);
add(m_returnURL); add(m_returnURL);
final CdiUtil cdiUtil = new CdiUtil(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final Subject subject = cdiUtil.findBean(Subject.class); final Subject subject = cdiUtil.findBean(Subject.class);
final Shiro shiro = cdiUtil.findBean(Shiro.class); final Shiro shiro = cdiUtil.findBean(Shiro.class);

View File

@ -143,25 +143,27 @@ public class UserLoginForm extends Form implements LoginConstants,
// final ConfigurationManager confManager = CDI.current().select( // final ConfigurationManager confManager = CDI.current().select(
// ConfigurationManager.class).get(); // ConfigurationManager.class).get();
final BeanManager beanManager = CDI.current().getBeanManager(); // final BeanManager beanManager = CDI.current().getBeanManager();
final Set<Bean<?>> beans = beanManager.getBeans( // final Set<Bean<?>> beans = beanManager.getBeans(
ConfigurationManager.class); // ConfigurationManager.class);
final Iterator<Bean<?>> iterator = beans.iterator(); // final Iterator<Bean<?>> iterator = beans.iterator();
final ConfigurationManager confManager; // final ConfigurationManager confManager;
if (iterator.hasNext()) { // if (iterator.hasNext()) {
@SuppressWarnings("unchecked") // @SuppressWarnings("unchecked")
final Bean<ConfigurationManager> bean // final Bean<ConfigurationManager> bean
= (Bean<ConfigurationManager>) iterator // = (Bean<ConfigurationManager>) iterator
.next(); // .next();
final CreationalContext<ConfigurationManager> ctx = beanManager. // final CreationalContext<ConfigurationManager> ctx = beanManager.
createCreationalContext(bean); // createCreationalContext(bean);
//
confManager = (ConfigurationManager) beanManager.getReference( // confManager = (ConfigurationManager) beanManager.getReference(
bean, ConfigurationManager.class, ctx); // bean, ConfigurationManager.class, ctx);
} else { // } else {
throw new UncheckedWrapperException( // throw new UncheckedWrapperException(
"Failed to lookup ConfigurationManager"); // "Failed to lookup ConfigurationManager");
} // }
final ConfigurationManager confManager = CdiUtil.createCdiUtil()
.findBean(ConfigurationManager.class);
securityConfig = confManager.findConfiguration(SecurityConfig.class); securityConfig = confManager.findConfiguration(SecurityConfig.class);
setMethod(Form.POST); setMethod(Form.POST);
@ -385,12 +387,19 @@ public class UserLoginForm extends Form implements LoginConstants,
); );
token.setRememberMe(getPersistentLoginValue(state, false)); token.setRememberMe(getPersistentLoginValue(state, false));
try { try {
LOGGER.debug("Trying to login user {}...", subject.toString());
subject.login(token); subject.login(token);
} catch (AuthenticationException ex) { } catch (AuthenticationException ex) {
onLoginFail(event, ex); onLoginFail(event, ex);
} }
LOGGER.debug("User {} logged in successfully.", token.getUsername()); LOGGER.debug("User {} logged in successfully.", token.getUsername());
LOGGER.debug("subject = {}", subject.toString());
LOGGER.debug("Current session is: {}",
state.getRequest().getSession().getId());
LOGGER.debug("Current Shiro session is {}",
subject.getSession().getId().toString());
} }
/** /**

View File

@ -21,15 +21,28 @@ package com.arsdigita.web;
import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.dispatcher.DispatcherHelper;
//import com.arsdigita.kernel.security.Util; //import com.arsdigita.kernel.security.Util;
import com.arsdigita.util.Assert; import com.arsdigita.util.Assert;
import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.util.servlet.HttpHost; import com.arsdigita.util.servlet.HttpHost;
import oracle.jrockit.jfr.tools.ConCatRepository;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.web.CcmApplication; import org.libreccm.web.CcmApplication;
import java.util.Iterator;
import java.util.Set;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/** /**
* <p> * <p>
* URL models a future request according to the servlet worldview. Its principal * URL models a future request according to the servlet worldview. Its principal
@ -516,8 +529,8 @@ public class URL {
* starts with a "/". For example, <code>"/ccm/forum/thread.jsp"</code>.</p> * starts with a "/". For example, <code>"/ccm/forum/thread.jsp"</code>.</p>
* *
* <p> * <p>
* This method is defined to return the equivalent of * <code>getWebContextPath() + getServletPath() + * This method is defined to return the equivalent of * <code>getWebContextPath() + getServletPath() +
getPathInfo()</code>.</p> * getPathInfo()</code>.</p>
* *
* @see javax.servlet.http.HttpServletRequest#getRequestURI() * @see javax.servlet.http.HttpServletRequest#getRequestURI()
* @return a <code>String</code> comprised of the context path, servlet * @return a <code>String</code> comprised of the context path, servlet
@ -679,7 +692,37 @@ public class URL {
public static final URL there(final HttpServletRequest sreq, public static final URL there(final HttpServletRequest sreq,
final String path, final String path,
final ParameterMap params) { final ParameterMap params) {
final WebConfig config = Web.getConfig(); final BeanManager beanManager;
try {
final InitialContext context = new InitialContext();
beanManager = (BeanManager) context.lookup(
"java:comp/BeanManager");
} catch (NamingException ex) {
throw new UncheckedWrapperException(ex);
}
final Set<Bean<?>> beans = beanManager.getBeans(
ConfigurationManager.class);
final Iterator<Bean<?>> iterator = beans.iterator();
final ConfigurationManager confManager;
if (iterator.hasNext()) {
@SuppressWarnings("unchecked")
final Bean<ConfigurationManager> bean
= (Bean<ConfigurationManager>) iterator
.next();
final CreationalContext<ConfigurationManager> ctx = beanManager
.createCreationalContext(bean);
confManager = (ConfigurationManager) beanManager.getReference(
bean, ConfigurationManager.class, ctx);
} else {
throw new IllegalStateException("No configuration manager");
}
// final ConfigurationManager confManager = CDI.current().select(
// ConfigurationManager.class).get();
final WebConfig config = confManager.findConfiguration(WebConfig.class);
// final WebConfig config = Web.getConfig();
Assert.exists(sreq, "HttpServletRequest sreq"); Assert.exists(sreq, "HttpServletRequest sreq");
Assert.exists(config, "WebConfig config"); Assert.exists(config, "WebConfig config");
@ -798,7 +841,7 @@ public class URL {
return URL.there(sreq, app.getPrimaryUrl().toString(), params); return URL.there(sreq, app.getPrimaryUrl().toString(), params);
} else { } else {
return URL.there(sreq, app.getPrimaryUrl().toString() + pathInfo, return URL.there(sreq, app.getPrimaryUrl().toString() + pathInfo,
params); params);
} }
} }

View File

@ -84,29 +84,32 @@ public final class WebConfig {
private String dynamicHostProviderClass; private String dynamicHostProviderClass;
public static WebConfig getConfig() { public static WebConfig getConfig() {
final BeanManager beanManager = CDI.current().getBeanManager(); final ConfigurationManager confManager = CDI.current().select(
final Set<Bean<?>> beans = beanManager.getBeans( ConfigurationManager.class).get();
ConfigurationManager.class);
final Iterator<Bean<?>> iterator = beans.iterator();
final ConfigurationManager confManager;
if (iterator.hasNext()) {
@SuppressWarnings("unchecked")
final Bean<ConfigurationManager> bean
= (Bean<ConfigurationManager>) iterator
.next();
final CreationalContext<ConfigurationManager> ctx = beanManager
.createCreationalContext(bean);
confManager = (ConfigurationManager) beanManager.getReference( // final BeanManager beanManager = CDI.current().getBeanManager();
bean, ConfigurationManager.class, ctx); // final Set<Bean<?>> beans = beanManager.getBeans(
} else { // ConfigurationManager.class);
LOGGER.error(new ParameterizedMessage( // final Iterator<Bean<?>> iterator = beans.iterator();
"No CDI Bean for type {} found.", // final ConfigurationManager confManager;
ConfigurationManager.class.getName())); // if (iterator.hasNext()) {
throw new IllegalStateException(String.format( // @SuppressWarnings("unchecked")
"No CDI Bean for type \"%s\" found", // final Bean<ConfigurationManager> bean
ConfigurationManager.class.getName())); // = (Bean<ConfigurationManager>) iterator
} // .next();
// final CreationalContext<ConfigurationManager> ctx = beanManager
// .createCreationalContext(bean);
//
// confManager = (ConfigurationManager) beanManager.getReference(
// bean, ConfigurationManager.class, ctx);
// } else {
// LOGGER.error(new ParameterizedMessage(
// "No CDI Bean for type {} found.",
// ConfigurationManager.class.getName()));
// throw new IllegalStateException(String.format(
// "No CDI Bean for type \"%s\" found",
// ConfigurationManager.class.getName()));
// }
// final CdiUtil cdiUtil = new CdiUtil(); // final CdiUtil cdiUtil = new CdiUtil();
// final ConfigurationManager confManager = cdiUtil.findBean( // final ConfigurationManager confManager = cdiUtil.findBean(

View File

@ -24,9 +24,9 @@ import static org.libreccm.core.CoreConstants.*;
import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;
import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObject;
import org.libreccm.core.DefaultEntityGraph;
import org.libreccm.l10n.LocalizedString; import org.libreccm.l10n.LocalizedString;
import org.libreccm.web.CcmApplication; import org.libreccm.web.CcmApplication;
import org.omg.CORBA.DomainManager;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
@ -91,6 +91,7 @@ import javax.xml.bind.annotation.XmlRootElement;
@NamedAttributeNode("subCategories") @NamedAttributeNode("subCategories")
})}) })})
}) })
@DefaultEntityGraph("Domain.allCategories")
@XmlRootElement(name = "domain", namespace = CAT_XML_NS) @XmlRootElement(name = "domain", namespace = CAT_XML_NS)
public class Domain extends CcmObject implements Serializable { public class Domain extends CcmObject implements Serializable {

View File

@ -29,6 +29,8 @@ import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.CDI; import javax.enterprise.inject.spi.CDI;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/** /**
* *
@ -44,6 +46,23 @@ public class CdiUtil {
beanManager = CDI.current().getBeanManager(); beanManager = CDI.current().getBeanManager();
} }
private CdiUtil(final BeanManager beanManager) {
this.beanManager = beanManager;
}
public static CdiUtil createCdiUtil() {
try {
final InitialContext context = new InitialContext();
final BeanManager beanManager = (BeanManager) context.lookup(
"java:comp/BeanManager");
return new CdiUtil(beanManager);
} catch(NamingException ex) {
throw new IllegalStateException("Unable to lookup BeanManager.", ex);
}
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T findBean(final Class<T> beanType) { public <T> T findBean(final Class<T> beanType) {
final Set<Bean<?>> beans = beanManager.getBeans(beanType); final Set<Bean<?>> beans = beanManager.getBeans(beanType);

View File

@ -18,13 +18,18 @@
*/ */
package org.libreccm.core; package org.libreccm.core;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.EntityGraph; import javax.persistence.EntityGraph;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
@ -39,7 +44,11 @@ import javax.persistence.criteria.Root;
*/ */
public abstract class AbstractEntityRepository<K, E> { public abstract class AbstractEntityRepository<K, E> {
static final String FETCH_GRAPH_HINT_KEY = "javax.persistence.fetchgraph"; private static final Logger LOGGER = LogManager.getLogger(
AbstractEntityRepository.class);
protected static final String FETCH_GRAPH_HINT_KEY
= "javax.persistence.fetchgraph";
/** /**
* The {@link EntityManager} instance to use. Provided by the container via * The {@link EntityManager} instance to use. Provided by the container via
@ -57,12 +66,99 @@ public abstract class AbstractEntityRepository<K, E> {
return entityManager; return entityManager;
} }
/**
* Create an {@link EntityGraph} for the entity class of this repository.
*
* For more details about entity graphs/fetch graphs refer to the JPA
* documentation. Internally this method uses
* {@link EntityManager#createEntityGraph(java.lang.Class)}.
*
* @return An EntityGraph for this entity graph.
*/
public EntityGraph<E> createEntityGraph() {
return entityManager.createEntityGraph(getEntityClass());
}
protected void applyDefaultEntityGraph(final TypedQuery<E> query) {
if (getEntityClass().isAnnotationPresent(DefaultEntityGraph.class)) {
LOGGER.debug("The following EntityGraphs are available for the "
+ "entity class {}:",
getEntityClass().getName());
getEntityManager().getEntityGraphs(getEntityClass()).stream()
.forEach(g -> LOGGER.debug("\t{}", g.getName()));
LOGGER.debug("Entity class {} has default entity graphs:",
getEntityClass().getName());
LOGGER.debug("Applying entity graph {}",
getEntityClass().getAnnotation(
DefaultEntityGraph.class).value());
query.setHint(FETCH_GRAPH_HINT_KEY,
entityManager.getEntityGraph(
getEntityClass().getAnnotation(
DefaultEntityGraph.class).value()));
}
}
/**
* Helper method for retrieving a single result from a query.
*
* @param query The query from which the result is retrieved.
*
* @return A first result or the query or {@code null} of there is no
* result.
*/
protected E getSingleResultOrNull(final TypedQuery<E> query) {
final List<E> result = query.getResultList();
if (result.isEmpty()) {
return null;
} else {
return result.get(0);
}
}
/**
* Helper method for retrieving a single result from a query. In contrast to
* {@link #getSingleResultOrNull(javax.persistence.TypedQuery)} this method
* return an {@link Optional} for the result.
*
* @param query The query from which the result is retrieved.
*
* @return An {@link Optional} instance wrapping the first result of the
* query. If there is no result the {@code Optional} is empty.
*/
protected Optional<E> getSingleResult(final TypedQuery<E> query) {
final List<E> result = query.getResultList();
if (result.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(result.get(0));
}
}
/**
* Creates a mutable copy of a named entity graph which an be further
* customised.
*
* Internally this method uses
* {@link EntityManager#createEntityGraph(java.lang.String)}.
*
* @param entityGraphName The name of the named entity graph.
*
* @return A mutable copy of the named entity graph identified by the
* provided name or {@code null} if there is no such named entity
* graph.
*/
@SuppressWarnings("unchecked")
public EntityGraph<E> createEntityGraph(final String entityGraphName) {
return (EntityGraph<E>) entityManager.createEntityGraph(
entityGraphName);
}
/** /**
* The class of entities for which this repository can be used. For creating * The class of entities for which this repository can be used. For creating
* a repository class overwrite this method. * a repository class overwrite this method.
* *
* @return The {@code Class} of the Entity which are managed by this * @return The {@code Class} of the Entity which are managed by this
* repository. * repository.
*/ */
public abstract Class<E> getEntityClass(); public abstract Class<E> getEntityClass();
@ -72,15 +168,21 @@ public abstract class AbstractEntityRepository<K, E> {
* @param entityId The ID of the entity to retrieve. * @param entityId The ID of the entity to retrieve.
* *
* @return The entity identified by the provided ID of {@code null} if there * @return The entity identified by the provided ID of {@code null} if there
* is no such entity. * is no such entity.
*/ */
public E findById(final K entityId) { public E findById(final K entityId) {
return entityManager.find(getEntityClass(), entityId); if (getEntityClass().isAnnotationPresent(DefaultEntityGraph.class)) {
return findById(entityId, getEntityClass().getAnnotation(
DefaultEntityGraph.class).value());
} else {
return entityManager.find(getEntityClass(), entityId);
}
} }
public E findById(final K entityId, final String entityGraphName) { public E findById(final K entityId, final String entityGraphName) {
@SuppressWarnings("unchecked")
final EntityGraph<E> entityGraph = (EntityGraph<E>) entityManager. final EntityGraph<E> entityGraph = (EntityGraph<E>) entityManager.
getEntityGraph(entityGraphName); getEntityGraph(entityGraphName);
return findById(entityId, entityGraph); return findById(entityId, entityGraph);
} }
@ -95,15 +197,15 @@ public abstract class AbstractEntityRepository<K, E> {
* responsible for. * responsible for.
* *
* @return The list of entities in the database which are of the type * @return The list of entities in the database which are of the type
* provided by {@link #getEntityClass()}. * provided by {@link #getEntityClass()}.
*/ */
public List<E> findAll() { public List<E> findAll() {
// We are using the Critiera API here because otherwise we can't // We are using the Critiera API here because otherwise we can't
// pass the type of the entity dynmacially. // pass the type of the entity dynmacially.
final CriteriaBuilder criteriaBuilder = entityManager final CriteriaBuilder criteriaBuilder = entityManager
.getCriteriaBuilder(); .getCriteriaBuilder();
final CriteriaQuery<E> criteriaQuery = criteriaBuilder.createQuery( final CriteriaQuery<E> criteriaQuery = criteriaBuilder.createQuery(
getEntityClass()); getEntityClass());
final Root<E> root = criteriaQuery.from(getEntityClass()); final Root<E> root = criteriaQuery.from(getEntityClass());
criteriaQuery.select(root); criteriaQuery.select(root);
@ -117,8 +219,9 @@ public abstract class AbstractEntityRepository<K, E> {
* entity is a a new one. * entity is a a new one.
* *
* @param entity The entity to check. * @param entity The entity to check.
*
* @return {@code true} if the entity is new (isn't in the database yet), * @return {@code true} if the entity is new (isn't in the database yet),
* {@code false} otherwise. * {@code false} otherwise.
*/ */
public abstract boolean isNew(final E entity); public abstract boolean isNew(final E entity);

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.libreccm.core;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({TYPE})
public @interface DefaultEntityGraph {
String value();
}

View File

@ -20,6 +20,8 @@ package org.libreccm.security;
import static org.libreccm.core.CoreConstants.*; import static org.libreccm.core.CoreConstants.*;
import org.libreccm.core.DefaultEntityGraph;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -33,6 +35,9 @@ import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Inheritance; import javax.persistence.Inheritance;
import javax.persistence.InheritanceType; import javax.persistence.InheritanceType;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery; import javax.persistence.NamedQuery;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
@ -56,6 +61,12 @@ import javax.xml.bind.annotation.XmlElementWrapper;
@NamedQuery(name = "Party.findByName", @NamedQuery(name = "Party.findByName",
query = "SELECT p FROM Party p WHERE p.name = :name") query = "SELECT p FROM Party p WHERE p.name = :name")
}) })
@NamedEntityGraphs({
@NamedEntityGraph(name = "Party.withRoleMemberships",
attributeNodes = @NamedAttributeNode(
value = "roleMemberships"))
})
@DefaultEntityGraph("Party.withRoleMemberships")
public class Party implements Serializable { public class Party implements Serializable {
private static final long serialVersionUID = 3319997992281332204L; private static final long serialVersionUID = 3319997992281332204L;

View File

@ -62,7 +62,8 @@ public class Shiro {
@Produces @Produces
@Named("securityManager") @Named("securityManager")
public SecurityManager getSecurityManager() { public SecurityManager getSecurityManager() {
return proxy(SecurityManager.class, new SubjectInvocationHandler()); return proxy(SecurityManager.class,
new SecurityManagerInvocationHandler());
} }
/** /**
@ -115,6 +116,7 @@ public class Shiro {
return publicUser; return publicUser;
} }
@SuppressWarnings("unchecked")
private <T> T proxy(final Class<T> clazz, final InvocationHandler handler) { private <T> T proxy(final Class<T> clazz, final InvocationHandler handler) {
return (T) Proxy.newProxyInstance(getClass().getClassLoader(), return (T) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class<?>[]{clazz}, new Class<?>[]{clazz},

View File

@ -20,6 +20,7 @@ package org.libreccm.security;
import static org.libreccm.core.CoreConstants.*; import static org.libreccm.core.CoreConstants.*;
import org.libreccm.core.DefaultEntityGraph;
import org.libreccm.core.EmailAddress; import org.libreccm.core.EmailAddress;
import java.io.Serializable; import java.io.Serializable;
@ -37,9 +38,13 @@ import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery; import javax.persistence.NamedQuery;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.OrderColumn;
import javax.persistence.Table; import javax.persistence.Table;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
@ -59,9 +64,18 @@ import javax.xml.bind.annotation.XmlTransient;
@NamedQuery(name = "User.findByName", @NamedQuery(name = "User.findByName",
query = "SELECT u FROM User u WHERE u.name = :name"), query = "SELECT u FROM User u WHERE u.name = :name"),
@NamedQuery(name = "User.findByEmailAddress", @NamedQuery(name = "User.findByEmailAddress",
query = "SELECT u FROM User u WHERE " + query = "SELECT u FROM User u WHERE "
"u.primaryEmailAddress.address = :emailAddress") + "u.primaryEmailAddress.address = :emailAddress")
}) })
@NamedEntityGraphs({
@NamedEntityGraph(name = "User.withGroupAndRoleMemberships",
attributeNodes = {
@NamedAttributeNode(
value = "groupMemberships"),
@NamedAttributeNode(
value = "roleMemberships")})
})
@DefaultEntityGraph("User.withGroupAndRoleMemberships")
@XmlRootElement(name = "user", namespace = CORE_XML_NS) @XmlRootElement(name = "user", namespace = CORE_XML_NS)
//Supressing a few warnings from PMD because they misleading here. //Supressing a few warnings from PMD because they misleading here.
//User is perfectly fine class name, and the complexity is not to high... //User is perfectly fine class name, and the complexity is not to high...
@ -112,9 +126,9 @@ public class User extends Party implements Serializable {
private List<EmailAddress> emailAddresses; private List<EmailAddress> emailAddresses;
/** /**
* A user can be banned which means that he or she can't login into * A user can be banned which means that he or she can't login into the
* the system anymore. We use this approach rather than simply deleting users * system anymore. We use this approach rather than simply deleting users to
* to preserve the edit history of several objects. * preserve the edit history of several objects.
*/ */
@Column(name = "BANNED") @Column(name = "BANNED")
@XmlElement(name = "banned", namespace = CORE_XML_NS) @XmlElement(name = "banned", namespace = CORE_XML_NS)

View File

@ -21,8 +21,10 @@ package org.libreccm.security;
import org.libreccm.core.AbstractEntityRepository; import org.libreccm.core.AbstractEntityRepository;
import java.util.List; import java.util.List;
import java.util.Optional;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityGraph;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
/** /**
@ -52,20 +54,59 @@ public class UserRepository extends AbstractEntityRepository<Long, User> {
* @param name The name of the user to find. * @param name The name of the user to find.
* *
* @return The user identified by the provided name. If there are multiple * @return The user identified by the provided name. If there are multiple
* user matching the user name (should be possible) the first one is * user matching the user name (should be possible) the first one is
* returned. If there is no matching user {@code null} is returned. * returned. If there is no matching user {@code null} is returned.
*/ */
public User findByName(final String name) { public User findByName(final String name) {
final TypedQuery<User> query = getEntityManager().createNamedQuery( final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.findByName", "User.findByName", User.class);
User.class); applyDefaultEntityGraph(query);
query.setParameter("name", name); query.setParameter("name", name);
final List<User> result = query.getResultList();
if (result.isEmpty()) { return getSingleResultOrNull(query);
return null;
} else { // final List<User> result = query.getResultList();
return result.get(0); // if (result.isEmpty()) {
} // return null;
// } else {
// return result.get(0);
// }
}
/**
* Finds a user by its name and applies the given named entity graph to the
* query.
*
* @param name The name of the user to find.
* @param entityGraphName The named entity graph to use.
*
* @return The user identified by the provided name. If there are multiple
* user matching the user name (should be possible) the first one is
* returned. If there is no matching user {@code null} is returned.
*/
public User findByName(final String name, final String entityGraphName) {
@SuppressWarnings("unchecked")
final EntityGraph<User> entityGraph
= (EntityGraph<User>) getEntityManager()
.getEntityGraph(entityGraphName);
return findByName(name, entityGraph);
}
public User findByName(final String name,
final EntityGraph<User> entityGraph) {
final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.findByName", User.class);
query.setParameter("name", name);
query.setHint(FETCH_GRAPH_HINT_KEY, entityGraph);
return getSingleResultOrNull(query);
// final List<User> result = query.getResultList();
// if (result.isEmpty()) {
// return null;
// } else {
// return result.get(0);
// }
} }
/** /**
@ -74,19 +115,42 @@ public class UserRepository extends AbstractEntityRepository<Long, User> {
* @param emailAddress The email address which identifies the user. * @param emailAddress The email address which identifies the user.
* *
* @return The user identified by the provided email address. If there are * @return The user identified by the provided email address. If there are
* multiple matching users only the first one is returned. If there is no * multiple matching users only the first one is returned. If there
* matching user {@code null} is returned. * is no matching user {@code null} is returned.
*/ */
public User findByEmailAddress(final String emailAddress) { public User findByEmailAddress(final String emailAddress) {
final TypedQuery<User> query = getEntityManager().createNamedQuery( final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.findByEmailAddress", User.class); "User.findByEmailAddress", User.class);
query.setParameter("emailAddress", emailAddress); query.setParameter("emailAddress", emailAddress);
final List<User> result = query.getResultList(); applyDefaultEntityGraph(query);
if (result.isEmpty()) {
return null; return getSingleResultOrNull(query);
} else {
return result.get(0); // final List<User> result = query.getResultList();
} // if (result.isEmpty()) {
// return null;
// } else {
// return result.get(0);
// }
}
public User findByEmailAddress(final String emailAddress,
final String entityGraphName) {
@SuppressWarnings("unchecked")
final EntityGraph<User> entityGraph
= (EntityGraph<User>) getEntityManager()
.getEntityGraph(entityGraphName);
return findByEmailAddress(emailAddress, entityGraph);
}
public User findByEmailAddress(final String emailAddress,
final EntityGraph<User> entityGraph) {
final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.findByEmailAddress", User.class);
query.setParameter("emailAddress", emailAddress);
query.setHint(FETCH_GRAPH_HINT_KEY, entityGraph);
return getSingleResultOrNull(query);
} }
} }