1 package org.collectionspace.services.jaxrs;
3 import static org.nuxeo.elasticsearch.ElasticSearchConstants.ES_ENABLED_PROPERTY;
5 import javax.servlet.ServletContextEvent;
6 import javax.ws.rs.core.PathSegment;
7 import javax.ws.rs.core.Response;
8 import javax.ws.rs.core.UriInfo;
10 import org.jboss.resteasy.core.Dispatcher;
11 import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap;
12 import org.jboss.resteasy.specimpl.PathSegmentImpl;
13 import org.collectionspace.authentication.AuthN;
14 import org.collectionspace.authentication.CSpaceTenant;
15 import org.collectionspace.services.account.Tenant;
16 import org.collectionspace.services.account.TenantResource;
17 import org.collectionspace.services.authorization.AuthZ;
18 import org.collectionspace.services.client.AbstractCommonListUtils;
19 import org.collectionspace.services.client.AuthorityClient;
20 import org.collectionspace.services.client.CollectionSpaceClient;
21 import org.collectionspace.services.client.PayloadOutputPart;
22 import org.collectionspace.services.client.PoxPayloadOut;
23 import org.collectionspace.services.client.ReportClient;
24 import org.collectionspace.services.client.workflow.WorkflowClient;
25 import org.collectionspace.services.common.CSWebApplicationException;
26 import org.collectionspace.services.common.ResourceMap;
27 import org.collectionspace.services.common.ServiceMain;
28 import org.collectionspace.services.common.api.RefName;
29 import org.collectionspace.services.common.config.ConfigUtils;
30 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
31 import org.collectionspace.services.common.query.UriInfoImpl;
32 import org.collectionspace.services.common.vocabulary.AuthorityResource;
34 import org.collectionspace.services.config.service.AuthorityInstanceType;
35 import org.collectionspace.services.config.service.ServiceBindingType;
36 import org.collectionspace.services.config.service.ServiceBindingType.AuthorityInstanceList;
37 import org.collectionspace.services.config.service.Term;
38 import org.collectionspace.services.config.service.TermList;
39 import org.collectionspace.services.config.tenant.TenantBindingType;
40 import org.collectionspace.services.config.types.PropertyItemType;
41 import org.collectionspace.services.config.types.PropertyType;
42 import org.collectionspace.services.jaxb.AbstractCommonList;
43 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
44 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
45 import org.collectionspace.services.report.ReportResource;
46 import org.nuxeo.elasticsearch.ElasticSearchComponent;
47 import org.nuxeo.elasticsearch.api.ElasticSearchService;
48 import org.nuxeo.runtime.api.Framework;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 import java.lang.reflect.Constructor;
55 import java.net.URLEncoder;
56 import java.nio.charset.StandardCharsets;
57 import java.nio.file.Files;
58 import java.util.Arrays;
59 import java.util.HashSet;
60 import java.util.Hashtable;
61 import java.util.List;
64 public class CSpaceResteasyBootstrap extends ResteasyBootstrap {
65 private static final Logger logger = LoggerFactory.getLogger(CSpaceResteasyBootstrap.class);
67 private static final String RESET_AUTHORITIES_PROPERTY = "org.collectionspace.services.authorities.reset";
68 private static final String RESET_ELASTICSEARCH_INDEX_PROPERTY = "org.collectionspace.services.elasticsearch.reset";
69 private static final String RESET_REPORTS_PROPERTY = "org.collectionspace.services.reports.reset";
70 private static final String QUICK_BOOT_PROPERTY = "org.collectionspace.services.quickboot";
71 private static final String REPORT_PROPERTY = "report";
74 public void contextInitialized(ServletContextEvent event) {
77 // This call to super instantiates and initializes our JAX-RS application class.
78 // The application class is org.collectionspace.services.jaxrs.CollectionSpaceJaxRsApplication.
80 logger.info("Starting up the CollectionSpace Services JAX-RS application.");
81 super.contextInitialized(event);
82 CollectionSpaceJaxRsApplication app = (CollectionSpaceJaxRsApplication)deployment.getApplication();
83 Dispatcher disp = deployment.getDispatcher();
84 disp.getDefaultContextObjects().put(ResourceMap.class, app.getResourceMap());
86 // Property can be set in the tomcat/bin/setenv.sh (or setenv.bat) file
87 String quickBoot = System.getProperty(QUICK_BOOT_PROPERTY, Boolean.FALSE.toString());
89 if (Boolean.valueOf(quickBoot) == false) {
90 // The below properties can be set in the tomcat/bin/setenv.sh (or setenv.bat) file.
91 String resetAuthsString = System.getProperty(RESET_AUTHORITIES_PROPERTY, Boolean.FALSE.toString());
92 String resetElasticsearchIndexString = System.getProperty(RESET_ELASTICSEARCH_INDEX_PROPERTY, Boolean.FALSE.toString());
93 String resetReportsString = System.getProperty(RESET_REPORTS_PROPERTY, Boolean.TRUE.toString());
95 initializeAuthorities(app.getResourceMap(), Boolean.valueOf(resetAuthsString));
97 if (Boolean.valueOf(resetElasticsearchIndexString) == true) {
98 resetElasticSearchIndex();
101 if (Boolean.valueOf(resetReportsString) == true) {
106 logger.info("CollectionSpace Services JAX-RS application started.");
107 } catch (Exception e) {
109 throw new RuntimeException(e);
115 public void contextDestroyed(ServletContextEvent event) {
116 logger.info("Shutting down the CollectionSpace Services JAX-RS application.");
117 //Do something if needed.
118 logger.info("CollectionSpace Services JAX-RS application stopped.");
121 public void resetReports() throws Exception {
122 logger.info("Resetting reports");
124 TenantBindingConfigReaderImpl tenantBindingConfigReader = ServiceMain.getInstance().getTenantBindingConfigReader();
125 Hashtable<String, TenantBindingType> tenantBindingsTable = tenantBindingConfigReader.getTenantBindings(false);
127 for (TenantBindingType tenantBinding : tenantBindingsTable.values()) {
128 ServiceBindingType reportServiceBinding = null;
130 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
131 if (serviceBinding.getName().toLowerCase().trim().equals(ReportClient.SERVICE_NAME)) {
132 reportServiceBinding = serviceBinding;
138 Set<String> reportNames = new HashSet<String>();
140 if (reportServiceBinding != null) {
141 for (PropertyType property : reportServiceBinding.getProperties()) {
142 for (PropertyItemType item : property.getItem()) {
143 if (item.getKey().equals(REPORT_PROPERTY)) {
144 reportNames.add(item.getValue());
150 if (reportNames.size() > 0) {
151 CSpaceTenant tenant = new CSpaceTenant(tenantBinding.getId(), tenantBinding.getName());
153 resetTenantReports(tenant, reportNames);
158 private void resetTenantReports(CSpaceTenant tenant, Set<String> reportNames) throws Exception {
159 logger.info("Resetting reports for tenant {}", tenant.getId());
161 AuthZ.get().login(tenant);
163 CollectionSpaceJaxRsApplication app = (CollectionSpaceJaxRsApplication) deployment.getApplication();
164 ResourceMap resourceMap = app.getResourceMap();
165 ReportResource reportResource = (ReportResource) resourceMap.get(ReportClient.SERVICE_NAME);
167 for (String reportName : reportNames) {
168 File reportMetadataFile = ReportResource.getReportMetadataFile(reportName);
170 if (!reportMetadataFile.exists()) {
172 "Metadata file not found for report {} at {}",
173 reportName, reportMetadataFile.getAbsolutePath());
178 String payload = new String(Files.readAllBytes(reportMetadataFile.toPath()));
179 String reportFilename = reportName + ".jrxml";
181 UriInfo uriInfo = new UriInfoImpl(
185 "pgSz=0&filename=" + URLEncoder.encode(reportFilename, StandardCharsets.UTF_8.toString()),
186 Arrays.asList((PathSegment) new PathSegmentImpl("", false))
189 AbstractCommonList list = reportResource.getList(uriInfo);
191 if (list.getTotalItems() == 0) {
192 logger.info("Adding report " + reportName);
195 reportResource.create(resourceMap, null, payload);
196 } catch(Exception e) {
197 logger.error(e.getMessage(), e);
200 for (ListItem item : list.getListItem()) {
201 String csid = AbstractCommonListUtils.ListItemGetCSID(item);
203 // Update an existing report iff:
204 // - it was created autmatically (i.e., by the SPRING_ADMIN user)
205 // - it was last updated automatically (i.e., by the SPRING_ADMIN user)
206 // - it is not soft-deleted
208 PoxPayloadOut reportPayload = reportResource.getResourceFromCsid(null, null, csid);
209 PayloadOutputPart corePart = reportPayload.getPart(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA);
211 String createdBy = corePart.asElement().selectSingleNode(CollectionSpaceClient.COLLECTIONSPACE_CORE_CREATED_BY).getText();
212 String updatedBy = corePart.asElement().selectSingleNode(CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_BY).getText();
213 String workflowState = corePart.asElement().selectSingleNode(CollectionSpaceClient.COLLECTIONSPACE_CORE_WORKFLOWSTATE).getText();
216 createdBy.equals(AuthN.SPRING_ADMIN_USER)
217 && updatedBy.equals(AuthN.SPRING_ADMIN_USER)
218 && !workflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)
220 logger.info("Updating report {} with csid {}", reportName, csid);
223 reportResource.update(resourceMap, null, csid, payload);
224 } catch (Exception e) {
225 logger.error(e.getMessage(), e);
229 "Not updating report {} with csid {} - it was not auto-created, or was updated or soft-deleted",
237 public void resetElasticSearchIndex() throws Exception {
238 boolean isEnabled = Boolean.parseBoolean(Framework.getProperty(ES_ENABLED_PROPERTY, "true"));
244 ElasticSearchComponent es = (ElasticSearchComponent) Framework.getService(ElasticSearchService.class);
246 for (String repositoryName : es.getRepositoryNames()) {
247 logger.info("Rebuilding Elasticsearch index for repository {}", repositoryName);
249 es.dropAndInitRepositoryIndex(repositoryName);
252 TenantBindingConfigReaderImpl tenantBindingConfigReader = ServiceMain.getInstance().getTenantBindingConfigReader();
253 Hashtable<String, TenantBindingType> tenantBindingsTable = tenantBindingConfigReader.getTenantBindings(false);
255 for (TenantBindingType tenantBinding : tenantBindingsTable.values()) {
256 CSpaceTenant tenant = new CSpaceTenant(tenantBinding.getId(), tenantBinding.getName());
258 AuthZ.get().login(tenant);
260 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
261 Boolean isElasticsearchIndexed = serviceBinding.isElasticsearchIndexed();
262 String servicesRepoDomainName = serviceBinding.getRepositoryDomain();
264 if (isElasticsearchIndexed && servicesRepoDomainName != null && servicesRepoDomainName.trim().isEmpty() == false) {
265 String repositoryName = ConfigUtils.getRepositoryName(tenantBinding, servicesRepoDomainName);
266 String docType = NuxeoUtils.getTenantQualifiedDocType(tenantBinding.getId(), serviceBinding.getObject().getName());
268 logger.info("Starting Elasticsearch reindexing for docType {} in repository {}", docType, repositoryName);
270 es.runReindexingWorker(repositoryName, String.format("SELECT ecm:uuid FROM %s", docType));
277 * Initialize all authorities and vocabularies defined in the service bindings.
281 public void initializeAuthorities(ResourceMap resourceMap, boolean reset) throws Exception {
282 TenantBindingConfigReaderImpl tenantBindingConfigReader = ServiceMain.getInstance().getTenantBindingConfigReader();
283 Hashtable<String, TenantBindingType> tenantBindingsTable = tenantBindingConfigReader.getTenantBindings(false);
284 for (TenantBindingType tenantBindings : tenantBindingsTable.values()) {
285 CSpaceTenant tenant = new CSpaceTenant(tenantBindings.getId(), tenantBindings.getName());
286 if (shouldInitializeAuthorities(tenant, reset) == true) {
287 logger.info("Initializing vocabularies and authorities of tenant '{}'.", tenant.getId());
288 for (ServiceBindingType serviceBinding : tenantBindings.getServiceBindings()) {
289 AuthorityInstanceList element = serviceBinding.getAuthorityInstanceList();
290 if (element != null && element.getAuthorityInstance() != null) {
291 List<AuthorityInstanceType> authorityInstanceList = element.getAuthorityInstance();
292 for (AuthorityInstanceType authorityInstance : authorityInstanceList) {
294 initializeAuthorityInstance(resourceMap, authorityInstance, serviceBinding, tenant, reset);
295 } catch (Exception e) {
296 logger.error("Could not initialize authorities and authority terms: " + e.getMessage());
303 // If we made it this far, we've either created the tenant's authorities and terms or we've reset them. Either way,
304 // we should mark the isAuthoritiesInitialized field of the tenant to 'true'.
306 setAuthoritiesInitialized(tenant, true);
311 @SuppressWarnings("rawtypes")
312 private AuthorityClient getAuthorityClient(String classname) throws Exception {
313 Class clazz = Class.forName(classname.trim());
314 Constructor co = clazz.getConstructor(null);
315 Object classInstance = co.newInstance(null);
316 return (AuthorityClient) classInstance;
319 private boolean shouldInitializeAuthorities(CSpaceTenant cspaceTenant, boolean reset) {
320 AuthZ.get().login(); // login as super admin
321 TenantResource tenantResource = new TenantResource();
322 Tenant tenantState = tenantResource.getTenant(cspaceTenant.getId());
325 // If the tenant's authorities have been initialized and
326 // we're not being asked to reset them, we'll return 'false'
327 // making any changes
329 return tenantState.isAuthoritiesInitialized() == false || reset == true;
332 private void setAuthoritiesInitialized(CSpaceTenant cspaceTenant, boolean initState) {
333 AuthZ.get().login(); // login as super admin
334 TenantResource tenantResource = new TenantResource();
335 Tenant tenantState = tenantResource.getTenant(cspaceTenant.getId());
337 tenantState.setAuthoritiesInitialized(initState);
338 tenantResource.updateTenant(cspaceTenant.getId(), tenantState);
343 * Check to see if an an authority instance and its corresponding terms exist. If not, try to create them.
345 private void initializeAuthorityInstance(ResourceMap resourceMap,
346 AuthorityInstanceType authorityInstance,
347 ServiceBindingType serviceBinding,
348 CSpaceTenant cspaceTenant,
349 boolean reset) throws Exception {
351 Response response = null;
352 String serviceName = serviceBinding.getName();
354 AuthZ.get().login(cspaceTenant);
355 String clientClassName = serviceBinding.getClientHandler();
356 AuthorityClient client = getAuthorityClient(clientClassName);
357 String authoritySpecifier = RefName.shortIdToPath(authorityInstance.getTitleRef()); // e.g., urn:cspace:name(ulan)
360 // Test to see if the authority instance exists already.
362 AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(serviceName.toLowerCase());
364 response = authorityResource.get(null, null, null, authoritySpecifier);
365 } catch (CSWebApplicationException e) {
366 response = e.getResponse(); // If the authority doesn't exist, we expect a 404 error
370 // If it doesn't exist (status is not 200), then try to create the authority instance
372 status = response.getStatus();
373 if (status != Response.Status.OK.getStatusCode()) {
374 String xmlPayload = client.createAuthorityInstance(authorityInstance.getTitleRef(), authorityInstance.getTitle());
375 response = authorityResource.createAuthority(xmlPayload);
376 status = response.getStatus();
377 if (status != Response.Status.CREATED.getStatusCode()) {
378 throw new CSWebApplicationException(response);
382 if (status == Response.Status.OK.getStatusCode()) {
383 logger.debug("Authority of type '{}' with the short ID of '{}' existed already.",
384 serviceName, authorityInstance.getTitleRef());
385 } else if (status == Response.Status.CREATED.getStatusCode()) {
386 logger.debug("Created a new authority of type '{}' with the short ID of '{}'.",
387 serviceName, authorityInstance.getTitleRef());
389 logger.warn("Unknown status '{}' encountered when creating or fetching authority of type '{}' with the short ID of '{}'.",
390 status, serviceName, authorityInstance.getTitleRef());
394 // Next, try to create or verify the authority terms.
396 initializeAuthorityInstanceTerms(authorityResource, client, authoritySpecifier, resourceMap, authorityInstance, serviceName, cspaceTenant);
399 private void initializeAuthorityInstanceTerms(
400 AuthorityResource authorityResource,
401 AuthorityClient client,
402 String authoritySpecifier,
403 ResourceMap resourceMap,
404 AuthorityInstanceType authorityInstance,
406 CSpaceTenant tenant) throws Exception {
409 Response response = null;
411 TermList termListElement = authorityInstance.getTermList();
412 if (termListElement == null) {
416 for (Term term : termListElement.getTerm()) {
418 // Check to see if the term already exists
421 String termSpecifier = RefName.shortIdToPath(term.getId());
422 authorityResource.getAuthorityItem(null, null, resourceMap, authoritySpecifier, termSpecifier);
423 status = Response.Status.OK.getStatusCode();
424 } catch (CSWebApplicationException e) {
425 response = e.getResponse(); // If the authority doesn't exist, we expect a 404 error
426 status = response.getStatus();
430 // If the term doesn't exist, create it.
432 if (status != Response.Status.OK.getStatusCode()) {
433 String termShortId = term.getId();
434 String termDisplayName = term.getContent().trim();
435 String xmlPayload = client.createAuthorityItemInstance(termShortId, termDisplayName);
437 authorityResource.createAuthorityItem(resourceMap, null, authoritySpecifier, xmlPayload);
438 logger.debug("Tenant:{}:Created a new term '{}:{}' in the authority of type '{}' with the short ID of '{}'.",
439 tenant.getName(), termDisplayName, termShortId, serviceName, authorityInstance.getTitleRef());
440 } catch (CSWebApplicationException e) {
441 response = e.getResponse();
442 status = response.getStatus();
443 if (status != Response.Status.CREATED.getStatusCode()) {
444 throw new CSWebApplicationException(response);