Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial import

  • Loading branch information...
commit e5701ce38ad9e94ea559499efc688ce9b809ee7b 1 parent 5a21a11
@ullgren ullgren authored
Showing with 1,950 additions and 3 deletions.
  1. +11 −3 README.md
  2. +502 −0 lgpl-2.1.txt
  3. +136 −0 pom.xml
  4. +142 −0 src/main/java/com/redpill_linpro/component/smb/SmbClient.java
  5. +44 −0 src/main/java/com/redpill_linpro/component/smb/SmbComponent.java
  6. +111 −0 src/main/java/com/redpill_linpro/component/smb/SmbConfiguration.java
  7. +98 −0 src/main/java/com/redpill_linpro/component/smb/SmbConsumer.java
  8. +100 −0 src/main/java/com/redpill_linpro/component/smb/SmbEndpoint.java
  9. +12 −0 src/main/java/com/redpill_linpro/component/smb/SmbGenericFile.java
  10. +246 −0 src/main/java/com/redpill_linpro/component/smb/SmbOperations.java
  11. +274 −0 src/main/java/com/redpill_linpro/component/smb/SmbProducer.java
  12. +53 −0 src/main/java/com/redpill_linpro/component/smb/strategy/CronPollStrategy.java
  13. +1 −0  src/main/resources/META-INF/services/org/apache/camel/component/smb
  14. BIN  src/test/data/logo1.png
  15. BIN  src/test/data/logo2.png
  16. +45 −0 src/test/java/com/redpill_linpro/component/smb/BaseSmbTestSupport.java
  17. +31 −0 src/test/java/com/redpill_linpro/component/smb/FromFileToSmbTest.java
  18. +56 −0 src/test/java/com/redpill_linpro/component/smb/FromFtpMoveFileTest.java
  19. +61 −0 src/test/java/com/redpill_linpro/component/smb/FromSmbToAsciiFileTest.java
  20. +5 −0 src/test/resources/camelsmb.prp.template
  21. +22 −0 src/test/resources/log4j.properties
View
14 README.md
@@ -1,4 +1,12 @@
-camel-smb
-=========
+Camel SMB Component
+===================
+This project is a Samba Camel component build on top of JCIFS (http://jcifs.samba.org/).
-Camel SMB Component
+It was originally developed by Redpill Linpro AB and Helsingborgs stad as part of a integration project.
+
+This component is licensed under the LGPL (http://www.gnu.org/licenses/lgpl-2.1.txt)
+
+Since we could not find a easy to integrate CIFS server implemented in Java the unit testing
+currently requires that a local CIFS server (Samba or MS Windows share).
+Also copy camelsmb.prp.template to your home directory, rename it to camelsmb.prp" and edit
+to include correct details.
View
502 lgpl-2.1.txt
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
View
136 pom.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.redpill_linpro.component.smb</groupId>
+ <artifactId>camel-smb</artifactId>
+ <packaging>jar</packaging>
+ <version>2.9.0-1.0</version>
+
+ <name>A Camel SMB Component</name>
+ <url>http://www.redpill-linpro.com</url>
+
+ <properties>
+ <camel-version>2.9.0</camel-version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core</artifactId>
+ <version>${camel-version}</version>
+ <exclusions>
+ <exclusion>
+ <artifactId>slf4j-api</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz</artifactId>
+ <version>1.8.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>jcifs</groupId>
+ <artifactId>jcifs</artifactId>
+ <version>1.3.17</version>
+ </dependency>
+
+ <!-- for testing -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test</artifactId>
+ <version>${camel-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test</artifactId>
+ <version>${camel-version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+ <organization>
+ <name>Redpill Linpro AB</name>
+ <url>http://www.redpill-linpro.com</url>
+ </organization>
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <!-- ******************************
+ * BUILD SOURCE FIELS
+ -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.1.2</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- ******************************
+ * BUILD JavaDoc FIELS
+ -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.7</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
View
142 src/main/java/com/redpill_linpro/component/smb/SmbClient.java
@@ -0,0 +1,142 @@
+package com.redpill_linpro.component.smb;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import jcifs.smb.NtlmPasswordAuthentication;
+import jcifs.smb.SmbException;
+import jcifs.smb.SmbFile;
+import jcifs.smb.SmbFileOutputStream;
+
+import org.apache.camel.util.IOHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class SmbClient {
+
+ private NtlmPasswordAuthentication authentication;
+
+ protected final transient Logger log = LoggerFactory.getLogger(getClass());
+
+ /**
+ * Creates the internal NtlmPasswordAuthentication, that is used for authentication, from the provided credentials.
+ *
+ * @param domain User domain to use at login
+ * @param username User name to use at login
+ * @param password The password for the provided user
+ */
+ public void login(String domain, String username, String password) {
+ if (log.isDebugEnabled()) {
+ log.debug("login() domain[" + domain + "] username[" + username + "] password[***]");
+ }
+ setAuthentication(new NtlmPasswordAuthentication(domain, username, password));
+ }
+
+ /**
+ *
+ * @param url
+ * @param out
+ * @return
+ * @throws IOException
+ * @throws MalformedURLException
+ */
+ public boolean retrieveFile(String url, OutputStream out) throws IOException, MalformedURLException {
+ if (log.isDebugEnabled()) {
+ log.debug("retrieveFile() path[" + url + "]");
+ }
+ SmbFile smbFile;
+ smbFile = new SmbFile(url, authentication);
+ IOHelper.copyAndCloseInput(smbFile.getInputStream(), out);
+ return true;
+ }
+
+ public boolean createDirs(String url) {
+ if (log.isDebugEnabled()) {
+ log.debug("createDirs() path[" + url + "]");
+ }
+ SmbFile smbFile;
+ try {
+ smbFile = new SmbFile(url, authentication);
+ if (!smbFile.exists()) {
+ smbFile.mkdirs();
+ }
+ } catch (MalformedURLException e) {
+ return false;
+ } catch (SmbException e) {
+ return false;
+ }
+ return true;
+
+ }
+
+ public InputStream getInputStream(String url) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("getInputStream() path[" + url + "]");
+ }
+ SmbFile smbFile = new SmbFile(url, authentication);
+ return smbFile.getInputStream();
+ }
+
+ public boolean storeFile(String url, InputStream inputStream) throws IOException {
+ if (log.isDebugEnabled())
+ log.debug("storeFile path[" + url + "]");
+ SmbFile smbFile = new SmbFile(url, authentication);
+ SmbFileOutputStream smbout = new SmbFileOutputStream(smbFile, false);
+ byte[] buf = new byte[512 * 1024];
+ int numRead;
+ while ( (numRead = inputStream.read(buf)) >= 0)
+ smbout.write(buf, 0, numRead);
+ smbout.close();
+ return true;
+ }
+
+ public List<SmbFile> listFiles(String url) throws SmbException, MalformedURLException {
+ List<SmbFile> fileList = new ArrayList<SmbFile>();
+ SmbFile dir = new SmbFile(url, authentication);
+ for (SmbFile f : dir.listFiles()){
+ fileList.add(f);
+ }
+ return fileList;
+ }
+
+ public boolean isExist(String url) throws Exception {
+ SmbFile sFile = new SmbFile(url, authentication);
+ return sFile.exists();
+ }
+
+ public boolean delete(String url) throws Exception {
+ SmbFile sFile = new SmbFile(url, authentication);
+ try {
+ sFile.delete();
+ } catch(SmbException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean rename(String fromUrl, String toUrl) throws Exception {
+ SmbFile sFile = new SmbFile(fromUrl, authentication);
+ SmbFile renamedFile = new SmbFile(toUrl, authentication);
+ try {
+ sFile.renameTo(renamedFile);
+ } catch (SmbException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public NtlmPasswordAuthentication getAuthentication() {
+ return authentication;
+ }
+
+ public void setAuthentication(NtlmPasswordAuthentication authentication) {
+ this.authentication = authentication;
+ }
+}
View
44 src/main/java/com/redpill_linpro/component/smb/SmbComponent.java
@@ -0,0 +1,44 @@
+package com.redpill_linpro.component.smb;
+
+import java.net.URI;
+import java.util.Map;
+
+import jcifs.smb.SmbFile;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.file.GenericFileComponent;
+import org.apache.camel.component.file.GenericFileEndpoint;
+
+public class SmbComponent extends GenericFileComponent<SmbFile> {
+
+ public SmbComponent() {
+
+ }
+
+ public SmbComponent(CamelContext context) {
+ super(context);
+ }
+
+ @Override
+ protected SmbEndpoint buildFileEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+ if (log.isDebugEnabled())
+ log.debug("buildFileEndpoint() uri[" + uri + "] remaining[" + remaining + "] parameters[" + parameters + "]");
+
+ uri = fixSpaces(uri);
+ SmbConfiguration config = new SmbConfiguration(new URI(uri));
+ SmbEndpoint endpoint = new SmbEndpoint(uri, this, config);
+ return endpoint;
+ }
+
+ @Override
+ protected void afterPropertiesSet(GenericFileEndpoint<SmbFile> endpoint) throws Exception {
+ if (log.isDebugEnabled())
+ log.debug("afterPropertiesSet()");
+ }
+
+ private String fixSpaces(String input) {
+ return input.replace(" ", "%20");
+ }
+
+
+}
View
111 src/main/java/com/redpill_linpro/component/smb/SmbConfiguration.java
@@ -0,0 +1,111 @@
+package com.redpill_linpro.component.smb;
+
+import java.net.URI;
+
+import org.apache.camel.component.file.GenericFileConfiguration;
+import org.apache.camel.util.ObjectHelper;
+
+public class SmbConfiguration extends GenericFileConfiguration {
+
+ private static final String DOMAIN_SEPARATOR = ";";
+ private static final String USER_PASS_SEPARATOR = ":";
+
+ private String domain = null;
+ private String username = null;
+ private String password = null;
+ private String host = null;
+ private String path = null;
+
+ public SmbConfiguration(URI uri) {
+ configure(uri);
+ }
+
+ @Override
+ public void configure(URI uri) {
+ super.configure(uri);
+ String userInfo = uri.getUserInfo();
+
+ if (userInfo.contains(DOMAIN_SEPARATOR)) {
+ setDomain(ObjectHelper.before(userInfo, DOMAIN_SEPARATOR));
+ userInfo = ObjectHelper.after(userInfo, DOMAIN_SEPARATOR);
+ }
+ if (userInfo.contains(USER_PASS_SEPARATOR)) {
+ setUsername(ObjectHelper.before(userInfo, USER_PASS_SEPARATOR));
+ setPassword(ObjectHelper.after(userInfo, USER_PASS_SEPARATOR));
+ }
+ else {
+ setUsername(userInfo);
+ }
+
+ setHost(uri.getHost());
+
+ setPath(uri.getPath());
+ }
+
+ public String getSmbPath() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("smb://");
+ buffer.append(getHost());
+ buffer.append(getPath());
+ return buffer.toString();
+ }
+
+ public String getSmbHostPath() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("smb://");
+ buffer.append(getHost());
+ buffer.append("/");
+ return buffer.toString();
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ //TODO: give this dirty handling some thinking.
+ public String getDirectory() {
+ String s = super.getDirectory();
+ s = s.replace('\\', '/');
+ //we always need /
+ //this is a bit dirty
+
+ return s;
+ }
+
+}
View
98 src/main/java/com/redpill_linpro/component/smb/SmbConsumer.java
@@ -0,0 +1,98 @@
+package com.redpill_linpro.component.smb;
+
+import java.io.IOException;
+import java.util.List;
+
+import jcifs.smb.SmbException;
+import jcifs.smb.SmbFile;
+
+import org.apache.camel.Processor;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileConsumer;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileOperations;
+
+public class SmbConsumer extends GenericFileConsumer<SmbFile>{
+
+ private String endpointPath;
+ private String currentRelativePath = "";
+
+ public SmbConsumer(GenericFileEndpoint<SmbFile> endpoint, Processor processor, GenericFileOperations<SmbFile> operations) {
+ super(endpoint, processor, operations);
+ this.endpointPath = endpoint.getConfiguration().getDirectory();
+ }
+
+ @Override
+ protected boolean pollDirectory(String fileName, List<GenericFile<SmbFile>> fileList, int depth) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("pollDirectory() running. My delay is [" + this.getDelay() + "] and my strategy is [" + this.getPollStrategy().getClass().toString() + "]");
+ log.debug("pollDirectory() fileName[" + fileName + "]");
+ }
+
+ List<SmbFile> smbFiles;
+ boolean currentFileIsDir = false;
+ smbFiles = operations.listFiles(fileName);
+ for (SmbFile smbFile : smbFiles) {
+ if (!canPollMoreFiles(fileList)) {
+ return false;
+ }
+ try {
+ if (smbFile.isDirectory()) {
+ currentFileIsDir = true;
+ }
+ else {
+ currentFileIsDir = false;
+ }
+ } catch (SmbException e1) {
+ log.warn("Caught SmbException: " + e1.getMessage());
+ }
+ if (currentFileIsDir) {
+ if (endpoint.isRecursive()) {
+ currentRelativePath = smbFile.getName().split("/")[0] + "/";
+ pollDirectory(fileName + smbFile.getName(), fileList, depth++);
+ }
+ else {
+ currentRelativePath = "";
+ }
+ }
+ else {
+ try {
+ GenericFile<SmbFile> genericFile = asGenericFile(fileName, smbFile);
+ if (isValidFile(genericFile, false)) {
+ if (isInProgress(genericFile)) {
+ log.info("skipping as we are already in progress with this file");
+ }
+ else {
+ fileList.add(asGenericFile(fileName, smbFile));
+ }
+ }
+ } catch (IOException e) {
+ log.warn("Caught IOException: " + e.getMessage());
+ }
+ }
+ }
+ return true;
+ }
+
+ //TODO: this needs some checking!
+ private GenericFile<SmbFile> asGenericFile(String path, SmbFile file) throws IOException{
+ SmbGenericFile<SmbFile> answer = new SmbGenericFile<SmbFile>();
+ answer.setAbsoluteFilePath(path + answer.getFileSeparator() + file.getName());
+ answer.setAbsolute(true);
+ answer.setEndpointPath(endpointPath);
+ answer.setFileNameOnly(file.getName());
+ answer.setFileLength(file.getContentLength());
+ answer.setFile(file);
+ answer.setLastModified(file.getLastModified());
+ answer.setFileName(currentRelativePath + file.getName());
+ answer.setRelativeFilePath(file.getName());
+ if (log.isDebugEnabled()) {
+ log.debug("asGenericFile():");
+ log.debug("absoluteFilePath[" + answer.getAbsoluteFilePath() +"] endpointpath[" + answer.getEndpointPath() + "] filenameonly["+ answer.getFileNameOnly() +"] filename[" + answer.getFileName() + "] relativepath[" + answer.getRelativeFilePath() + "]");
+
+ }
+ return answer;
+ }
+
+}
View
100 src/main/java/com/redpill_linpro/component/smb/SmbEndpoint.java
@@ -0,0 +1,100 @@
+package com.redpill_linpro.component.smb;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Map;
+
+import jcifs.smb.SmbFile;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileProducer;
+import org.apache.camel.impl.DefaultExchange;
+
+public class SmbEndpoint extends GenericFileEndpoint<SmbFile> {
+
+ protected SmbClient smbClient;
+ protected Map<String, Object> props;
+
+ private String cron;
+
+ public SmbEndpoint(String uri, SmbComponent smbComponent, SmbConfiguration configuration) {
+ super(uri, smbComponent);
+ this.configuration = configuration;
+ }
+
+ @Override
+ public SmbConfiguration getConfiguration() {
+ return (SmbConfiguration) configuration;
+ }
+
+ @Override
+ public SmbConsumer createConsumer(Processor processor) throws Exception {
+ SmbConsumer consumer = new SmbConsumer(this, processor, createSmbOperations());
+
+ consumer.setMaxMessagesPerPoll(getMaxMessagesPerPoll());
+ configureConsumer(consumer);
+ return consumer;
+ }
+
+ @Override
+ public GenericFileProducer<SmbFile> createProducer() throws Exception {
+ return new SmbProducer(this, createSmbOperations());
+ }
+
+ @Override
+ public Exchange createExchange(GenericFile<SmbFile> file) {
+ Exchange answer = new DefaultExchange(this);
+ if (file != null) {
+ file.bindToExchange(answer);
+ }
+ return answer;
+ }
+
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public SmbOperations<SmbFile> createSmbOperations(){
+ SmbClient client = new SmbClient();
+ SmbOperations operations = new SmbOperations(client);
+ operations.setEndpoint(this);
+ return operations;
+ }
+
+ @Override
+ public String getScheme() {
+ return "smb";
+ }
+
+ @Override
+ public char getFileSeparator() {
+ return '/';
+ }
+
+ @Override
+ public boolean isAbsolute(String name) {
+ return true;
+ }
+
+ public void setCron(String cron) {
+ if (log.isDebugEnabled())
+ log.debug("setCron() cron[" + cron + "]");
+ String newCron = "";
+ try {
+ newCron = URLDecoder.decode(cron, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ log.warn("cron expression [" + cron + "] not valid!");
+ }
+ this.cron = newCron;
+ }
+
+ public String getCron() {
+ return cron;
+ }
+
+ @Override
+ public boolean isSingleton(){
+ return false;
+ }
+}
View
12 src/main/java/com/redpill_linpro/component/smb/SmbGenericFile.java
@@ -0,0 +1,12 @@
+package com.redpill_linpro.component.smb;
+
+import org.apache.camel.component.file.GenericFile;
+
+public class SmbGenericFile<T> extends GenericFile<T> {
+
+ @Override
+ public char getFileSeparator() {
+ return '/';
+ }
+
+}
View
246 src/main/java/com/redpill_linpro/component/smb/SmbOperations.java
@@ -0,0 +1,246 @@
+package com.redpill_linpro.component.smb;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.FileComponent;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.apache.camel.component.file.GenericFileOperations;
+import org.apache.camel.util.ExchangeHelper;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+
+public class SmbOperations<SmbFile> implements GenericFileOperations<SmbFile> {
+
+ private GenericFileEndpoint<SmbFile> endpoint;
+ private SmbClient client;
+
+
+ public SmbOperations(SmbClient smbClient) {
+ this.client = smbClient;
+ }
+
+ public void setEndpoint(GenericFileEndpoint<SmbFile> endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ public boolean deleteFile(String name) throws GenericFileOperationFailedException {
+ try {
+ login();
+ return client.delete(getPath(name));
+
+ } catch (Exception e) {
+ throw new GenericFileOperationFailedException("could not delete file " + e);
+ }
+ }
+
+ public boolean existsFile(String name) throws GenericFileOperationFailedException {
+ try {
+ login();
+ return client.isExist(getPath(name));
+ } catch (Exception e) {
+ throw new GenericFileOperationFailedException("could not determine if file exists " + e);
+ }
+ }
+
+ public boolean renameFile(String from, String to) throws GenericFileOperationFailedException {
+ String fromPath = getPath(from);
+ String toPath = getPath(to);
+ try {
+ login();
+ return client.rename(fromPath, toPath);
+ } catch (Exception e) {
+ throw new GenericFileOperationFailedException("could not rename file " + e);
+ }
+ }
+
+ public boolean buildDirectory(String directory, boolean absolute) throws GenericFileOperationFailedException {
+ try {
+ login();
+ return client.createDirs(getPath(directory));
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean retrieveFile(String name, Exchange exchange) throws GenericFileOperationFailedException {
+ if (ObjectHelper.isNotEmpty(endpoint.getLocalWorkDirectory())) {
+ return retrieveFileToFileInLocalWorkDirectory(name, exchange);
+ }
+ return retrieveFileToStreamInBody(name, exchange);
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean retrieveFileToFileInLocalWorkDirectory(String name, Exchange exchange) throws GenericFileOperationFailedException {
+ File temp;
+
+ File local = new File(endpoint.getLocalWorkDirectory());
+ local.mkdirs();
+ OutputStream os;
+ GenericFile<SmbFile> file = (GenericFile<SmbFile>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE);
+ ObjectHelper.notNull(file, "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");
+
+ // use relative filename in local work directory
+ String relativeName = file.getRelativeFilePath();
+ temp = new File(local, relativeName + ".inprogress");
+ local = new File(local, relativeName);
+
+ // delete any existing files
+ if (temp.exists()) {
+ if (!FileUtil.deleteFile(temp)) {
+ throw new GenericFileOperationFailedException("Cannot delete existing local work file: " + temp);
+ }
+ }
+ if (local.exists()) {
+ if (!FileUtil.deleteFile(local)) {
+ throw new GenericFileOperationFailedException("Cannot delete existing local work file: " + local);
+ }
+ }
+ // create new temp local work file
+ try {
+ if (!temp.createNewFile()) {
+ throw new GenericFileOperationFailedException("Cannot create new local work file: " + temp);
+ }
+ } catch (IOException e1) {
+ throw new GenericFileOperationFailedException("Cannot create new local work file: " + temp + " " + e1);
+ }
+
+ // store content as a file in the local work directory in the temp handle
+ try {
+ os = new FileOutputStream(temp);
+ } catch (FileNotFoundException e1) {
+ throw new GenericFileOperationFailedException("File not found: " + temp + " " + e1);
+ }
+
+ // set header with the path to the local work file
+ exchange.getIn().setHeader(Exchange.FILE_LOCAL_WORK_PATH, local.getPath());
+
+ boolean result;
+
+ try {
+ // store the java.io.File handle as the body
+ file.setBody(local);
+ login();
+ result = client.retrieveFile(getPath(name), os);
+ } catch (IOException e) {
+ throw new GenericFileOperationFailedException("Cannot retrieve file: " + name, e);
+ } catch (Exception e) {
+ throw new GenericFileOperationFailedException("Cannot retrieve file: " + name, e);
+ } finally {
+ IOHelper.close(os, "retrieve: " + name);
+ }
+
+ try {
+ if (!FileUtil.renameFile(temp, local, true)) {
+ throw new GenericFileOperationFailedException("Cannot rename local work file from: " + temp + " to: " + local);
+ }
+ } catch (IOException e) {
+ throw new GenericFileOperationFailedException("Cannot rename local work file from: " + temp + " to: " + local, e);
+ }
+
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean retrieveFileToStreamInBody(String name, Exchange exchange) throws GenericFileOperationFailedException {
+ OutputStream os = null;
+ boolean result;
+ try {
+ os = new ByteArrayOutputStream();
+ GenericFile<SmbFile> target = (GenericFile<SmbFile>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE);
+ ObjectHelper.notNull(target, "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");
+ target.setBody(os);
+
+ // use input stream which works with Apache SSHD used for testing
+ login();
+ result = client.retrieveFile(getPath(name), os);
+
+ } catch (IOException e) {
+ throw new GenericFileOperationFailedException("Cannot retrieve file: " + name, e);
+ } catch (Exception e) {
+ throw new GenericFileOperationFailedException("Cannot retrieve file: " + name, e);
+ } finally {
+ IOHelper.close(os, "retrieve: " + name);
+ }
+
+ return result;
+ }
+
+ public boolean storeFile(String name, Exchange exchange) throws GenericFileOperationFailedException {
+ String storeName = getPath(name);
+
+ InputStream is = null;
+ try {
+ is = ExchangeHelper.getMandatoryInBody(exchange, InputStream.class);
+
+ login();
+ client.storeFile(storeName, is);
+ return true;
+ } catch (Exception e) {
+ throw new GenericFileOperationFailedException("Cannot store file " + storeName, e);
+ } finally {
+ IOHelper.close(is, "store: " + storeName);
+ }
+ }
+
+ public String getCurrentDirectory() throws GenericFileOperationFailedException {
+ return null;
+ }
+
+ public void changeCurrentDirectory(String path) throws GenericFileOperationFailedException {
+ }
+
+ public void changeToParentDirectory() throws GenericFileOperationFailedException {
+ }
+
+ public List<SmbFile> listFiles() throws GenericFileOperationFailedException {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<SmbFile> listFiles(String path) throws GenericFileOperationFailedException {
+ String listPath = getDirPath(path);
+ List<SmbFile> files = new ArrayList<SmbFile>();
+ try {
+ login();
+ for (Object f : client.listFiles(listPath)){
+ files.add((SmbFile) f);
+ }
+ } catch (Exception e) {
+ throw new GenericFileOperationFailedException("Could not get files " + e.getMessage());
+ }
+ return files;
+ }
+
+ public void login() {
+ String domain = ((SmbConfiguration) endpoint.getConfiguration()).getDomain();
+ String username = ((SmbConfiguration) endpoint.getConfiguration()).getUsername();
+ String password = ((SmbConfiguration) endpoint.getConfiguration()).getPassword();
+
+ client.login(domain, username, password);
+ }
+
+ private String getPath(String pathEnd) {
+ String path = ((SmbConfiguration)endpoint.getConfiguration()).getSmbHostPath() + pathEnd;
+ return path.replace('\\', '/');
+ }
+
+ private String getDirPath(String pathEnd) {
+ String path = ((SmbConfiguration)endpoint.getConfiguration()).getSmbHostPath() + pathEnd;
+ if (!path.endsWith("/")) {
+ path = path + "/";
+ }
+ return path.replace('\\', '/');
+ }
+}
View
274 src/main/java/com/redpill_linpro/component/smb/SmbProducer.java
@@ -0,0 +1,274 @@
+package com.redpill_linpro.component.smb;
+
+import java.io.File;
+
+import jcifs.smb.SmbFile;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.ServicePoolAware;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.apache.camel.component.file.GenericFileOperations;
+import org.apache.camel.component.file.GenericFileProducer;
+import org.apache.camel.spi.Language;
+import org.apache.camel.util.ExchangeHelper;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+
+public class SmbProducer extends GenericFileProducer<SmbFile> implements ServicePoolAware{
+
+ private String endpointPath;
+
+ protected SmbProducer(GenericFileEndpoint<SmbFile> endpoint, GenericFileOperations<SmbFile> operations) {
+ super(endpoint, operations);
+
+ }
+
+ @Override
+ public String getFileSeparator() {
+
+ //return "/";
+ return File.separator;
+ }
+
+ @Override
+ public String normalizePath(String name) {
+ if (log.isDebugEnabled()) {
+ log.debug("normalizePath() name[" + name + "]");
+ log.debug("normalizePath() returning [" + name.replace('\\', '/') + "]");
+ }
+ return name.replace('\\', '/');
+ }
+
+ public void process(Exchange exchange) throws Exception {
+
+ Exchange smbExchange = getEndpoint().createExchange(exchange);
+ setEndpointPath(getEndpoint().getEndpointUri());
+ processExchange(smbExchange);
+ ExchangeHelper.copyResults(exchange, smbExchange);
+ }
+
+ public void setEndpointPath(String endpointPath) {
+ this.endpointPath = endpointPath;
+ }
+
+ public String getEndpointPath() {
+ return endpointPath;
+ }
+
+ @Override
+ public void postWriteCheck() {
+ //not at this time
+ }
+
+ protected void processExchange(Exchange exchange) throws Exception {
+ if (log.isDebugEnabled()) {
+ log.trace("Processing " + exchange);
+ }
+
+ try {
+ String target = createFileName(exchange);
+ if (log.isDebugEnabled())
+ log.debug("processExchange() target[" + target + "]");
+
+ preWriteCheck();
+
+ // should we write to a temporary name and then afterwards rename to real target
+ boolean writeAsTempAndRename = ObjectHelper.isNotEmpty(endpoint.getTempFileName());
+ String tempTarget = null;
+ if (writeAsTempAndRename) {
+ // compute temporary name with the temp prefix
+ tempTarget = createTempFileName(exchange, target);
+ if (log.isDebugEnabled())
+ log.debug("tempTarget [" + tempTarget + "]");
+
+ if (log.isDebugEnabled()) {
+ log.debug("Writing using tempNameFile: " + tempTarget);
+ }
+
+ // cater for file exists option on the real target as
+ // the file operations code will work on the temp file
+
+ // if an existing file already exists what should we do?
+ if (operations.existsFile(target)) {
+ if (endpoint.getFileExist() == GenericFileExist.Ignore) {
+ // ignore but indicate that the file was written
+ if (log.isDebugEnabled()) {
+ log.debug("An existing file already exists: " + target + ". Ignore and do not override it.");
+ }
+ return;
+ } else if (endpoint.getFileExist() == GenericFileExist.Fail) {
+ throw new GenericFileOperationFailedException("File already exist: " + target + ". Cannot write new file.");
+ } else if (endpoint.isEagerDeleteTargetFile() && endpoint.getFileExist() == GenericFileExist.Override) {
+ // we override the target so we do this by deleting it so the temp file can be renamed later
+ // with success as the existing target file have been deleted
+ if (log.isDebugEnabled()) {
+ log.debug("Eagerly deleting existing file: " + target);
+ }
+ if (!operations.deleteFile(target)) {
+ throw new GenericFileOperationFailedException("Cannot delete file: " + target);
+ }
+ }
+ }
+
+ // delete any pre existing temp file
+ if (operations.existsFile(tempTarget)) {
+ if (log.isDebugEnabled()) {
+ log.debug("Deleting existing temp file: " + tempTarget);
+ }
+ if (!operations.deleteFile(tempTarget)) {
+ throw new GenericFileOperationFailedException("Cannot delete file: " + tempTarget);
+ }
+ }
+ }
+
+ // write/upload the file
+ writeFile(exchange, tempTarget != null ? tempTarget : target);
+
+ // if we did write to a temporary name then rename it to the real
+ // name after we have written the file
+ if (tempTarget != null) {
+
+ // if we should not eager delete the target file then do it now just before renaming
+ if (!endpoint.isEagerDeleteTargetFile() && operations.existsFile(target)
+ && endpoint.getFileExist() == GenericFileExist.Override) {
+ // we override the target so we do this by deleting it so the temp file can be renamed later
+ // with success as the existing target file have been deleted
+ if (log.isDebugEnabled()) {
+ log.debug("Deleting existing file: " + target);
+ }
+ if (!operations.deleteFile(target)) {
+ throw new GenericFileOperationFailedException("Cannot delete file: " + target);
+ }
+ }
+
+ // now we are ready to rename the temp file to the target file
+ if (log.isDebugEnabled()) {
+ log.debug("Renaming file: [" + tempTarget + "] to: [" + target + "]");
+ }
+ boolean renamed = operations.renameFile(tempTarget, target);
+ if (!renamed) {
+ throw new GenericFileOperationFailedException("Cannot rename file from: " + tempTarget + " to: " + target);
+ }
+ }
+
+ // any done file to write?
+ if (endpoint.getDoneFileName() != null) {
+ if (log.isDebugEnabled())
+ log.debug("doneFileName not null");
+ }
+
+ // lets store the name we really used in the header, so end-users
+ // can retrieve it
+ exchange.getIn().setHeader(Exchange.FILE_NAME_PRODUCED, target);
+ } catch (Exception e) {
+ handleFailedWrite(exchange, e);
+ }
+
+ postWriteCheck();
+ }
+
+ public String createFileName(Exchange exchange) {
+ String answer;
+
+ String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+
+
+ // expression support
+ Expression expression = endpoint.getFileName();
+ if (name != null) {
+ // the header name can be an expression too, that should override
+ // whatever configured on the endpoint
+ if (StringHelper.hasStartToken(name, "simple")) {
+ if (log.isDebugEnabled()) {
+ log.debug(Exchange.FILE_NAME + " contains a Simple expression: " + name);
+ }
+ Language language = getEndpoint().getCamelContext().resolveLanguage("file");
+ expression = language.createExpression(name);
+ }
+ }
+ if (expression != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Filename evaluated as expression: " + expression);
+ }
+ name = expression.evaluate(exchange, String.class);
+ }
+
+ // flatten name
+ if (name != null && endpoint.isFlatten()) {
+ int pos = name.lastIndexOf(getFileSeparator());
+ if (pos == -1) {
+ pos = name.lastIndexOf('/');
+ }
+ if (pos != -1) {
+ name = name.substring(pos + 1);
+ }
+ }
+ if (log.isDebugEnabled())
+ log.debug("createFilename() name[" + name + "]");
+
+ // compute path by adding endpoint starting directory
+ String endpointPath = endpoint.getConfiguration().getDirectory();
+ if (log.isDebugEnabled())
+ log.debug("createFileName() enpointPath [" + endpointPath + "]");
+ // Its a directory so we should use it as a base path for the filename
+ // If the path isn't empty, we need to add a trailing / if it isn't already there
+ String baseDir = "";
+ if (endpointPath.length() > 0) {
+ baseDir = endpointPath + (endpointPath.endsWith(getFileSeparator()) ? "" : getFileSeparator());
+ }
+ if (name != null) {
+ answer = baseDir + name;
+ } else {
+ // use a generated filename if no name provided
+ answer = baseDir + endpoint.getGeneratedFileName(exchange.getIn());
+ }
+
+ if (endpoint.getConfiguration().needToNormalize()) {
+ // must normalize path to cater for Windows and other OS
+ answer = normalizePath(answer);
+ }
+
+ return answer;
+ }
+
+ @Override
+ public void writeFile(Exchange exchange, String fileName) throws GenericFileOperationFailedException {
+ if (log.isDebugEnabled())
+ log.debug("writeFile() fileName[" + fileName + "]");
+ // build directory if auto create is enabled
+ if (endpoint.isAutoCreate()) {
+ // we must normalize it (to avoid having both \ and / in the name which confuses java.io.File)
+ String name = FileUtil.normalizePath(fileName);
+
+ // use java.io.File to compute the file path
+ File file = new File(name);
+ String directory = file.getParent();
+ boolean absolute = FileUtil.isAbsolute(file);
+ if (directory != null) {
+ if (!operations.buildDirectory(directory, absolute)) {
+
+ log.warn("Cannot build directory [" + directory + "] (could be because of denied permissions)");
+
+ }
+ }
+ }
+
+ // upload
+ if (log.isDebugEnabled()) {
+ log.debug("About to write [" + fileName + "] to [" + getEndpoint() + "] from exchange [" + exchange + "]");
+ }
+
+ boolean success = operations.storeFile(fileName, exchange);
+ if (!success) {
+ throw new GenericFileOperationFailedException("Error writing file [" + fileName + "]");
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Wrote [" + fileName + "] to [" + getEndpoint() + "]");
+ }
+ }
+
+}
View
53 src/main/java/com/redpill_linpro/component/smb/strategy/CronPollStrategy.java
@@ -0,0 +1,53 @@
+package com.redpill_linpro.component.smb.strategy;
+
+import java.util.Date;
+
+import org.apache.camel.Consumer;
+import org.apache.camel.Endpoint;
+import org.apache.camel.impl.DefaultPollingConsumerPollStrategy;
+import org.quartz.CronExpression;
+
+import com.redpill_linpro.component.smb.SmbConsumer;
+import com.redpill_linpro.component.smb.SmbEndpoint;
+
+public class CronPollStrategy extends DefaultPollingConsumerPollStrategy {
+ @Override
+ public boolean begin(Consumer consumer, Endpoint endpoint) {
+ log.trace("CronPolicy begin called");
+
+ SmbConsumer smbConsumer = (SmbConsumer) consumer;
+
+ try {
+ CronExpression cronExpression = lookup((SmbEndpoint) endpoint);
+
+ Date now = new Date();
+ Date nextTrigger = cronExpression.getNextValidTimeAfter(now);
+ Long timeSpan = Math.abs(nextTrigger.getTime() - now.getTime());
+
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Now [%1$tH:%1$tM:%1$tS] Next [%2$tH:%2$tM:%2$tS]", now, nextTrigger));
+ log.debug(String.format("Span [%d] Delay [%d]", timeSpan, smbConsumer.getDelay()));
+ }
+
+ return (timeSpan <= smbConsumer.getDelay());
+ } catch (Exception e) {
+ log.warn("No valid cron expression", e);
+ return true;
+ }
+ }
+
+ public boolean rollback(Consumer consumer, Endpoint endpoint, int retryCounter, Exception cause) throws Exception {
+ log.info("strategy got an Exception: " + cause);
+ log.info("retryCounter [" + retryCounter + "]");
+ throw cause;
+ }
+
+ private CronExpression lookup(SmbEndpoint smbEndpoint) throws Exception {
+ String cron = smbEndpoint.getCron();
+
+ if (cron == null) throw new Exception("No cron expression available");
+ if (!CronExpression.isValidExpression(cron)) throw new Exception("Invalid cron expression [" + cron + "]");
+
+ return new CronExpression(cron);
+ }
+}
View
1  src/main/resources/META-INF/services/org/apache/camel/component/smb
@@ -0,0 +1 @@
+class=com.redpill_linpro.component.smb.SmbComponent
View
BIN  src/test/data/logo1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  src/test/data/logo2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
45 src/test/java/com/redpill_linpro/component/smb/BaseSmbTestSupport.java
@@ -0,0 +1,45 @@
+package com.redpill_linpro.component.smb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Properties;
+
+import org.apache.camel.test.CamelTestSupport;
+import org.junit.Assert;
+import org.junit.Before;
+
+public class BaseSmbTestSupport extends CamelTestSupport {
+
+ private static Properties properties;
+
+ @Before
+ public void setUp() throws Exception {
+ properties = new Properties();
+ File configFile = new File(System.getProperty("user.home")+File.separator+"camelsmb.prp");
+ if ( !configFile.exists() ) {
+ Assert.fail("Copy src/test/resources/camelsmb.prp.template to " +System.getProperty("user.home")+File.separator+"camelsmb.prp and edit for correct details.");
+ }
+ properties.load(new FileInputStream(configFile));
+ super.setUp();
+ }
+
+ public String getUsername() {
+ return properties.getProperty("username");
+ }
+
+ public String getDomain() {
+ return properties.getProperty("domain");
+ }
+
+ public String getPassword() {
+ return properties.getProperty("password");
+ }
+
+ public String getShare() {
+ return properties.getProperty("share");
+ }
+
+ public String getLocalSharePath() {
+ return properties.getProperty("localpath");
+ }
+}
View
31 src/test/java/com/redpill_linpro/component/smb/FromFileToSmbTest.java
@@ -0,0 +1,31 @@
+package com.redpill_linpro.component.smb;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.Test;
+
+/**
+ * Unit test to verify that we can pool an ASCII file from a local file path and store it on the SMB Server
+ */
+public class FromFileToSmbTest extends BaseSmbTestSupport {
+ private String getSmbUrl() {
+ return "smb://"+getDomain()+";"+getUsername()+"@localhost/"+getShare()+"/camel/"+getClass().getSimpleName()+"?password="+getPassword()+"&fileExist=Override";
+ }
+
+ @Test
+ public void testFromFileToSmb() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(2);
+
+ assertMockEndpointsSatisfied();
+ }
+
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ public void configure() throws Exception {
+ from(getSmbUrl()).to("mock:result");
+ from("file:src/test/data?noop=true&consumer.delay=3000").to(getSmbUrl());
+ }
+ };
+ }
+}
View
56 src/test/java/com/redpill_linpro/component/smb/FromFtpMoveFileTest.java
@@ -0,0 +1,56 @@
+package com.redpill_linpro.component.smb;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Producer;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit test to test both consumer.moveNamePrefix and consumer.moveNamePostfix options.
+ */
+public class FromFtpMoveFileTest extends BaseSmbTestSupport {
+ private String getSmbUrl() {
+ return "smb://"+getDomain()+";"+getUsername()+"@localhost/"+getShare()+"/camel/"
+ +getClass().getSimpleName()+"?password="+getPassword()
+ +"&move=done/sub2/${file:name}.old&consumer.delay=5000";
+ }
+
+ @Test
+ public void testPollFileAndShouldBeMoved() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+ mock.expectedBodiesReceived("Hello World this file will be moved");
+ mock.expectedFileExists(getLocalSharePath() + "/camel/"
+ +getClass().getSimpleName()+"/done/sub2/hello.txt.old");
+
+ mock.assertIsSatisfied();
+ }
+
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ // prepares the FTP Server by creating a file on the server that we want to unit
+ // test that we can pool and store as a local file
+ Endpoint endpoint = context.getEndpoint(getSmbUrl());
+ Exchange exchange = endpoint.createExchange();
+ exchange.getIn().setBody("Hello World this file will be moved");
+ exchange.getIn().setHeader(Exchange.FILE_NAME, "hello.txt");
+ Producer producer = endpoint.createProducer();
+ producer.start();
+ producer.process(exchange);
+ producer.stop();
+ }
+
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ public void configure() throws Exception {
+ from(getSmbUrl()).to("mock:result");
+ }
+ };
+ }
+}
View
61 src/test/java/com/redpill_linpro/component/smb/FromSmbToAsciiFileTest.java
@@ -0,0 +1,61 @@
+package com.redpill_linpro.component.smb;
+
+import java.io.File;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Producer;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit test to verify that we can pool an ASCII file from the SMB Server and store it on a local file path
+ */
+public class FromSmbToAsciiFileTest extends BaseSmbTestSupport {
+
+ private String getSmbUrl() {
+ return "smb://"+getDomain()+";"+getUsername()+"@localhost/"+getShare()+"/camel/"+getClass().getSimpleName()+"?password="+getPassword()+"&fileExist=Override";
+ }
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ // prepares the FTP Server by creating a file on the server that we want to unit
+ // test that we can pool and store as a local file
+ Endpoint endpoint = context.getEndpoint(getSmbUrl());
+ Exchange exchange = endpoint.createExchange();
+ exchange.getIn().setBody("Hello World from SMBServer");
+ exchange.getIn().setHeader(Exchange.FILE_NAME, "hello.txt");
+ Producer producer = endpoint.createProducer();
+ producer.start();
+ producer.process(exchange);
+ producer.stop();
+ }
+
+ @Test
+ public void testFromSmbToFile() throws Exception {
+ MockEndpoint resultEndpoint = getMockEndpoint("mock:result");
+ resultEndpoint.expectedMinimumMessageCount(1);
+ resultEndpoint.expectedBodiesReceived("Hello World from SMBServer");
+
+ resultEndpoint.assertIsSatisfied();
+
+ // assert the file
+ File file = new File("target/smbtest/deleteme.txt");
+ assertTrue("The ASCII file should exists", file.exists());
+ assertTrue("File size wrong", file.length() > 10);
+ }
+
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ public void configure() throws Exception {
+ String fileUrl = "file:target/smbtest/?fileExist=Override&noop=true";
+ from(getSmbUrl()).setHeader(Exchange.FILE_NAME, constant("deleteme.txt")).
+ convertBodyTo(String.class).to(fileUrl).to("mock:result");
+ }
+ };
+ }
+}
View
5 src/test/resources/camelsmb.prp.template
@@ -0,0 +1,5 @@
+username=joed
+password=secret
+domain=WORKGROUP
+share=share
+localpath=C:/temp/share
View
22 src/test/resources/log4j.properties
@@ -0,0 +1,22 @@
+
+#
+# The logging properties used for eclipse testing, We want to see debug output on the console.
+#
+log4j.rootLogger=INFO, out
+
+# uncomment the following line to turn on Camel debugging
+log4j.logger.org.apache.camel=DEBUG
+
+# uncomment the following line to turn on ActiveMQ debugging
+#log4j.logger.org.apache.activemq=DEBUG
+
+log4j.logger.org.springframework=DEBUG
+
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=[%30.30t] %-30.30c{1} %-5p %m%n
+#log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
Please sign in to comment.
Something went wrong with that request. Please try again.