From c28693e484537e803e777f61518d922b22c83db3 Mon Sep 17 00:00:00 2001 From: Brian Rosenberger Date: Mon, 24 Aug 2015 07:57:03 +0000 Subject: [PATCH] Added Serena Dimensions CM RESTful web service to list items from a project git-svn-id: https://brutex.net/svn/xservices/trunk@175 e7e49efb-446e-492e-b9ec-fcafc1997a86 --- src/java/cache.ccf | 13 + src/java/log4j.properties | 1 + .../brutex/xservices/cmtypes/ItemType.java | 167 ++++++++ .../xservices/cmtypes/ItemTypeList.java | 38 ++ .../net/brutex/xservices/ws/rs/DIMCMInfo.java | 80 ++++ .../brutex/xservices/ws/rs/DIMCMInfoImpl.java | 371 ++++++++++++++++++ src/java/shiro.ini | 6 +- web/WEB-INF/cxf-beans.xml | 8 + 8 files changed, 683 insertions(+), 1 deletion(-) create mode 100644 src/java/net/brutex/xservices/cmtypes/ItemType.java create mode 100644 src/java/net/brutex/xservices/cmtypes/ItemTypeList.java create mode 100644 src/java/net/brutex/xservices/ws/rs/DIMCMInfo.java create mode 100644 src/java/net/brutex/xservices/ws/rs/DIMCMInfoImpl.java diff --git a/src/java/cache.ccf b/src/java/cache.ccf index 3ba1130..1a90aaa 100644 --- a/src/java/cache.ccf +++ b/src/java/cache.ccf @@ -37,6 +37,19 @@ jcs.region.CVSCache.cacheattributes.MaxSpoolPerRun=5 jcs.region.CVSCache.elementattributes=org.apache.jcs.engine.ElementAttributes jcs.region.CVSCache.elementattributes.IsEternal=false +jcs.region.DIMCM=DC +jcs.region.DIMCM.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes +jcs.region.DIMCM.cacheattributes.MaxObjects=50 +jcs.region.DIMCM.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache +jcs.region.DIMCM.cacheattributes.UseMemoryShrinker=true +jcs.region.DIMCM.cacheattributes.MaxMemoryIdleTimeSeconds=3600 +jcs.region.DIMCM.cacheattributes.ShrinkerIntervalSeconds=60 +jcs.region.DIMCM.cacheattributes.MaxSpoolPerRun=5 +jcs.region.DIMCM.elementattributes=org.apache.jcs.engine.ElementAttributes +jcs.region.DIMCM.elementattributes.IsEternal=false + + + # AVAILABLE AUXILIARY CACHES jcs.auxiliary.DC=org.apache.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory jcs.auxiliary.DC.attributes=org.apache.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes diff --git a/src/java/log4j.properties b/src/java/log4j.properties index 23c393e..189173f 100644 --- a/src/java/log4j.properties +++ b/src/java/log4j.properties @@ -9,6 +9,7 @@ log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c{2} - %m%n # Print only messages of level WARN or above in the package com.foo. log4j.logger.net.brutex.xservices=INFO log4j.logger.net.brutex.xservices.ws.rs=DEBUG +log4j.logger.net.brutex.xservices.security=DEBUG log4j.logger.org.springframework=INFO diff --git a/src/java/net/brutex/xservices/cmtypes/ItemType.java b/src/java/net/brutex/xservices/cmtypes/ItemType.java new file mode 100644 index 0000000..7d0f5d4 --- /dev/null +++ b/src/java/net/brutex/xservices/cmtypes/ItemType.java @@ -0,0 +1,167 @@ +/* + * Copyright 2014 Brian Rosenberger (Brutex Network) + * + * Licensed 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 net.brutex.xservices.cmtypes; + +import java.net.URL; + +import javax.xml.bind.annotation.XmlType; + +@XmlType(name="Item") +public class ItemType { + private String longFilename; + private String shortFilename; + private String dirName; + private String createdDate; + private String createdUser; + private String object_id; + private String object_uid; + private String object_spec_uid; + private String object_spec; + private String itemFormat; + private String updatedDate; + private String updatedUser; + + + /* + SystemAttributes.FULL_PATH_NAME, + SystemAttributes.ITEMFILE_DIR, + SystemAttributes.ITEMFILE_FILENAME, + SystemAttributes.ITEMFILE_DIR, + SystemAttributes.OBJECT_SPEC, + SystemAttributes.OBJECT_ID, + SystemAttributes.OBJECT_SPEC_UID, + SystemAttributes.OBJECT_UID, + SystemAttributes.CREATION_DATE, + SystemAttributes.CREATION_USER, + SystemAttributes.ITEM_FORMAT, + SystemAttributes.LAST_UPDATED_DATE, + SystemAttributes.LAST_UPDATED_USER + + */ + + public String getDirName() { + return dirName; + } + + public void setDirName(String dirName) { + this.dirName = dirName; + } + + public String getCreatedUser() { + return createdUser; + } + + public void setCreatedUser(String createdUser) { + this.createdUser = createdUser; + } + + public String getItemFormat() { + return itemFormat; + } + + public void setItemFormat(String itemFormat) { + this.itemFormat = itemFormat; + } + + public String getUpdatedDate() { + return updatedDate; + } + + public void setUpdatedDate(String updatedDate) { + this.updatedDate = updatedDate; + } + + public String getUpdatedUser() { + return updatedUser; + } + + public void setUpdatedUser(String updatedUser) { + this.updatedUser = updatedUser; + } + + public String getObject_uid() { + return object_uid; + } + + public void setObject_uid(String object_uid) { + this.object_uid = object_uid; + } + + public String getObject_spec_uid() { + return object_spec_uid; + } + + public void setObject_spec_uid(String object_spec_uid) { + this.object_spec_uid = object_spec_uid; + } + + public String getObject_spec() { + return object_spec; + } + + public void setObject_spec(String object_spec) { + this.object_spec = object_spec; + } + private URL url; + + public URL getUrl() { + return url; + } + + public void setUrl(URL url) { + this.url = url; + } + + public String getObject_id() { + return object_id; + } + public void setObject_id(String object_id) { + this.object_id = object_id; + } + + public String getLongFilename() { + return longFilename; + } + public void setLongFilename(String longFilename) { + this.longFilename = longFilename; + } + public String getShortFilename() { + return shortFilename; + } + public void setShortFilename(String shortFilename) { + this.shortFilename = shortFilename; + } + + public String getCreatedDate() { + return createdDate; + } + public void setCreatedDate(String createdDate) { + this.createdDate = createdDate; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(longFilename); + sb.append(";"); + sb.append(" ("); + sb.append(shortFilename); + sb.append("), related as "); + sb.append(", created at "); + sb.append(createdDate); + return sb.toString(); + } +} diff --git a/src/java/net/brutex/xservices/cmtypes/ItemTypeList.java b/src/java/net/brutex/xservices/cmtypes/ItemTypeList.java new file mode 100644 index 0000000..29e6a5d --- /dev/null +++ b/src/java/net/brutex/xservices/cmtypes/ItemTypeList.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Brian Rosenberger (Brutex Network) + * + * Licensed 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 net.brutex.xservices.cmtypes; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class ItemTypeList implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 2662597594771167080L; + + + @XmlElement(name="ItemRevision") + public List list = new ArrayList<>(); + +} diff --git a/src/java/net/brutex/xservices/ws/rs/DIMCMInfo.java b/src/java/net/brutex/xservices/ws/rs/DIMCMInfo.java new file mode 100644 index 0000000..a0a24fc --- /dev/null +++ b/src/java/net/brutex/xservices/ws/rs/DIMCMInfo.java @@ -0,0 +1,80 @@ +/* + * Copyright 2014 Brian Rosenberger (Brutex Network) + * + * Licensed 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 net.brutex.xservices.ws.rs; + +import java.io.File; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.jcs.access.exception.CacheException; + +import net.brutex.xservices.ws.XServicesFault; + + +/** + * The Dim CM Browsing Rest Service. + * + * @author Brian Rosenberger, bru(at)brutex.de + */ + +@Path("/") +@Produces({ "text/xml" }) +public abstract interface DIMCMInfo { + + public final static String BASE_PATH = "/DIMCMService/"; + public final static String SERVICE_NAME = "DIMCMInfoService"; + + /** + * Get the file/ directory listing. + * + * @param paramHttpHeaders the param http headers + * @param uriInfo request url info + * @param directory The directory to list. + * @param includeDirectories Whether or not to include directories in the listing. Default is true. + * @param includeFiles Whether or not to include files in the listing. Default is true. + * @param depth Include subdirectories down to a given depth. Default is 1. + * @param search Additional "Glob search pattern" for the file/ directory name. I.e. '*.log' + * @param itemsPerPage How many items to return with one call. Default is 50. + * @param page Paging support. Default is 1. + * @param useCache whether or not to use cache. Defaults to true. + * @return the FileInfo Set as an XML structure + * @throws CacheException + */ + @GET + @Path("getItems/") + public abstract Response getFiles( + @Context HttpHeaders paramHttpHeaders, + @Context UriInfo uriInfo, + @QueryParam("projSpec") String project, + @QueryParam("directory") String directory, + @QueryParam("recursive") @DefaultValue("false") boolean recursive, + @QueryParam("includeFiles") @DefaultValue("1") boolean includeFiles, + @QueryParam("depth") @DefaultValue("1") int depth, + @QueryParam("search") String search, + @QueryParam("itemsPerPage") @DefaultValue("50") int itemsPerPage, + @QueryParam("page") @DefaultValue("1") int page, + @QueryParam("usecache") @DefaultValue("true") boolean useCache) throws CacheException; + +} diff --git a/src/java/net/brutex/xservices/ws/rs/DIMCMInfoImpl.java b/src/java/net/brutex/xservices/ws/rs/DIMCMInfoImpl.java new file mode 100644 index 0000000..b48aa2a --- /dev/null +++ b/src/java/net/brutex/xservices/ws/rs/DIMCMInfoImpl.java @@ -0,0 +1,371 @@ +/* + * Copyright 2014 Brian Rosenberger (Brutex Network) + * + * Licensed 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 net.brutex.xservices.ws.rs; + + + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.file.FileVisitOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import net.brutex.xservices.cmtypes.ItemType; +import net.brutex.xservices.cmtypes.ItemTypeList; +import net.brutex.xservices.types.FileInfoType; +import net.brutex.xservices.util.FileWalker; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.jcs.JCS; +import org.apache.jcs.access.exception.CacheException; +import org.apache.log4j.Logger; + + +/* + * The Serena Dimensions CM Java API is required for these imports. + * The API is not included in this package due to copyright reasons, + * please get Dimensions CM from Serena Software Inc., Evaluation versions + * are available from http://www.serena.com + * + * required Jars: + * serena.darius-14.1.jar + * serena.dmclient-14.1.jar + * serena.dmfile-14.1.jar + * serena.dmnet-14.1.jar + * serena.dmtpi-14.1.jar + * + */ + +import com.serena.dmclient.api.BulkOperator; +import com.serena.dmclient.api.DimensionsConnection; +import com.serena.dmclient.api.DimensionsConnectionDetails; +import com.serena.dmclient.api.DimensionsConnectionManager; +import com.serena.dmclient.api.ItemRevision; +import com.serena.dmclient.api.Project; +import com.serena.dmclient.api.RepositoryFolder; +import com.serena.dmclient.api.SystemAttributes; + +/** + * The Class FileInfoImpl. + * + * @author Brian Rosenberger, bru(at)brutex.de + */ +public class DIMCMInfoImpl implements DIMCMInfo { + + Logger logger = Logger.getLogger(DIMCMInfoImpl.class); + + /* + * (non-Javadoc) + * + * @see + * net.brutex.xservices.ws.rs.FileInfo#getFiles(javax.ws.rs.core.HttpHeaders + * , java.lang.String, boolean, boolean, int, java.lang.String, int, int) + */ + public Response getFiles(HttpHeaders h, UriInfo uriInfo, String projSpec, + String directory, boolean recursive, boolean withFiles, int level, + String search, int count, int page, boolean useCache) throws CacheException { + + + /* + * try to hit cache first + */ + JCS cache = JCS.getInstance("DIMCM"); + String cachekey = projSpec + directory + String.valueOf(recursive); + if(useCache) { + ItemTypeList cacheresult = (ItemTypeList) cache.get(cachekey); + if(cacheresult != null) return Response.ok(cacheresult).build(); + } + + //Reject when project has not the form "PRODUCT:PROJECT" + if(! projSpec.contains(":")) return Response.noContent().build(); + + Project project = getDIMCMConnection().getObjectFactory().getProject(projSpec); + RepositoryFolder folder = null; + + if (directory == null) { + folder = project.getRootFolder(); + } else { + while(directory.startsWith("/") || directory.startsWith("\\")) { + directory = directory.substring(1); + } + if(directory.equals("")) { + folder = project.getRootFolder(); + } else { + folder = project.findRepositoryFolderByPath(directory); + } + } + + + ItemTypeList resultlist = new ItemTypeList(); + resultlist.list = getItems(folder, recursive); + if(cache!=null) cache.put(cachekey, resultlist); + + //does this help? + DimensionsConnectionManager.unregisterThreadConnection(); + + return Response.ok(resultlist).build(); + + } + + + + + List getItems(RepositoryFolder f, boolean recursive) { + DimensionsConnection conn = getDIMCMConnection(); + List result = new ArrayList<>(); + + /* get Items from current folder */ + /* latest revision only */ + List revisions = f.getLatestItemRevisions(); + + int[] attr = { SystemAttributes.FULL_PATH_NAME, + SystemAttributes.ITEMFILE_DIR, + SystemAttributes.ITEMFILE_FILENAME, + SystemAttributes.ITEMFILE_DIR, SystemAttributes.OBJECT_SPEC, + SystemAttributes.OBJECT_ID, SystemAttributes.OBJECT_SPEC_UID, + SystemAttributes.OBJECT_UID, SystemAttributes.CREATION_DATE, + SystemAttributes.CREATION_USER, SystemAttributes.ITEM_FORMAT, + SystemAttributes.LAST_UPDATED_DATE, + SystemAttributes.LAST_UPDATED_USER }; + BulkOperator bulk = conn.getObjectFactory().getBulkOperator(revisions); + bulk.queryAttribute(attr); + + // Copy into JAXB object + for (ItemRevision r : revisions) { + ItemType item = new ItemType(); + item.setLongFilename((String) r + .getAttribute(SystemAttributes.FULL_PATH_NAME)); + item.setDirName((String) r + .getAttribute(SystemAttributes.ITEMFILE_DIR)); + item.setShortFilename((String) r + .getAttribute(SystemAttributes.ITEMFILE_FILENAME)); + item.setObject_id((String) r + .getAttribute(SystemAttributes.OBJECT_ID)); + item.setObject_uid((String.valueOf(r + .getAttribute(SystemAttributes.OBJECT_UID)))); + item.setObject_spec((String) r + .getAttribute(SystemAttributes.OBJECT_SPEC)); + item.setObject_spec_uid(String.valueOf(r + .getAttribute(SystemAttributes.OBJECT_SPEC_UID))); + item.setObject_spec_uid(String.valueOf(r + .getAttribute(SystemAttributes.OBJECT_SPEC_UID))); + item.setCreatedDate(String.valueOf(r + .getAttribute(SystemAttributes.CREATION_DATE))); + item.setCreatedUser(String.valueOf(r + .getAttribute(SystemAttributes.CREATION_USER))); + item.setItemFormat(String.valueOf(r + .getAttribute(SystemAttributes.ITEM_FORMAT))); + item.setUpdatedDate(String.valueOf(r + .getAttribute(SystemAttributes.LAST_UPDATED_DATE))); + item.setUpdatedUser(String.valueOf(r + .getAttribute(SystemAttributes.LAST_UPDATED_USER))); + + try { + item.setUrl(new URL(getBaseURL() + + "?jsp=api&command=openi&object_id=" + + item.getObject_spec() + "&DB_CONN=" + + conn.getConnectionDetails().getDbConn() + "&DB_NAME=" + + conn.getConnectionDetails().getDbName())); + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + result.add(item); + } + + /* + * for recursive add other folders + */ + if(recursive) { + List folders = f.getAllChildFolders(); + for(RepositoryFolder ff : folders) { + result.addAll(getItems(ff, false)); + } + } + + return result; + } + + /** + * Sets the directory. + * + * @param list + * the list + * @param dir + * the dir + * @param withDirectories + * the with directories + * @param withFiles + * the with files + * @param depth + * the depth + * @param search + * the search + */ + private void setDirectory(final URI baseuri, final List list, + File dir, boolean withDirectories, boolean withFiles, + final int depth, String search) { + if (depth <= 0) + return; + + if (search == null || search.equals("")) { + search = "*"; + logger.info("No search pattern supplied, using default '*'."); + } + + FileWalker finder = new FileWalker(search); + try { + Files.walkFileTree(dir.toPath(), + EnumSet.of(FileVisitOption.FOLLOW_LINKS), depth, finder); + logger.info("FileWalker returned '" + finder.getCount() + + "' hits. '" + finder.getTotal() + + "' files have been scanned."); + List result = finder.getResult(); + for (Path f : result) { + if (!withDirectories) { + if (f.toFile().isDirectory()) + continue; + } + if (!withFiles) { + if (f.toFile().isFile()) + continue; + } + list.add(new FileInfoType(f, baseuri)); + } + } catch (IOException e2) { + logger.error(e2.getMessage(), e2); + ; + } + } + + /** + * Sets the directory. + * + * @param dir + * the dir + * @param withDirectories + * the with directories + * @param withFiles + * the with files + * @param depth + * the depth + * @param search + * the search + * @return the list + */ + private List setDirectory(URI baseuri, String dir, + boolean withDirectories, boolean withFiles, int depth, String search) { + List list = new ArrayList(); + setDirectory(baseuri, list, new File(dir), withDirectories, withFiles, + depth, search); + return list; + } + + private boolean isPermitted(String dir) { + /* + * + * logger.warn(String.format( + * "User '%s' does not have permission to access '%s'." + * ,SecurityUtils.getSubject().getPrincipal(), dir )); throw new + * NotAuthorizedException(new + * UnauthorizedException("User does not have permission to access "+ + * dir)); } + */ + return true; + } + + // http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java + private static String humanReadableByteCount(long bytes, boolean si) { + int unit = si ? 1000 : 1024; + if (bytes < unit) + return bytes + " B"; + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + + (si ? "" : "i"); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } + + private DimensionsConnection getDIMCMConnection() { + /* + * Do we have a registered connection already? + */ + DimensionsConnection conn = null; + + try { + conn = DimensionsConnectionManager.getThreadConnection(); + if (conn != null) + return conn; + } catch (Exception e) { + logger.error(e.getMessage()); + } + + /* + * Create a new connection from property file + */ + PropertiesConfiguration props; + try { + props = new PropertiesConfiguration(this.getClass() + .getClassLoader().getResource("/../dimcm.properties")); + } catch (ConfigurationException e) { + e.printStackTrace(); + return null; + } + + DimensionsConnectionDetails details = new DimensionsConnectionDetails(); + details.setUsername(props.getString("user")); + details.setPassword(props.getString("password")); + details.setDbName(props.getString("dbname")); + details.setDbConn(props.getString("dbconn")); + details.setServer(props.getString("server")); + conn = DimensionsConnectionManager.getConnection(details); + DimensionsConnectionManager.registerThreadConnection(conn); + return conn; + } + + private String getBaseURL() { + final String CACHE_BASEURL = "DIMCM.conf.baseurl"; + try { + JCS cache = JCS.getInstance("DIMCM"); + String baseurl = (String) cache.get(CACHE_BASEURL); + if(baseurl != null) return baseurl; + + PropertiesConfiguration props = new PropertiesConfiguration(this.getClass().getClassLoader().getResource("/../dimcm.properties")); + baseurl = props.getString("baseurl"); + cache.put(CACHE_BASEURL, baseurl); + return baseurl; + + } catch (CacheException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + return null; + } catch (ConfigurationException e) { + e.printStackTrace(); + return null; + + } +} +} \ No newline at end of file diff --git a/src/java/shiro.ini b/src/java/shiro.ini index 932cc85..fd86801 100644 --- a/src/java/shiro.ini +++ b/src/java/shiro.ini @@ -28,7 +28,7 @@ brian = brian, Administrator # roles. #Administrator = c:/t*/*, c:/windows/*, d:/**/VIDEO, C:/Users/brosenberger/**, d:/data/**, c:/** -Administrator = c:/TEMP/**, XmlService||test +Administrator = FileInfoService||c:/temp/**, XmlService||test [urls] # The 'urls' section is used for url-based security @@ -46,6 +46,10 @@ Administrator = c:/TEMP/**, XmlService||test /StringService = anon /XmlService = anon + +/fileinfo/** = authcBasic +/dimcminfo/** = anon + /** = authcBasic #/** = anon diff --git a/web/WEB-INF/cxf-beans.xml b/web/WEB-INF/cxf-beans.xml index a86d9fa..1ed7311 100644 --- a/web/WEB-INF/cxf-beans.xml +++ b/web/WEB-INF/cxf-beans.xml @@ -116,5 +116,13 @@ + + + + + + + +