javajasper-reportsjasperserver

How to get the ReportDesign class of some subreport on the server for scriptlet-based modifications?


Locally I have a master.jrxml report and some subreport.jrxml and can load and manipulate the subreport via ReportDesign design = JRXmlLoader.load( "/local-file-dir/path/to/subreport.jrxml" ) in the Scriptlet code.

On the server, the above load method (trigged by my master.jrxml) obviously cannot deal with repo paths, no matter what I tried (basically net.sf.jasperreports.engine.JRException: java.io.FileNotFoundException: ...)

(subreport.jrxml uploaded as a JRXML File Resource - not as Report with invisibly created folder structures ; subreport-attached.jrxml uploaded as a file resource of my master.jrxml Report )

I also tried the following with the above uri variants as found elsewhere in the web without success:


Solution

  • Woohoo! :) The last approach with the com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService worked like this:

    JasperDesign design = JRXmlLoader.load(
       ( (RepositoryService) StaticApplicationContext.getApplicationContext()
         .getBean( "repositoryService" ) 
       )
       .getResourceData( JasperServerUtil.getExecutionContext() , 
         "repo:/some/where/subreport.jrxml" )
       .getDataStream()
    

    Puh! hard birth.

    Usage in Jobs for/since JasperReports Server v7.2.0

    Since we moved to JRS 7.2.0 (CE) (from JRS 6.3.0 (CE)) we realized, that the above solution works fine for ad hoc report execution but throws an error when run in a report generation job (= job context):

    Caused by: java.lang.IllegalStateException: No thread-bound request found: 
           Are you referring to request attributes outside of an actual web request,
           or processing a request outside of the originally receiving thread?
           If you are actually operating within a web request and still receive this 
           message, your code is probably running outside of 
           DispatcherServlet/DispatcherPortlet: 
           In this case, use  RequestContextListener  or  RequestContextFilter to 
           expose the current request.
        at org.springframework.web.context.request.RequestContextHolder.
             currentRequestAttributes(RequestContextHolder.java:131)
    

    In this case we found another more verbose single point solution working in both scenarios:

    This may not be the best solution from a security point of view since an admin context/rights are used, but for our environment that was fine.

    complete code

    For completeness I'll copy the referenced code in here: (especially since the wiki is in a weeklong process of beeing moved and maybe some stuff may get lost)

    package com.jaspersoft.jasperserver.api.security.externalAuth.sample.processors;
     
    import java.util.ArrayList;
    import java.util.List;
    import java.util.regex.Pattern;
     
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.security.core.context.SecurityContextHolder;
     
    import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
    import com.jaspersoft.jasperserver.api.common.domain.impl.ExecutionContextImpl;
    import com.jaspersoft.jasperserver.api.metadata.common.domain.Folder;
    import com.jaspersoft.jasperserver.api.metadata.common.domain.InternalURI;
    import com.jaspersoft.jasperserver.api.metadata.common.domain.client.FolderImpl;
    import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
    import com.jaspersoft.jasperserver.api.metadata.user.domain.ObjectPermission;
    import com.jaspersoft.jasperserver.api.metadata.user.domain.Role;
    import com.jaspersoft.jasperserver.api.metadata.user.domain.User;
    import com.jaspersoft.jasperserver.api.metadata.user.service.ObjectPermissionService;
    import com.jaspersoft.jasperserver.api.metadata.user.service.UserAuthorityService;
     
    /**
     * This class is useful to handle folders in the JRS Repository.
     *
     */
    public class FolderHelper {
        private static Log log = LogFactory.getLog(FolderHelper.class);
        private RepositoryService repositoryService;
        private ObjectPermissionService permissionService;
        private ExecutionContext ctx;
        private UserAuthorityService internalUserAuthorityService;
     
        protected static final Pattern RESOURCE_ID_INVALID_CHAR =
                Pattern.compile("[^\\p{L}\\p{N}]");
     
        protected static final String RESOURCE_ID_CHAR_REPLACEMENT = "_";
     
        /**
         * Constructor for the Folder Helper Class
         * @param repositoryService
         * @param permissionService
         * @param internalUserAuthorityService
         */
        public FolderHelper(RepositoryService repositoryService,
                ObjectPermissionService permissionService,
                UserAuthorityService internalUserAuthorityService) {
            super();
            this.repositoryService = repositoryService;
            this.permissionService = permissionService;
            this.internalUserAuthorityService = internalUserAuthorityService;
            this.ctx = getAdminContext();
        }
     
        /**
         * Set a Permission to a folder for the requested role
         * @param target  The InternalURI of the folder
         * @param role The role expressed as String
         * @param permissionType Permission type expressed as String, the available values are available in the class JasperServerAclEntry
         * @return the object permission generated, already saved using the permissionService
         */
        public ObjectPermission setPermissionOnFolderForRole(InternalURI target, String role, String permissionType) {
            Role userRole=internalUserAuthorityService.getRole(ctx, role);
            return setPermissionsToFolder(target, userRole, permissionType);
        }
     
        /**
         * Set a Permission to a folder for the requested user
         * @param target  The InternalURI of the folder
         * @param user The role expressed as String
         * @param permissionType Permission type expressed as String, the available values are available in the class JasperServerAclEntry
         * @return the object permission generated, already saved using the permissionService
         */
        public ObjectPermission setPermissionOnFolderForUser(InternalURI target, User user, String permissionType) {
            return setPermissionsToFolder(target, user, permissionType);
        }
     
        /**
         * Set a Permission to a folder for the requested recipient
         * @param target The InternalURI of the folder
         * @param recipient  The recipient of the permission, it can be a User, a Role...
         * @param permissionType Permission type expressed as String, the available values are available in the class JasperServerAclEntry
         * @return  the object permission generated, already saved using the permissionService
         */
        public ObjectPermission setPermissionsToFolder(InternalURI target, Object recipient, String permissionType) {
            int permissionMask = JasperServerPermissionUtil.parsePermission(permissionType);
            ObjectPermission permission = permissionService.newObjectPermission(ctx);
            permission.setURI(target.getURI());
            permission.setPermissionRecipient(recipient);
            permission.setPermissionMask(permissionMask);
            permissionService.putObjectPermission(ctx, permission);
            return permission;
        }
     
        /**
         * Create a new folder
         * @param name Name of the folder
         * @param label Label of the folder
         * @param parent parent URI for the folder
         * @param description Description of the folder
         * @return the Folder Object for the created folder
         */
        public Folder createFolder(String name, String label, String parent, String description) {
     
            Folder folder = new FolderImpl();
            folder.setName(name);
            folder.setLabel(label);
            folder.setParentFolder(parent);
            folder.setDescription(description);
     
            repositoryService.saveFolder(ctx, folder);
            return folder;
        }
     
        public String createFolderIfNotExists(String name, String label, String parent, String description) {
            String folderPath = parent + Folder.SEPARATOR + name;
            if(!folderExists(folderPath)){
                createFolder(name, label, parent, description);
            }
            return folderPath;
        }
     
        /**
         * Remove all the setted up permissions on a folder.
         * It retrieves only the permissions defined for the folder, not the inherited ones.
         * An inherited permission is marked with a * in the JRS interfaces
         * @param folder
         */
        public void removeAllPermissionsFromFolder(InternalURI folder){
            User currentUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
     
            List listPermissions=permissionService.getEffectivePermissionsForObject(ctx, folder);
            for(ObjectPermission perm:listPermissions){
                log.error("deleting permission for:"+perm.getPermissionRecipient().toString());
                if(!perm.getPermissionRecipient().equals(currentUser)){
                    int permissionMask = JasperServerPermissionUtil.parsePermission("NOTHING");
                    perm.setPermissionMask(permissionMask);
                    permissionService.putObjectPermission(ctx, perm);
                }
     
            }
     
        }
     
        /**
         * @return the AdminContext, needed to perform some operations on folders
         */
        protected ExecutionContext getAdminContext() {
            ExecutionContext ctx = new ExecutionContextImpl();
            List attrs = new ArrayList();
            attrs.add(ObjectPermissionService.PRIVILEGED_OPERATION);
            ctx.setAttributes(attrs);
            return ctx;
        }
     
        /**
         * @param folderPath
         * @return true if the folder already exists in the repository
         */
        public boolean folderExists(String folderPath){
            return repositoryService.getFolder(ctx, folderPath)!=null;
        }
     
        /**
         * @param inputLabel the name to set as ID
         * @return the String with unallowed character replaced by _
         */
        public static String getCompliantIdForResource(String inputLabel){
            String id = RESOURCE_ID_INVALID_CHAR.matcher(inputLabel).replaceAll(
                    RESOURCE_ID_CHAR_REPLACEMENT);
            return id;
        }
     
    }
    
    
    package com.jaspersoft.jasperserver.api.security.externalAuth.sample.processors;
     
    import java.util.Collections;
    import java.util.Map;
     
    import org.apache.log4j.LogManager;
    import org.apache.log4j.Logger;
    import org.springframework.security.core.context.SecurityContextHolder;
     
    import com.jaspersoft.jasperserver.api.metadata.common.domain.Folder;
    import com.jaspersoft.jasperserver.api.metadata.common.util.RepositoryLabelIDHelper;
    import com.jaspersoft.jasperserver.api.metadata.user.domain.User;
    import com.jaspersoft.jasperserver.api.security.externalAuth.processors.AbstractExternalUserProcessor;
     
    /**
     * This class is useful to create a folder automatically when a user logs in and set
     * its permissions according to configuration.
     * It adds features to externalUserFolderProcessor (which is already provided as an example in
     * the documentation).
     *
     */
    public class ExtendedUserFolderProcessor extends AbstractExternalUserProcessor {
        private static final Logger log = LogManager.getLogger(ExtendedUserFolderProcessor.class);
     
        // The parent folder to create user directories under. default value is root.
        private String userFoldersParentDirectory = "";
     
        // The permission for the home folder created
        private String userPermissionOnFolder = "READ_WRITE_CREATE_DELETE";
     
        // Map containing  ROLE_NAME : permission_on_folder
        private Map rolePermissionMap = Collections.emptyMap();
     
        // Pattern for the folder label
        private String folderLabelPattern = UserDetailPropertyKey.USERNAME.value;
     
        // Pattern for the folder id
        private String folderIdPattern = UserDetailPropertyKey.USERNAME.value;
     
        private Boolean createParentFolder = Boolean.FALSE;
     
        // Checks if user has a folder on his name in the configured location, if not creates
        // one. The parent folder is created if not existent and requested.
        @Override
        public void process() {
            FolderHelper helper = new FolderHelper(getRepositoryService(),
                    getObjectPermissionService(), getUserAuthorityService());
     
            if (getCreateParentFolder()&&!helper.folderExists(getUserFoldersParentDirectory())){
                 createParentFolder(helper);
             }
     
            if (!helper.folderExists(getUserFolderPathUri())){
                createUserFolder(helper);
            }
     
        }
     
        private void createUserFolder(FolderHelper helper) {
            User currentUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
     
            // preparing the folder
            String folderLabel = getUserFolderLabel(currentUser);
            String generatedId = RepositoryLabelIDHelper.generateIdBasedOnLabel(getRepositoryService(), getUserFolderPathUri(), getUserFolderId(currentUser));
     
            Folder folder = helper.createFolder(generatedId, folderLabel, getUserFoldersParentDirectory(), "Default user folder");
            log.debug("folder "+folder.getName()+" was created with label \""+folder.getLabel()+"\" .");
     
            // setting the permission for the user
            helper.setPermissionsToFolder(folder, currentUser, getUserPermissionOnFolder());
     
            for(String role : getRolePermissionMap().keySet()){
                String permissionType = getRolePermissionMap().get(role);
                helper.setPermissionOnFolderForRole(folder, role, permissionType);
            }
     
        }
     
        private void createParentFolder(FolderHelper helper) {
            // Preparing the folder
            Integer lastIdx = getUserFoldersParentDirectory().lastIndexOf(Folder.SEPARATOR);
            String dirName = getUserFoldersParentDirectory().substring(lastIdx+1);
            String dirParentPath = getUserFoldersParentDirectory().substring(0, lastIdx);
     
            String generatedId = RepositoryLabelIDHelper.generateIdBasedOnLabel(getRepositoryService(), dirParentPath, dirName);
     
            Folder folder = helper.createFolder(generatedId, dirName, dirParentPath, "Container for user folders");
            log.debug("folder "+folder.getName()+" was created with label \""+folder.getLabel()+"\" .");
        }
     
        private String getStringFromPattern(User currentUser, String folderId) {
            String folderLabel = folderId;
            folderLabel = folderLabel.replaceAll(UserDetailPropertyKey.USERNAME.value, currentUser.getUsername());
            folderLabel = folderLabel.replaceAll(UserDetailPropertyKey.FULL_NAME.value, currentUser.getFullName());
            folderLabel = folderLabel.replaceAll(UserDetailPropertyKey.EMAIL_ADDRESS.value, currentUser.getEmailAddress());
            folderLabel = folderLabel.replaceAll(UserDetailPropertyKey.TENANT.value, currentUser.getTenantId());
            return folderLabel;
        }
     
        private String getUserFolderId(User currentUser) {
            return getStringFromPattern(currentUser, getFolderIdPattern());
        }
     
        private String getUserFolderLabel(User currentUser) {
            return getStringFromPattern(currentUser, getFolderLabelPattern());
        }
     
        private String getUserFolderPathUri() {
            User user = ((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal());
            String folderId = getUserFolderId(user);
            return getUserFoldersParentDirectory() + Folder.SEPARATOR + FolderHelper.getCompliantIdForResource(folderId);
        }
     
        public String getUserFoldersParentDirectory() {
            return userFoldersParentDirectory;
        }
     
        public void setUserFoldersParentDirectory(String userFoldersParentDirectory) {
            this.userFoldersParentDirectory = userFoldersParentDirectory;
        }
     
        public Map getRolePermissionMap() {
            return rolePermissionMap;
        }
     
        public void setRolePermissionMap(Map rolePermissionMap) {
            this.rolePermissionMap = rolePermissionMap;
        }
     
        public String getUserPermissionOnFolder() {
            return userPermissionOnFolder;
        }
     
        public void setUserPermissionOnFolder(String userPermissionOnFolder) {
            this.userPermissionOnFolder = userPermissionOnFolder;
        }
     
        public String getFolderLabelPattern() {
            return folderLabelPattern;
        }
     
        public void setFolderLabelPattern(String folderLabelPattern) {
            this.folderLabelPattern = folderLabelPattern;
        }
     
        public String getFolderIdPattern() {
            return folderIdPattern;
        }
     
        public void setFolderIdPattern(String folderIdPattern) {
            this.folderIdPattern = folderIdPattern;
        }
     
        public Boolean getCreateParentFolder() {
            return createParentFolder;
        }
     
        public void setCreateParentFolder(Boolean createParentFolder) {
            this.createParentFolder = createParentFolder;
        }
     
        private enum UserDetailPropertyKey{
            USERNAME("USERNAME"), FULL_NAME("FULL_NAME"),EMAIL_ADDRESS("EMAIL_ADDRESS"),TENANT("TENANT");
            private String value;
     
            private UserDetailPropertyKey(String value) {
                    this.value = value;
            }
     
        }
     
    }