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
)
/repo/path/subreport.jrxml
repo:/repo/path/subreport.jrxml
subreport-attached.jrxml
repo:subreport-attached.jrxml
I also tried the following with the above uri variants as found elsewhere in the web without success:
JRXmlLoader:
JRXmlLoader.load(
new DefaultRepositoryService(
DefaultJasperReportsContext.getInstance()
).getInputStream( subrepPath )
RepositoryUtil:
RepositoryUtil.getInstance(
DefaultJasperReportsContext.getInstance()
).getInputStreamFromLocation( subrepPath )
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.
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 Jaspersoft Community Wiki page shows some sample code with a FolderHelper and an ExtendedUserFolderProcessor
you could completely add these 2 classes to your project
for compact code on the calling code we additionally added this getExecCtx()
method to ExtendedUserFolderProcessor
:
public ExecutionContext getExecCtx() {
return new FolderHelper( getRepositoryService() , getObjectPermissionService() , getUserAuthorityService() )
.getAdminContext() ;
}
then use the following code to get the ExecutionContext
:
new ExtendedUserFolderProcessor().getExecCtx()
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.
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;
}
}
}