001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.component;
018
019import java.io.ByteArrayInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022
023import org.apache.camel.Component;
024import org.apache.camel.api.management.ManagedAttribute;
025import org.apache.camel.api.management.ManagedOperation;
026import org.apache.camel.api.management.ManagedResource;
027import org.apache.camel.api.management.mbean.ManagedResourceEndpointMBean;
028import org.apache.camel.converter.IOConverter;
029import org.apache.camel.impl.ProcessorEndpoint;
030import org.apache.camel.spi.Metadata;
031import org.apache.camel.spi.UriParam;
032import org.apache.camel.spi.UriPath;
033import org.apache.camel.util.IOHelper;
034import org.apache.camel.util.ResourceHelper;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038/**
039 * A useful base class for endpoints which depend on a resource
040 * such as things like Velocity or XQuery based components.
041 */
042@ManagedResource(description = "Managed ResourceEndpoint")
043public abstract class ResourceEndpoint extends ProcessorEndpoint implements ManagedResourceEndpointMBean {
044    protected final Logger log = LoggerFactory.getLogger(getClass());
045    private volatile byte[] buffer;
046
047    @UriPath(description = "Path to the resource."
048        + " You can prefix with: classpath, file, http, ref, or bean."
049        + " classpath, file and http loads the resource using these protocols (classpath is default)."
050        + " ref will lookup the resource in the registry."
051        + " bean will call a method on a bean to be used as the resource."
052        + " For bean you can specify the method name after dot, eg bean:myBean.myMethod.")
053    @Metadata(required = "true")
054    private String resourceUri;
055    @UriParam(defaultValue = "false", description = "Sets whether to use resource content cache or not")
056    private boolean contentCache;
057    @UriParam(defaultValue = "false", description = "Sets whether the context map should allow access to all details."
058            + " By default only the message body and headers can be accessed."
059            + " This option can be enabled for full access to the current Exchange and CamelContext."
060            + " Doing so impose a potential security risk as this opens access to the full power of CamelContext API.")
061    private boolean allowContextMapAll;
062
063    public ResourceEndpoint() {
064    }
065
066    public ResourceEndpoint(String endpointUri, Component component, String resourceUri) {
067        super(endpointUri, component);
068        this.resourceUri = resourceUri;
069    }
070
071    /**
072     * Gets the resource as an input stream considering the cache flag as well.
073     * <p/>
074     * If cache is enabled then the resource content is cached in an internal buffer and this content is
075     * returned to avoid loading the resource over and over again.
076     *
077     * @return the input stream
078     * @throws IOException is thrown if error loading the content of the resource to the local cache buffer
079     */
080    public InputStream getResourceAsInputStream() throws IOException {
081        // try to get the resource input stream
082        InputStream is;
083        if (isContentCache()) {
084            synchronized (this) {
085                if (buffer == null) {
086                    log.debug("Reading resource: {} into the content cache", resourceUri);
087                    is = getResourceAsInputStreamWithoutCache();
088                    buffer = IOConverter.toBytes(is);
089                    IOHelper.close(is, resourceUri, log);
090                }
091            }
092            log.debug("Using resource: {} from the content cache", resourceUri);
093            return new ByteArrayInputStream(buffer);
094        }
095
096        return getResourceAsInputStreamWithoutCache();
097    }
098
099    protected InputStream getResourceAsInputStreamWithoutCache() throws IOException {
100        return loadResource(resourceUri);
101    }
102
103    /**
104     * Loads the given resource.
105     *
106     * @param uri uri of the resource.
107     * @return the loaded resource
108     * @throws IOException is thrown if resource is not found or cannot be loaded
109     */
110    protected InputStream loadResource(String uri) throws IOException {
111        return ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(), uri);
112    }
113
114    @ManagedAttribute(description = "Whether the resource is cached")
115    public boolean isContentCache() {
116        return contentCache;
117    }
118
119    @ManagedOperation(description = "Clears the cached resource, forcing to re-load the resource on next request")
120    public void clearContentCache() {
121        log.debug("Clearing resource: {} from the content cache", resourceUri);
122        buffer = null;
123    }
124
125    public boolean isContentCacheCleared() {
126        return buffer == null;
127    }
128
129    @ManagedAttribute(description = "Whether the context map is limited to only include the message body and headers")
130    public boolean isAllowContextMapAll() {
131        return allowContextMapAll;
132    }
133
134    /**
135     * Sets whether the context map should allow access to all details.
136     * By default only the message body and headers can be accessed.
137     * This option can be enabled for full access to the current Exchange and CamelContext.
138     * Doing so impose a potential security risk as this opens access to the full power of CamelContext API.
139     */
140    public void setAllowContextMapAll(boolean allowContextMapAll) {
141        this.allowContextMapAll = allowContextMapAll;
142    }
143
144    @ManagedAttribute(description = "Camel context ID")
145    public String getCamelId() {
146        return getCamelContext().getName();
147    }
148
149    @ManagedAttribute(description = "Camel ManagementName")
150    public String getCamelManagementName() {
151        return getCamelContext().getManagementName();
152    }
153
154    @ManagedAttribute(description = "Endpoint service state")
155    public String getState() {
156        return getStatus().name();
157    }
158
159    /**
160     * Sets whether to use resource content cache or not.
161     */
162    public void setContentCache(boolean contentCache) {
163        this.contentCache = contentCache;
164    }
165
166    public String getResourceUri() {
167        return resourceUri;
168    }
169
170    /**
171     * Path to the resource.
172     * <p/>
173     * You can prefix with: classpath, file, http, ref, or bean.
174     * classpath, file and http loads the resource using these protocols (classpath is default).
175     * ref will lookup the resource in the registry.
176     * bean will call a method on a bean to be used as the resource.
177     * For bean you can specify the method name after dot, eg bean:myBean.myMethod
178     *
179     * @param resourceUri  the resource path
180     */
181    public void setResourceUri(String resourceUri) {
182        this.resourceUri = resourceUri;
183    }
184}