/***************************************************************** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. ****************************************************************/ package org.apache.cayenne.configuration.server; import org.apache.cayenne.ConfigurationException; import org.apache.cayenne.DataChannel; import org.apache.cayenne.DataChannelFilter; import org.apache.cayenne.access.DataDomain; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.DataRowStoreFactory; import org.apache.cayenne.access.types.ValueObjectTypeRegistry; import org.apache.cayenne.cache.NestedQueryCache; import org.apache.cayenne.cache.QueryCache; import org.apache.cayenne.configuration.ConfigurationTree; import org.apache.cayenne.configuration.Constants; import org.apache.cayenne.configuration.DataChannelDescriptor; import org.apache.cayenne.configuration.DataChannelDescriptorLoader; import org.apache.cayenne.configuration.DataChannelDescriptorMerger; import org.apache.cayenne.configuration.DataNodeDescriptor; import org.apache.cayenne.configuration.RuntimeProperties; import org.apache.cayenne.di.Inject; import org.apache.cayenne.di.Injector; import org.apache.cayenne.di.Provider; import org.apache.cayenne.event.EventManager; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.EntitySorter; import org.apache.cayenne.resource.Resource; import org.apache.cayenne.resource.ResourceLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.List; /** * A {@link DataChannel} provider that provides a single instance of DataDomain * configured per configuration supplied via injected * {@link DataChannelDescriptorLoader}. * * @since 3.1 */ public class DataDomainProvider implements Provider<DataDomain> { private static Logger logger = LoggerFactory.getLogger(DataDomainProvider.class); @Inject protected ResourceLocator resourceLocator; @Inject protected DataChannelDescriptorMerger descriptorMerger; @Inject protected DataChannelDescriptorLoader loader; @Inject(Constants.SERVER_DOMAIN_FILTERS_LIST) protected List<DataChannelFilter> filters; @Inject(Constants.SERVER_DOMAIN_LISTENERS_LIST) protected List<Object> listeners; @Inject(Constants.SERVER_PROJECT_LOCATIONS_LIST) protected List<String> locations; @Inject protected Injector injector; @Inject protected QueryCache queryCache; @Inject protected RuntimeProperties runtimeProperties; @Inject protected DataNodeFactory dataNodeFactory; @Inject protected ValueObjectTypeRegistry valueObjectTypeRegistry; @Override public DataDomain get() throws ConfigurationException { try { return createAndInitDataDomain(); } catch (ConfigurationException e) { throw e; } catch (Exception e) { String causeMessage = e.getMessage(); String message = causeMessage != null && causeMessage.length() > 0 ? causeMessage : e.getClass().getName(); throw new DataDomainLoadException("DataDomain startup failed: %s", e, message); } } protected DataDomain createDataDomain(String name) { return new DataDomain(name); } protected DataDomain createAndInitDataDomain() throws Exception { DataChannelDescriptor descriptor = loadDescriptor(); DataDomain dataDomain = createDataDomain(descriptor.getName()); dataDomain.setMaxIdQualifierSize(runtimeProperties.getInt(Constants.SERVER_MAX_ID_QUALIFIER_SIZE_PROPERTY, -1)); dataDomain.setQueryCache(new NestedQueryCache(queryCache)); dataDomain.setEntitySorter(injector.getInstance(EntitySorter.class)); dataDomain.setEventManager(injector.getInstance(EventManager.class)); dataDomain.setDataRowStoreFactory(injector.getInstance(DataRowStoreFactory.class)); dataDomain.initWithProperties(descriptor.getProperties()); for (DataMap dataMap : descriptor.getDataMaps()) { dataDomain.addDataMap(dataMap); } dataDomain.getEntityResolver().applyDBLayerDefaults(); dataDomain.getEntityResolver().applyObjectLayerDefaults(); dataDomain.getEntityResolver().setValueObjectTypeRegistry(valueObjectTypeRegistry); for (DataNodeDescriptor nodeDescriptor : descriptor.getNodeDescriptors()) { addDataNode(dataDomain, nodeDescriptor); } // init default node DataNode defaultNode = null; if (descriptor.getDefaultNodeName() != null) { defaultNode = dataDomain.getDataNode(descriptor.getDefaultNodeName()); } if (defaultNode == null) { Collection<DataNode> allNodes = dataDomain.getDataNodes(); if (allNodes.size() == 1) { defaultNode = allNodes.iterator().next(); } } if (defaultNode != null) { logger.info("setting DataNode '" + defaultNode.getName() + "' as default, used by all unlinked DataMaps"); dataDomain.setDefaultNode(defaultNode); } for (DataChannelFilter filter : filters) { dataDomain.addFilter(filter); } for (Object listener : listeners) { dataDomain.addListener(listener); } return dataDomain; } /** * @since 4.0 */ protected DataChannelDescriptor loadDescriptor() { DataChannelDescriptor descriptor = locations.isEmpty() ? new DataChannelDescriptor() : loadDescriptorFromConfigs(); String nameOverride = runtimeProperties.get(Constants.SERVER_DOMAIN_NAME_PROPERTY); if (nameOverride != null) { descriptor.setName(nameOverride); } return descriptor; } /** * @since 4.0 */ protected DataNode addDataNode(DataDomain dataDomain, DataNodeDescriptor nodeDescriptor) throws Exception { DataNode dataNode = dataNodeFactory.createDataNode(nodeDescriptor); // DataMaps for (String dataMapName : nodeDescriptor.getDataMapNames()) { dataNode.addDataMap(dataDomain.getDataMap(dataMapName)); } dataDomain.addNode(dataNode); return dataNode; } private DataChannelDescriptor loadDescriptorFromConfigs() { long t0 = System.currentTimeMillis(); if (logger.isDebugEnabled()) { logger.debug("starting configuration loading: " + locations); } DataChannelDescriptor[] descriptors = new DataChannelDescriptor[locations.size()]; for (int i = 0; i < locations.size(); i++) { String location = locations.get(i); Collection<Resource> configurations = resourceLocator.findResources(location); if (configurations.isEmpty()) { throw new DataDomainLoadException("Configuration resource \"%s\" is not found.", location); } Resource configurationResource = configurations.iterator().next(); // no support for multiple configs yet, but this is not a hard error if (configurations.size() > 1) { logger.info("found " + configurations.size() + " configurations for " + location + ", will use the first one: " + configurationResource.getURL()); } ConfigurationTree<DataChannelDescriptor> tree = loader.load(configurationResource); if (!tree.getLoadFailures().isEmpty()) { // TODO: andrus 03/10/2010 - log the errors before throwing? throw new DataDomainLoadException(tree, "Error loading DataChannelDescriptor"); } descriptors[i] = tree.getRootNode(); } long t1 = System.currentTimeMillis(); if (logger.isDebugEnabled()) { logger.debug("finished configuration loading in " + (t1 - t0) + " ms."); } return descriptorMerger.merge(descriptors); } }