Merge rome-propono into rome
This commit is contained in:
commit
fdfabe4f11
107 changed files with 11178 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/.classpath
|
||||||
|
/.project
|
||||||
|
/.settings
|
||||||
|
/target
|
203
LICENSE
Normal file
203
LICENSE
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
26
NOTICE
Normal file
26
NOTICE
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
Portions of the Propono code are Copyright (C) 2007 Sun Microsystems, Inc.
|
||||||
|
|
||||||
|
Sun Microsystems, Inc.
|
||||||
|
4150 Network Circle, Santa Clara, California 95054, U.S.A.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
U.S. Government Rights - Commercial software. Government users are subject to
|
||||||
|
the Sun Microsystems, Inc. standard license agreement and applicable provisions
|
||||||
|
of the FAR and its supplements.
|
||||||
|
|
||||||
|
Use is subject to license terms.
|
||||||
|
|
||||||
|
This distribution may include materials developed by third parties.
|
||||||
|
|
||||||
|
Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered
|
||||||
|
trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
|
||||||
|
|
||||||
|
This product is covered and controlled by U.S. Export Control laws and may be
|
||||||
|
subject to the export or import laws in other countries. Nuclear, missile,
|
||||||
|
chemical biological weapons or nuclear maritime end uses or end users, whether
|
||||||
|
direct or indirect, are strictly prohibited. Export or reexport to countries
|
||||||
|
subject to U.S. embargo or to entities identified on U.S. export exclusion
|
||||||
|
lists, including, but not limited to, the denied persons and specially
|
||||||
|
designated nationals lists is strictly prohibited.
|
||||||
|
|
6
README.md
Normal file
6
README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
rome
|
||||||
|
====
|
||||||
|
|
||||||
|
ROME is a set of RSS and Atom Utilities for Java. It makes it easy to work in Java with most syndication formats: RSS 0.90, RSS 0.91 Netscape, RSS 0.91 Userland, RSS 0.92, RSS 0.93, RSS 0.94, RSS 1.0, RSS 2.0, Atom 0.3, Atom 1.0
|
||||||
|
|
||||||
|
More Information: http://rometools.github.io/rome-propono/
|
122
pom.xml
Normal file
122
pom.xml
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<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/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.rometools</groupId>
|
||||||
|
<artifactId>rome-parent</artifactId>
|
||||||
|
<version>1.6.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>rome-propono</artifactId>
|
||||||
|
<version>1.6.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>rome-propono</name>
|
||||||
|
|
||||||
|
<description>The ROME Propono subproject is a Java class library that
|
||||||
|
supports publishing protocols, specifically the Atom Publishing Protocol
|
||||||
|
and the legacy MetaWeblog API. Propono includes an Atom client library,
|
||||||
|
Atom server framework and a Blog client that supports both Atom protocol
|
||||||
|
and the MetaWeblog API.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<url>http://rometools.github.io/rome-propono/</url>
|
||||||
|
|
||||||
|
<scm>
|
||||||
|
<connection>scm:git:ssh://github.com/rometools/rome-propono.git</connection>
|
||||||
|
<developerConnection>scm:git:ssh://git@github.com/rometools/rome-propono.git</developerConnection>
|
||||||
|
<url>https://github.com/rometools/rome-propono</url>
|
||||||
|
</scm>
|
||||||
|
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<name>Dave Johnson</name>
|
||||||
|
<url>http://rollerweblogger.org/roller</url>
|
||||||
|
<timezone>-5</timezone>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<name>Robert Cooper</name>
|
||||||
|
<email>kebernet@gmail.com</email>
|
||||||
|
<url>http://www.screaming-penguin.com</url>
|
||||||
|
<timezone>-4</timezone>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>sonatype-nexus-snapshots</id>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-scm-publish-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<scmBranch>gh-pages</scmBranch>
|
||||||
|
<pubScmUrl>${project.scm.developerConnection}</pubScmUrl>
|
||||||
|
<content>${project.build.directory}/site</content>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.rometools</groupId>
|
||||||
|
<artifactId>rome</artifactId>
|
||||||
|
<version>1.6.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-httpclient</groupId>
|
||||||
|
<artifactId>commons-httpclient</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-beanutils</groupId>
|
||||||
|
<artifactId>commons-beanutils</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.xmlrpc</groupId>
|
||||||
|
<artifactId>xmlrpc-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.oauth.core</groupId>
|
||||||
|
<artifactId>oauth</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jetty</groupId>
|
||||||
|
<artifactId>jetty</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates AtomService or ClientCollection based on username, password and end-point URI of Atom
|
||||||
|
* protocol service.
|
||||||
|
*/
|
||||||
|
public class AtomClientFactory {
|
||||||
|
|
||||||
|
static {
|
||||||
|
Atom10Parser.setResolveURIs(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create AtomService by reading service doc from Atom Server.
|
||||||
|
*/
|
||||||
|
public static ClientAtomService getAtomService(final String uri, final AuthStrategy authStrategy) throws ProponoException {
|
||||||
|
return new ClientAtomService(uri, authStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ClientCollection bound to URI.
|
||||||
|
*/
|
||||||
|
public static ClientCollection getCollection(final String uri, final AuthStrategy authStrategy) throws ProponoException {
|
||||||
|
return new ClientCollection(uri, authStrategy);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
public interface AuthStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add authentication credenticals, tokens, etc. to HTTP method
|
||||||
|
*/
|
||||||
|
void addAuthentication(HttpClient httpClient, HttpMethodBase method) throws ProponoException;
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.io.impl.Base64;
|
||||||
|
|
||||||
|
public class BasicAuthStrategy implements AuthStrategy {
|
||||||
|
private final String credentials;
|
||||||
|
|
||||||
|
public BasicAuthStrategy(final String username, final String password) {
|
||||||
|
new Base64();
|
||||||
|
credentials = new String(Base64.encode((username + ":" + password).getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() throws ProponoException {
|
||||||
|
// op-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuthentication(final HttpClient httpClient, final HttpMethodBase method) throws ProponoException {
|
||||||
|
httpClient.getParams().setAuthenticationPreemptive(true);
|
||||||
|
final String header = "Basic " + credentials;
|
||||||
|
method.setRequestHeader("Authorization", header);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
|
||||||
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
import org.jdom2.Document;
|
||||||
|
import org.jdom2.Element;
|
||||||
|
import org.jdom2.input.SAXBuilder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.AtomService;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class models an Atom Publising Protocol Service Document. It extends the common
|
||||||
|
* {@link com.rometools.rome.propono.atom.common.Collection} class to add a <code>getEntry()</code>
|
||||||
|
* method and to return {@link com.rometools.rome.propono.atom.client.ClientWorkspace} objects
|
||||||
|
* instead of common {@link com.rometools.rome.propono.atom.common.Workspace}s.
|
||||||
|
*/
|
||||||
|
public class ClientAtomService extends AtomService {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ClientAtomService.class);
|
||||||
|
|
||||||
|
private String uri = null;
|
||||||
|
private HttpClient httpClient = null;
|
||||||
|
private AuthStrategy authStrategy = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Atom blog service instance for specified URL and user account.
|
||||||
|
*
|
||||||
|
* @param url End-point URL of Atom service
|
||||||
|
*/
|
||||||
|
ClientAtomService(final String uri, final AuthStrategy authStrategy) throws ProponoException {
|
||||||
|
this.uri = uri;
|
||||||
|
this.authStrategy = authStrategy;
|
||||||
|
final Document doc = getAtomServiceDocument();
|
||||||
|
parseAtomServiceDocument(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get full entry from service by entry edit URI.
|
||||||
|
*/
|
||||||
|
public ClientEntry getEntry(final String uri) throws ProponoException {
|
||||||
|
final GetMethod method = new GetMethod(uri);
|
||||||
|
authStrategy.addAuthentication(httpClient, method);
|
||||||
|
try {
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
if (method.getStatusCode() != 200) {
|
||||||
|
throw new ProponoException("ERROR HTTP status code=" + method.getStatusCode());
|
||||||
|
}
|
||||||
|
final Entry romeEntry = Atom10Parser.parseEntry(new InputStreamReader(method.getResponseBodyAsStream()), uri, Locale.US);
|
||||||
|
if (!romeEntry.isMediaEntry()) {
|
||||||
|
return new ClientEntry(this, null, romeEntry, false);
|
||||||
|
} else {
|
||||||
|
return new ClientMediaEntry(this, null, romeEntry, false);
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR: getting or parsing entry/media", e);
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAuthentication(final HttpMethodBase method) throws ProponoException {
|
||||||
|
authStrategy.addAuthentication(httpClient, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthStrategy getAuthStrategy() {
|
||||||
|
return authStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Document getAtomServiceDocument() throws ProponoException {
|
||||||
|
GetMethod method = null;
|
||||||
|
final int code = -1;
|
||||||
|
try {
|
||||||
|
httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
|
||||||
|
// TODO: make connection timeout configurable
|
||||||
|
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
|
||||||
|
|
||||||
|
method = new GetMethod(uri);
|
||||||
|
authStrategy.addAuthentication(httpClient, method);
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
|
||||||
|
final SAXBuilder builder = new SAXBuilder();
|
||||||
|
final String doc = method.getResponseBodyAsString();
|
||||||
|
LOG.debug(doc);
|
||||||
|
return builder.build(method.getResponseBodyAsStream());
|
||||||
|
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
final String msg = "ERROR retrieving Atom Service Document, code: " + code;
|
||||||
|
LOG.debug(msg, t);
|
||||||
|
throw new ProponoException(msg, t);
|
||||||
|
} finally {
|
||||||
|
if (method != null) {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deserialize an Atom service XML document into an object */
|
||||||
|
private void parseAtomServiceDocument(final Document document) throws ProponoException {
|
||||||
|
final Element root = document.getRootElement();
|
||||||
|
final List<Element> spaces = root.getChildren("workspace", AtomService.ATOM_PROTOCOL);
|
||||||
|
for (final Element e : spaces) {
|
||||||
|
addWorkspace(new ClientWorkspace(e, this, uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package access to httpClient.
|
||||||
|
*/
|
||||||
|
HttpClient getHttpClient() {
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
import org.jdom2.Document;
|
||||||
|
import org.jdom2.Element;
|
||||||
|
import org.jdom2.JDOMException;
|
||||||
|
import org.jdom2.input.SAXBuilder;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models an Atom protocol Categories element, which may contain ROME Atom
|
||||||
|
* {@link com.rometools.rome.feed.atom.Category} elements.
|
||||||
|
*/
|
||||||
|
public class ClientCategories extends Categories {
|
||||||
|
private ClientCollection clientCollection = null;
|
||||||
|
|
||||||
|
/** Load select from XML element */
|
||||||
|
public ClientCategories(final Element e, final ClientCollection clientCollection) throws ProponoException {
|
||||||
|
this.clientCollection = clientCollection;
|
||||||
|
parseCategoriesElement(e);
|
||||||
|
if (getHref() != null) {
|
||||||
|
fetchContents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fetchContents() throws ProponoException {
|
||||||
|
final GetMethod method = new GetMethod(getHrefResolved());
|
||||||
|
clientCollection.addAuthentication(method);
|
||||||
|
try {
|
||||||
|
clientCollection.getHttpClient().executeMethod(method);
|
||||||
|
if (method.getStatusCode() != 200) {
|
||||||
|
throw new ProponoException("ERROR HTTP status code=" + method.getStatusCode());
|
||||||
|
}
|
||||||
|
final SAXBuilder builder = new SAXBuilder();
|
||||||
|
final Document catsDoc = builder.build(new InputStreamReader(method.getResponseBodyAsStream()));
|
||||||
|
parseCategoriesElement(catsDoc.getRootElement());
|
||||||
|
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
throw new ProponoException("ERROR: reading out-of-line categories", ioe);
|
||||||
|
} catch (final JDOMException jde) {
|
||||||
|
throw new ProponoException("ERROR: parsing out-of-line categories", jde);
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
|
||||||
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
import org.jdom2.Element;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.AtomService;
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.atom.common.Collection;
|
||||||
|
import com.rometools.propono.atom.common.Workspace;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models an Atom collection, extends Collection and adds methods for adding, retrieving, updateing
|
||||||
|
* and deleting entries.
|
||||||
|
*/
|
||||||
|
public class ClientCollection extends Collection {
|
||||||
|
|
||||||
|
private final boolean writable = true;
|
||||||
|
|
||||||
|
private HttpClient httpClient = null;
|
||||||
|
private AuthStrategy authStrategy = null;
|
||||||
|
private ClientWorkspace workspace = null;
|
||||||
|
private ClientAtomService service = null;
|
||||||
|
|
||||||
|
ClientCollection(final Element e, final ClientWorkspace workspace, final String baseURI) throws ProponoException {
|
||||||
|
super(e, baseURI);
|
||||||
|
this.workspace = workspace;
|
||||||
|
service = workspace.getAtomService();
|
||||||
|
httpClient = workspace.getAtomService().getHttpClient();
|
||||||
|
authStrategy = workspace.getAtomService().getAuthStrategy();
|
||||||
|
parseCollectionElement(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientCollection(final String href, final AuthStrategy authStrategy) throws ProponoException {
|
||||||
|
super("Standalone connection", "text", href);
|
||||||
|
this.authStrategy = authStrategy;
|
||||||
|
try {
|
||||||
|
httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
|
||||||
|
// TODO: make connection timeout configurable
|
||||||
|
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw new ProponoException("ERROR creating HTTPClient", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAuthentication(final HttpMethodBase method) throws ProponoException {
|
||||||
|
authStrategy.addAuthentication(httpClient, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package access to httpClient to allow use by ClientEntry and ClientMediaEntry.
|
||||||
|
*/
|
||||||
|
HttpClient getHttpClient() {
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get iterator over entries in this collection. Entries returned are considered to be partial
|
||||||
|
* entries cannot be saved/updated.
|
||||||
|
*/
|
||||||
|
public Iterator<ClientEntry> getEntries() throws ProponoException {
|
||||||
|
return new EntryIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get full entry specified by entry edit URI. Note that entry may or may not be associated with
|
||||||
|
* this collection.
|
||||||
|
*
|
||||||
|
* @return ClientEntry or ClientMediaEntry specified by URI.
|
||||||
|
*/
|
||||||
|
public ClientEntry getEntry(final String uri) throws ProponoException {
|
||||||
|
final GetMethod method = new GetMethod(uri);
|
||||||
|
authStrategy.addAuthentication(httpClient, method);
|
||||||
|
try {
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
if (method.getStatusCode() != 200) {
|
||||||
|
throw new ProponoException("ERROR HTTP status code=" + method.getStatusCode());
|
||||||
|
}
|
||||||
|
final Entry romeEntry = Atom10Parser.parseEntry(new InputStreamReader(method.getResponseBodyAsStream()), uri, Locale.US);
|
||||||
|
if (!romeEntry.isMediaEntry()) {
|
||||||
|
return new ClientEntry(service, this, romeEntry, false);
|
||||||
|
} else {
|
||||||
|
return new ClientMediaEntry(service, this, romeEntry, false);
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR: getting or parsing entry/media, HTTP code: ", e);
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get workspace or null if collection is not associated with a workspace.
|
||||||
|
*/
|
||||||
|
public Workspace getWorkspace() {
|
||||||
|
return workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if collection is writable.
|
||||||
|
*/
|
||||||
|
public boolean isWritable() {
|
||||||
|
return writable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new entry associated with collection, but do not save to server.
|
||||||
|
*
|
||||||
|
* @throws ProponoException if collecton is not writable.
|
||||||
|
*/
|
||||||
|
public ClientEntry createEntry() throws ProponoException {
|
||||||
|
if (!isWritable()) {
|
||||||
|
throw new ProponoException("Collection is not writable");
|
||||||
|
}
|
||||||
|
return new ClientEntry(service, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new media entry assocaited with collection, but do not save. server. Depending on the
|
||||||
|
* Atom server, you may or may not be able to persist the properties of the entry that is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* @param title Title to used for uploaded file.
|
||||||
|
* @param slug String to be used in file-name of stored file
|
||||||
|
* @param contentType MIME content-type of file.
|
||||||
|
* @param bytes Data to be uploaded as byte array.
|
||||||
|
* @throws ProponoException if collecton is not writable
|
||||||
|
*/
|
||||||
|
public ClientMediaEntry createMediaEntry(final String title, final String slug, final String contentType, final byte[] bytes) throws ProponoException {
|
||||||
|
if (!isWritable()) {
|
||||||
|
throw new ProponoException("Collection is not writable");
|
||||||
|
}
|
||||||
|
return new ClientMediaEntry(service, this, title, slug, contentType, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new media entry assocaited with collection, but do not save. server. Depending on the
|
||||||
|
* Atom server, you may or may not be able to. persist the properties of the entry that is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* @param title Title to used for uploaded file.
|
||||||
|
* @param slug String to be used in file-name of stored file
|
||||||
|
* @param contentType MIME content-type of file.
|
||||||
|
* @param is Data to be uploaded as InputStream.
|
||||||
|
* @throws ProponoException if collecton is not writable
|
||||||
|
*/
|
||||||
|
public ClientMediaEntry createMediaEntry(final String title, final String slug, final String contentType, final InputStream is) throws ProponoException {
|
||||||
|
if (!isWritable()) {
|
||||||
|
throw new ProponoException("Collection is not writable");
|
||||||
|
}
|
||||||
|
return new ClientMediaEntry(service, this, title, slug, contentType, is);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save to collection a new entry that was created by a createEntry() or createMediaEntry() and
|
||||||
|
* save it to the server.
|
||||||
|
*
|
||||||
|
* @param entry Entry to be saved.
|
||||||
|
* @throws ProponoException on error, if collection is not writable or if entry is partial.
|
||||||
|
*/
|
||||||
|
public void addEntry(final ClientEntry entry) throws ProponoException {
|
||||||
|
if (!isWritable()) {
|
||||||
|
throw new ProponoException("Collection is not writable");
|
||||||
|
}
|
||||||
|
entry.addToCollection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void parseCollectionElement(final Element element) throws ProponoException {
|
||||||
|
if (workspace == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setHref(element.getAttribute("href").getValue());
|
||||||
|
|
||||||
|
final Element titleElem = element.getChild("title", AtomService.ATOM_FORMAT);
|
||||||
|
if (titleElem != null) {
|
||||||
|
setTitle(titleElem.getText());
|
||||||
|
if (titleElem.getAttribute("type", AtomService.ATOM_FORMAT) != null) {
|
||||||
|
setTitleType(titleElem.getAttribute("type", AtomService.ATOM_FORMAT).getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Element> acceptElems = element.getChildren("accept", AtomService.ATOM_PROTOCOL);
|
||||||
|
if (acceptElems != null && !acceptElems.isEmpty()) {
|
||||||
|
for (final Element acceptElem : acceptElems) {
|
||||||
|
addAccept(acceptElem.getTextTrim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop to parse <app:categories> element to Categories objects
|
||||||
|
final List<Element> catsElems = element.getChildren("categories", AtomService.ATOM_PROTOCOL);
|
||||||
|
for (final Element catsElem : catsElems) {
|
||||||
|
final Categories cats = new ClientCategories(catsElem, this);
|
||||||
|
addCategories(cats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
267
src/main/java/com/rometools/propono/atom/client/ClientEntry.java
Normal file
267
src/main/java/com/rometools/propono/atom/client/ClientEntry.java
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.commons.beanutils.BeanUtils;
|
||||||
|
import org.apache.commons.httpclient.Header;
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
import org.apache.commons.httpclient.methods.DeleteMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.PostMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.PutMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.StringRequestEntity;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.propono.utils.Utilities;
|
||||||
|
import com.rometools.rome.feed.atom.Content;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Link;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Generator;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client implementation of Atom entry, extends ROME Entry to add methods for easily getting/setting
|
||||||
|
* content, updating and removing the entry from the server.
|
||||||
|
*/
|
||||||
|
public class ClientEntry extends Entry {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ClientEntry.class);
|
||||||
|
|
||||||
|
private ClientAtomService service = null;
|
||||||
|
private ClientCollection collection = null;
|
||||||
|
protected boolean partial = false;
|
||||||
|
|
||||||
|
public ClientEntry(final ClientAtomService service, final ClientCollection collection) {
|
||||||
|
this.service = service;
|
||||||
|
this.collection = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientEntry(final ClientAtomService service, final ClientCollection collection, final Entry entry, final boolean partial) throws ProponoException {
|
||||||
|
this.service = service;
|
||||||
|
this.collection = collection;
|
||||||
|
this.partial = partial;
|
||||||
|
try {
|
||||||
|
BeanUtils.copyProperties(this, entry);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR: copying fields from ROME entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set content of entry.
|
||||||
|
*
|
||||||
|
* @param contentString content string.
|
||||||
|
* @param type Must be "text" for plain text, "html" for escaped HTML, "xhtml" for XHTML or a
|
||||||
|
* valid MIME content-type.
|
||||||
|
*/
|
||||||
|
public void setContent(final String contentString, final String type) {
|
||||||
|
final Content newContent = new Content();
|
||||||
|
newContent.setType(type == null ? Content.HTML : type);
|
||||||
|
newContent.setValue(contentString);
|
||||||
|
final ArrayList<Content> contents = new ArrayList<Content>();
|
||||||
|
contents.add(newContent);
|
||||||
|
setContents(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to set first content object in content collection. Atom 1.0 allows only
|
||||||
|
* one content element per entry.
|
||||||
|
*/
|
||||||
|
public void setContent(final Content c) {
|
||||||
|
final ArrayList<Content> contents = new ArrayList<Content>();
|
||||||
|
contents.add(c);
|
||||||
|
setContents(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to get first content object in content collection. Atom 1.0 allows only
|
||||||
|
* one content element per entry.
|
||||||
|
*/
|
||||||
|
public Content getContent() {
|
||||||
|
if (getContents() != null && !getContents().isEmpty()) {
|
||||||
|
final Content c = getContents().get(0);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if entries are equal based on edit URI.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (o instanceof ClientEntry) {
|
||||||
|
final ClientEntry other = (ClientEntry) o;
|
||||||
|
if (other.getEditURI() != null && getEditURI() != null) {
|
||||||
|
return other.getEditURI().equals(getEditURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update entry by posting new representation of entry to server. Note that you should not
|
||||||
|
* attempt to update entries that you get from iterating over a collection they may be "partial"
|
||||||
|
* entries. If you want to update an entry, you must get it via one of the
|
||||||
|
* <code>getEntry()</code> methods in {@link com.rometools.rome.propono.atom.common.Collection}
|
||||||
|
* or {@link com.rometools.rome.propono.atom.common.AtomService}.
|
||||||
|
*
|
||||||
|
* @throws ProponoException If entry is a "partial" entry.
|
||||||
|
*/
|
||||||
|
public void update() throws ProponoException {
|
||||||
|
if (partial) {
|
||||||
|
throw new ProponoException("ERROR: attempt to update partial entry");
|
||||||
|
}
|
||||||
|
final EntityEnclosingMethod method = new PutMethod(getEditURI());
|
||||||
|
addAuthentication(method);
|
||||||
|
final StringWriter sw = new StringWriter();
|
||||||
|
final int code = -1;
|
||||||
|
try {
|
||||||
|
Atom10Generator.serializeEntry(this, sw);
|
||||||
|
method.setRequestEntity(new StringRequestEntity(sw.toString(), null, null));
|
||||||
|
method.setRequestHeader("Content-type", "application/atom+xml; charset=utf-8");
|
||||||
|
getHttpClient().executeMethod(method);
|
||||||
|
final InputStream is = method.getResponseBodyAsStream();
|
||||||
|
if (method.getStatusCode() != 200 && method.getStatusCode() != 201) {
|
||||||
|
throw new ProponoException("ERROR HTTP status=" + method.getStatusCode() + " : " + Utilities.streamToString(is));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
final String msg = "ERROR: updating entry, HTTP code: " + code;
|
||||||
|
LOG.debug(msg, e);
|
||||||
|
throw new ProponoException(msg, e);
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove entry from server.
|
||||||
|
*/
|
||||||
|
public void remove() throws ProponoException {
|
||||||
|
if (getEditURI() == null) {
|
||||||
|
throw new ProponoException("ERROR: cannot delete unsaved entry");
|
||||||
|
}
|
||||||
|
final DeleteMethod method = new DeleteMethod(getEditURI());
|
||||||
|
addAuthentication(method);
|
||||||
|
try {
|
||||||
|
getHttpClient().executeMethod(method);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new ProponoException("ERROR: removing entry, HTTP code", ex);
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCollection(final ClientCollection collection) {
|
||||||
|
this.collection = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientCollection getCollection() {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the URI that can be used to edit the entry via HTTP PUT or DELETE.
|
||||||
|
*/
|
||||||
|
public String getEditURI() {
|
||||||
|
for (int i = 0; i < getOtherLinks().size(); i++) {
|
||||||
|
final Link link = getOtherLinks().get(i);
|
||||||
|
if (link.getRel() != null && link.getRel().equals("edit")) {
|
||||||
|
return link.getHrefResolved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToCollection(final ClientCollection col) throws ProponoException {
|
||||||
|
setCollection(col);
|
||||||
|
final EntityEnclosingMethod method = new PostMethod(getCollection().getHrefResolved());
|
||||||
|
addAuthentication(method);
|
||||||
|
final StringWriter sw = new StringWriter();
|
||||||
|
int code = -1;
|
||||||
|
try {
|
||||||
|
Atom10Generator.serializeEntry(this, sw);
|
||||||
|
method.setRequestEntity(new StringRequestEntity(sw.toString(), null, null));
|
||||||
|
method.setRequestHeader("Content-type", "application/atom+xml; charset=utf-8");
|
||||||
|
getHttpClient().executeMethod(method);
|
||||||
|
final InputStream is = method.getResponseBodyAsStream();
|
||||||
|
code = method.getStatusCode();
|
||||||
|
if (code != 200 && code != 201) {
|
||||||
|
throw new ProponoException("ERROR HTTP status=" + code + " : " + Utilities.streamToString(is));
|
||||||
|
}
|
||||||
|
final Entry romeEntry = Atom10Parser.parseEntry(new InputStreamReader(is), getCollection().getHrefResolved(), Locale.US);
|
||||||
|
BeanUtils.copyProperties(this, romeEntry);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
final String msg = "ERROR: saving entry, HTTP code: " + code;
|
||||||
|
LOG.debug(msg, e);
|
||||||
|
throw new ProponoException(msg, e);
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
final Header locationHeader = method.getResponseHeader("Location");
|
||||||
|
if (locationHeader == null) {
|
||||||
|
LOG.warn("WARNING added entry, but no location header returned");
|
||||||
|
} else if (getEditURI() == null) {
|
||||||
|
final List<Link> links = getOtherLinks();
|
||||||
|
final Link link = new Link();
|
||||||
|
link.setHref(locationHeader.getValue());
|
||||||
|
link.setRel("edit");
|
||||||
|
links.add(link);
|
||||||
|
setOtherLinks(links);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAuthentication(final HttpMethodBase method) throws ProponoException {
|
||||||
|
if (service != null) {
|
||||||
|
service.addAuthentication(method);
|
||||||
|
} else if (collection != null) {
|
||||||
|
collection.addAuthentication(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpClient getHttpClient() {
|
||||||
|
if (service != null) {
|
||||||
|
return service.getHttpClient();
|
||||||
|
} else if (collection != null) {
|
||||||
|
return collection.getHttpClient();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreated(final Date d) {
|
||||||
|
// protected against null created property (an old Atom 0.3 property)
|
||||||
|
if (d != null) {
|
||||||
|
super.setCreated(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,304 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.commons.beanutils.BeanUtils;
|
||||||
|
import org.apache.commons.httpclient.Header;
|
||||||
|
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
|
||||||
|
import org.apache.commons.httpclient.methods.PostMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.PutMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.StringRequestEntity;
|
||||||
|
import org.jdom2.JDOMException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.propono.utils.Utilities;
|
||||||
|
import com.rometools.rome.feed.atom.Content;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Link;
|
||||||
|
import com.rometools.rome.io.FeedException;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Generator;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client implementation of Atom media-link entry, an Atom entry that provides meta-data for a media
|
||||||
|
* file (e.g. uploaded image or audio file).
|
||||||
|
*/
|
||||||
|
public class ClientMediaEntry extends ClientEntry {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ClientMediaEntry.class);
|
||||||
|
|
||||||
|
private String slug = null;
|
||||||
|
private byte[] bytes;
|
||||||
|
private InputStream inputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ClientMedieEntry for service and collection.
|
||||||
|
*/
|
||||||
|
public ClientMediaEntry(final ClientAtomService service, final ClientCollection collection) {
|
||||||
|
super(service, collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientMediaEntry(final ClientAtomService service, final ClientCollection collection, final Entry entry, final boolean partial)
|
||||||
|
throws ProponoException {
|
||||||
|
super(service, collection, entry, partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientMediaEntry(final ClientAtomService service, final ClientCollection collection, final String title, final String slug,
|
||||||
|
final String contentType, final InputStream is) {
|
||||||
|
this(service, collection);
|
||||||
|
inputStream = is;
|
||||||
|
setTitle(title);
|
||||||
|
setSlug(slug);
|
||||||
|
final Content content = new Content();
|
||||||
|
content.setType(contentType);
|
||||||
|
final List<Content> contents = new ArrayList<Content>();
|
||||||
|
contents.add(content);
|
||||||
|
setContents(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientMediaEntry(final ClientAtomService service, final ClientCollection collection, final String title, final String slug,
|
||||||
|
final String contentType, final byte[] bytes) {
|
||||||
|
this(service, collection);
|
||||||
|
this.bytes = bytes;
|
||||||
|
setTitle(title);
|
||||||
|
setSlug(slug);
|
||||||
|
final Content content = new Content();
|
||||||
|
content.setType(contentType);
|
||||||
|
final List<Content> contents = new ArrayList<Content>();
|
||||||
|
contents.add(content);
|
||||||
|
setContents(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get bytes of media resource associated with entry.
|
||||||
|
*
|
||||||
|
* @return Bytes or null if none available or if entry uses an InputStream instead.
|
||||||
|
*/
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set media resource data as a byte array, don't try this if you have already set the data as
|
||||||
|
* an InputStream.
|
||||||
|
*/
|
||||||
|
public void setBytes(final byte[] bytes) {
|
||||||
|
if (inputStream != null) {
|
||||||
|
throw new IllegalStateException("ERROR: already has inputStream, cannot set both inputStream and bytes");
|
||||||
|
}
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get input stream for media resource associated with this entry.
|
||||||
|
*
|
||||||
|
* @return InputStream or null if none available or if entry uses bytes instead.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set media resource data as an input stream, don't try this if you have already set the data
|
||||||
|
* as a byte array.
|
||||||
|
*/
|
||||||
|
public void setInputStream(final InputStream inputStream) {
|
||||||
|
if (bytes != null) {
|
||||||
|
throw new IllegalStateException("ERROR: already has bytes, cannot set both bytes and inputStream");
|
||||||
|
}
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get media link URI for editing the media resource associated with this entry via HTTP PUT or
|
||||||
|
* DELETE.
|
||||||
|
*/
|
||||||
|
public String getMediaLinkURI() {
|
||||||
|
for (int i = 0; i < getOtherLinks().size(); i++) {
|
||||||
|
final Link link = getOtherLinks().get(i);
|
||||||
|
if (link.getRel() != null && link.getRel().equals("edit-media")) {
|
||||||
|
return link.getHrefResolved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get media resource as an InputStream, should work regardless of whether you set the media
|
||||||
|
* resource data as an InputStream or as a byte array.
|
||||||
|
*/
|
||||||
|
public InputStream getAsStream() throws ProponoException {
|
||||||
|
if (getContents() != null && !getContents().isEmpty()) {
|
||||||
|
final Content c = getContents().get(0);
|
||||||
|
if (c.getSrc() != null) {
|
||||||
|
return getResourceAsStream();
|
||||||
|
} else if (inputStream != null) {
|
||||||
|
return inputStream;
|
||||||
|
} else if (bytes != null) {
|
||||||
|
return new ByteArrayInputStream(bytes);
|
||||||
|
} else {
|
||||||
|
throw new ProponoException("ERROR: no src URI or binary data to return");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ProponoException("ERROR: no content found in entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getResourceAsStream() throws ProponoException {
|
||||||
|
if (getEditURI() == null) {
|
||||||
|
throw new ProponoException("ERROR: not yet saved to server");
|
||||||
|
}
|
||||||
|
final GetMethod method = new GetMethod(((Content) getContents()).getSrc());
|
||||||
|
try {
|
||||||
|
getCollection().getHttpClient().executeMethod(method);
|
||||||
|
if (method.getStatusCode() != 200) {
|
||||||
|
throw new ProponoException("ERROR HTTP status=" + method.getStatusCode());
|
||||||
|
}
|
||||||
|
return method.getResponseBodyAsStream();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
throw new ProponoException("ERROR: getting media entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update entry on server.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void update() throws ProponoException {
|
||||||
|
if (partial) {
|
||||||
|
throw new ProponoException("ERROR: attempt to update partial entry");
|
||||||
|
}
|
||||||
|
EntityEnclosingMethod method = null;
|
||||||
|
final Content updateContent = getContents().get(0);
|
||||||
|
try {
|
||||||
|
if (getMediaLinkURI() != null && getBytes() != null) {
|
||||||
|
// existing media entry and new file, so PUT file to edit-media URI
|
||||||
|
method = new PutMethod(getMediaLinkURI());
|
||||||
|
if (inputStream != null) {
|
||||||
|
method.setRequestEntity(new InputStreamRequestEntity(inputStream));
|
||||||
|
} else {
|
||||||
|
method.setRequestEntity(new InputStreamRequestEntity(new ByteArrayInputStream(getBytes())));
|
||||||
|
}
|
||||||
|
|
||||||
|
method.setRequestHeader("Content-type", updateContent.getType());
|
||||||
|
} else if (getEditURI() != null) {
|
||||||
|
// existing media entry and NO new file, so PUT entry to edit URI
|
||||||
|
method = new PutMethod(getEditURI());
|
||||||
|
final StringWriter sw = new StringWriter();
|
||||||
|
Atom10Generator.serializeEntry(this, sw);
|
||||||
|
method.setRequestEntity(new StringRequestEntity(sw.toString(), null, null));
|
||||||
|
method.setRequestHeader("Content-type", "application/atom+xml; charset=utf8");
|
||||||
|
} else {
|
||||||
|
throw new ProponoException("ERROR: media entry has no edit URI or media-link URI");
|
||||||
|
}
|
||||||
|
getCollection().addAuthentication(method);
|
||||||
|
method.addRequestHeader("Title", getTitle());
|
||||||
|
getCollection().getHttpClient().executeMethod(method);
|
||||||
|
if (inputStream != null) {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
final InputStream is = method.getResponseBodyAsStream();
|
||||||
|
if (method.getStatusCode() != 200 && method.getStatusCode() != 201) {
|
||||||
|
throw new ProponoException("ERROR HTTP status=" + method.getStatusCode() + " : " + Utilities.streamToString(is));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR: saving media entry");
|
||||||
|
}
|
||||||
|
if (method.getStatusCode() != 201) {
|
||||||
|
throw new ProponoException("ERROR HTTP status=" + method.getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Package access, to be called by DefaultClientCollection */
|
||||||
|
@Override
|
||||||
|
void addToCollection(final ClientCollection col) throws ProponoException {
|
||||||
|
setCollection(col);
|
||||||
|
final EntityEnclosingMethod method = new PostMethod(col.getHrefResolved());
|
||||||
|
getCollection().addAuthentication(method);
|
||||||
|
try {
|
||||||
|
final Content c = getContents().get(0);
|
||||||
|
if (inputStream != null) {
|
||||||
|
method.setRequestEntity(new InputStreamRequestEntity(inputStream));
|
||||||
|
} else {
|
||||||
|
method.setRequestEntity(new InputStreamRequestEntity(new ByteArrayInputStream(getBytes())));
|
||||||
|
}
|
||||||
|
method.setRequestHeader("Content-type", c.getType());
|
||||||
|
method.setRequestHeader("Title", getTitle());
|
||||||
|
method.setRequestHeader("Slug", getSlug());
|
||||||
|
getCollection().getHttpClient().executeMethod(method);
|
||||||
|
if (inputStream != null) {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
final InputStream is = method.getResponseBodyAsStream();
|
||||||
|
if (method.getStatusCode() == 200 || method.getStatusCode() == 201) {
|
||||||
|
final Entry romeEntry = Atom10Parser.parseEntry(new InputStreamReader(is), col.getHrefResolved(), Locale.US);
|
||||||
|
BeanUtils.copyProperties(this, romeEntry);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new ProponoException("ERROR HTTP status-code=" + method.getStatusCode() + " status-line: " + method.getStatusLine());
|
||||||
|
}
|
||||||
|
} catch (final IOException ie) {
|
||||||
|
throw new ProponoException("ERROR: saving media entry", ie);
|
||||||
|
} catch (final JDOMException je) {
|
||||||
|
throw new ProponoException("ERROR: saving media entry", je);
|
||||||
|
} catch (final FeedException fe) {
|
||||||
|
throw new ProponoException("ERROR: saving media entry", fe);
|
||||||
|
} catch (final IllegalAccessException ae) {
|
||||||
|
throw new ProponoException("ERROR: saving media entry", ae);
|
||||||
|
} catch (final InvocationTargetException te) {
|
||||||
|
throw new ProponoException("ERROR: saving media entry", te);
|
||||||
|
}
|
||||||
|
final Header locationHeader = method.getResponseHeader("Location");
|
||||||
|
if (locationHeader == null) {
|
||||||
|
LOG.warn("WARNING added entry, but no location header returned");
|
||||||
|
} else if (getEditURI() == null) {
|
||||||
|
final List<Link> links = getOtherLinks();
|
||||||
|
final Link link = new Link();
|
||||||
|
link.setHref(locationHeader.getValue());
|
||||||
|
link.setRel("edit");
|
||||||
|
links.add(link);
|
||||||
|
setOtherLinks(links);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set string to be used in file name of new media resource on server. */
|
||||||
|
public String getSlug() {
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get string to be used in file name of new media resource on server. */
|
||||||
|
public void setSlug(final String slug) {
|
||||||
|
this.slug = slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jdom2.Element;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.AtomService;
|
||||||
|
import com.rometools.propono.atom.common.Workspace;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents Atom protocol workspace on client-side. It extends the common
|
||||||
|
* {@link com.rometools.rome.propono.atom.common.Workspace} to return
|
||||||
|
* {@link com.rometools.rome.propono.atom.client.ClientCollection} objects instead of common
|
||||||
|
* {@link com.rometools.rome.propono.atom.common.Collection}s.
|
||||||
|
*/
|
||||||
|
public class ClientWorkspace extends Workspace {
|
||||||
|
|
||||||
|
private ClientAtomService atomService = null;
|
||||||
|
|
||||||
|
ClientWorkspace(final Element e, final ClientAtomService atomService, final String baseURI) throws ProponoException {
|
||||||
|
super("dummy", "dummy");
|
||||||
|
this.atomService = atomService;
|
||||||
|
parseWorkspaceElement(e, baseURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package access to parent service.
|
||||||
|
*/
|
||||||
|
ClientAtomService getAtomService() {
|
||||||
|
return atomService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deserialize a Atom workspace XML element into an object */
|
||||||
|
protected void parseWorkspaceElement(final Element element, final String baseURI) throws ProponoException {
|
||||||
|
final Element titleElem = element.getChild("title", AtomService.ATOM_FORMAT);
|
||||||
|
setTitle(titleElem.getText());
|
||||||
|
if (titleElem.getAttribute("type", AtomService.ATOM_FORMAT) != null) {
|
||||||
|
setTitleType(titleElem.getAttribute("type", AtomService.ATOM_FORMAT).getValue());
|
||||||
|
}
|
||||||
|
final List<Element> collections = element.getChildren("collection", AtomService.ATOM_PROTOCOL);
|
||||||
|
for (final Element e : collections) {
|
||||||
|
addCollection(new ClientCollection(e, this, baseURI));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
import org.jdom2.Document;
|
||||||
|
import org.jdom2.input.SAXBuilder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Feed;
|
||||||
|
import com.rometools.rome.feed.atom.Link;
|
||||||
|
import com.rometools.rome.io.WireFeedInput;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables iteration over entries in Atom protocol collection.
|
||||||
|
*/
|
||||||
|
public class EntryIterator implements Iterator<ClientEntry> {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(EntryIterator.class);
|
||||||
|
|
||||||
|
private final ClientCollection collection;
|
||||||
|
|
||||||
|
private Iterator<Entry> members = null;
|
||||||
|
private Feed col = null;
|
||||||
|
private final String collectionURI;
|
||||||
|
private String nextURI;
|
||||||
|
|
||||||
|
EntryIterator(final ClientCollection collection) throws ProponoException {
|
||||||
|
this.collection = collection;
|
||||||
|
collectionURI = collection.getHrefResolved();
|
||||||
|
nextURI = collectionURI;
|
||||||
|
getNextEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if more entries are available.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (!members.hasNext()) {
|
||||||
|
try {
|
||||||
|
getNextEntries();
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
LOG.error("An error occured while getting next entries", ignored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return members.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get next entry in collection.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ClientEntry next() {
|
||||||
|
if (hasNext()) {
|
||||||
|
final Entry romeEntry = members.next();
|
||||||
|
try {
|
||||||
|
if (!romeEntry.isMediaEntry()) {
|
||||||
|
return new ClientEntry(null, collection, romeEntry, true);
|
||||||
|
} else {
|
||||||
|
return new ClientMediaEntry(null, collection, romeEntry, true);
|
||||||
|
}
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
throw new RuntimeException("Unexpected exception creating ClientEntry or ClientMedia", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove entry is not implemented.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
// optional method, not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getNextEntries() throws ProponoException {
|
||||||
|
if (nextURI == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final GetMethod colGet = new GetMethod(collection.getHrefResolved(nextURI));
|
||||||
|
collection.addAuthentication(colGet);
|
||||||
|
try {
|
||||||
|
collection.getHttpClient().executeMethod(colGet);
|
||||||
|
final SAXBuilder builder = new SAXBuilder();
|
||||||
|
final Document doc = builder.build(colGet.getResponseBodyAsStream());
|
||||||
|
final WireFeedInput feedInput = new WireFeedInput();
|
||||||
|
col = (Feed) feedInput.build(doc);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR: fetching or parsing next entries, HTTP code: " + (colGet != null ? colGet.getStatusCode() : -1), e);
|
||||||
|
} finally {
|
||||||
|
colGet.releaseConnection();
|
||||||
|
}
|
||||||
|
members = col.getEntries().iterator();
|
||||||
|
col.getEntries().size();
|
||||||
|
|
||||||
|
nextURI = null;
|
||||||
|
final List<Link> altLinks = col.getOtherLinks();
|
||||||
|
if (altLinks != null) {
|
||||||
|
for (final Link link : altLinks) {
|
||||||
|
if ("next".equals(link.getRel())) {
|
||||||
|
nextURI = link.getHref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
import org.apache.commons.httpclient.NameValuePair;
|
||||||
|
import org.apache.commons.httpclient.methods.PostMethod;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
public class GDataAuthStrategy implements AuthStrategy {
|
||||||
|
private final String email;
|
||||||
|
private final String password;
|
||||||
|
private final String service;
|
||||||
|
private String authToken;
|
||||||
|
|
||||||
|
public GDataAuthStrategy(final String email, final String password, final String service) throws ProponoException {
|
||||||
|
this.email = email;
|
||||||
|
this.password = password;
|
||||||
|
this.service = service;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() throws ProponoException {
|
||||||
|
try {
|
||||||
|
final HttpClient httpClient = new HttpClient();
|
||||||
|
final PostMethod method = new PostMethod("https://www.google.com/accounts/ClientLogin");
|
||||||
|
final NameValuePair[] data = { new NameValuePair("Email", email), new NameValuePair("Passwd", password),
|
||||||
|
new NameValuePair("accountType", "GOOGLE"), new NameValuePair("service", service),
|
||||||
|
new NameValuePair("source", "ROME Propono Atompub Client") };
|
||||||
|
method.setRequestBody(data);
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
|
||||||
|
final String responseBody = method.getResponseBodyAsString();
|
||||||
|
final int authIndex = responseBody.indexOf("Auth=");
|
||||||
|
|
||||||
|
authToken = "GoogleLogin auth=" + responseBody.trim().substring(authIndex + 5);
|
||||||
|
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
throw new ProponoException("ERROR obtaining Google authentication string", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuthentication(final HttpClient httpClient, final HttpMethodBase method) throws ProponoException {
|
||||||
|
httpClient.getParams().setAuthenticationPreemptive(true);
|
||||||
|
method.setRequestHeader("Authorization", authToken);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No authentication
|
||||||
|
*/
|
||||||
|
public class NoAuthStrategy implements AuthStrategy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuthentication(final HttpClient httpClient, final HttpMethodBase method) throws ProponoException {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,295 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import net.oauth.OAuth;
|
||||||
|
import net.oauth.OAuthAccessor;
|
||||||
|
import net.oauth.OAuthConsumer;
|
||||||
|
import net.oauth.OAuthMessage;
|
||||||
|
import net.oauth.OAuthServiceProvider;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
import org.apache.commons.httpclient.NameValuePair;
|
||||||
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.PostMethod;
|
||||||
|
import org.apache.commons.httpclient.util.ParameterParser;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy for using OAuth.
|
||||||
|
*/
|
||||||
|
public class OAuthStrategy implements AuthStrategy {
|
||||||
|
|
||||||
|
private State state = State.UNAUTHORIZED;
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
UNAUTHORIZED, // have not sent any requests
|
||||||
|
REQUEST_TOKEN, // have a request token
|
||||||
|
AUTHORIZED, // are authorized
|
||||||
|
ACCESS_TOKEN // have access token, ready to make calls
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
private final String consumerKey;
|
||||||
|
private final String consumerSecret;
|
||||||
|
private final String keyType;
|
||||||
|
|
||||||
|
private final String reqUrl;
|
||||||
|
private final String authzUrl;
|
||||||
|
private final String accessUrl;
|
||||||
|
|
||||||
|
private final String nonce;
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
private String requestToken = null;
|
||||||
|
private String accessToken = null;
|
||||||
|
private String tokenSecret = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create OAuth authentcation strategy and negotiate with services to obtain access token to be
|
||||||
|
* used in subsequent calls.
|
||||||
|
*
|
||||||
|
* @param username Username to be used in authentication
|
||||||
|
* @param key Consumer key
|
||||||
|
* @param secret Consumer secret
|
||||||
|
* @param keyType Key type (e.g. "HMAC-SHA1")
|
||||||
|
* @param reqUrl URL of request token service
|
||||||
|
* @param authzUrl URL of authorize service
|
||||||
|
* @param accessUrl URL of acess token service
|
||||||
|
* @throws ProponoException on any sort of initialization error
|
||||||
|
*/
|
||||||
|
public OAuthStrategy(final String username, final String key, final String secret, final String keyType, final String reqUrl, final String authzUrl,
|
||||||
|
final String accessUrl) throws ProponoException {
|
||||||
|
|
||||||
|
this.username = username;
|
||||||
|
this.reqUrl = reqUrl;
|
||||||
|
this.authzUrl = authzUrl;
|
||||||
|
this.accessUrl = accessUrl;
|
||||||
|
consumerKey = key;
|
||||||
|
consumerSecret = secret;
|
||||||
|
this.keyType = keyType;
|
||||||
|
|
||||||
|
nonce = UUID.randomUUID().toString();
|
||||||
|
timestamp = new Date().getTime() / 1000L;
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() throws ProponoException {
|
||||||
|
callOAuthUri(reqUrl);
|
||||||
|
callOAuthUri(authzUrl);
|
||||||
|
callOAuthUri(accessUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuthentication(final HttpClient httpClient, final HttpMethodBase method) throws ProponoException {
|
||||||
|
|
||||||
|
if (state != State.ACCESS_TOKEN) {
|
||||||
|
throw new ProponoException("ERROR: authentication strategy failed init");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add OAuth name/values to request query string
|
||||||
|
|
||||||
|
// wish we didn't have to parse them apart first, ugh
|
||||||
|
List<NameValuePair> originalqlist = null;
|
||||||
|
if (method.getQueryString() != null) {
|
||||||
|
String qstring = method.getQueryString().trim();
|
||||||
|
qstring = qstring.startsWith("?") ? qstring.substring(1) : qstring;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<NameValuePair> parameters = new ParameterParser().parse(qstring, '&');
|
||||||
|
originalqlist = parameters;
|
||||||
|
} else {
|
||||||
|
originalqlist = new ArrayList<NameValuePair>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// put query string into hashmap form to please OAuth.net classes
|
||||||
|
final Map<String, String> params = new HashMap<String, String>();
|
||||||
|
for (final Object element : originalqlist) {
|
||||||
|
final NameValuePair pair = (NameValuePair) element;
|
||||||
|
params.put(pair.getName(), pair.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// add OAuth params to query string
|
||||||
|
params.put("xoauth_requestor_id", username);
|
||||||
|
params.put("oauth_consumer_key", consumerKey);
|
||||||
|
params.put("oauth_signature_method", keyType);
|
||||||
|
params.put("oauth_timestamp", Long.toString(timestamp));
|
||||||
|
params.put("oauth_nonce", nonce);
|
||||||
|
params.put("oauth_token", accessToken);
|
||||||
|
params.put("oauth_token_secret", tokenSecret);
|
||||||
|
|
||||||
|
// sign complete URI
|
||||||
|
String finalUri = null;
|
||||||
|
final OAuthServiceProvider provider = new OAuthServiceProvider(reqUrl, authzUrl, accessUrl);
|
||||||
|
final OAuthConsumer consumer = new OAuthConsumer(null, consumerKey, consumerSecret, provider);
|
||||||
|
final OAuthAccessor accessor = new OAuthAccessor(consumer);
|
||||||
|
accessor.tokenSecret = tokenSecret;
|
||||||
|
OAuthMessage message;
|
||||||
|
try {
|
||||||
|
message = new OAuthMessage(method.getName(), method.getURI().toString(), params.entrySet());
|
||||||
|
message.sign(accessor);
|
||||||
|
|
||||||
|
finalUri = OAuth.addParameters(message.URL, message.getParameters());
|
||||||
|
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw new ProponoException("ERROR: OAuth signing request", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pull query string off and put it back onto method
|
||||||
|
method.setQueryString(finalUri.substring(finalUri.lastIndexOf("?")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void callOAuthUri(final String uri) throws ProponoException {
|
||||||
|
|
||||||
|
final HttpClient httpClient = new HttpClient();
|
||||||
|
|
||||||
|
final HttpMethodBase method;
|
||||||
|
final String content;
|
||||||
|
|
||||||
|
final Map<String, String> params = new HashMap<String, String>();
|
||||||
|
params.put("oauth_version", "1.0");
|
||||||
|
if (username != null) {
|
||||||
|
params.put("xoauth_requestor_id", username);
|
||||||
|
}
|
||||||
|
params.put("oauth_consumer_key", consumerKey);
|
||||||
|
params.put("oauth_signature_method", keyType);
|
||||||
|
params.put("oauth_timestamp", Long.toString(timestamp));
|
||||||
|
params.put("oauth_nonce", nonce);
|
||||||
|
params.put("oauth_callback", "none");
|
||||||
|
|
||||||
|
final OAuthServiceProvider provider = new OAuthServiceProvider(reqUrl, authzUrl, accessUrl);
|
||||||
|
final OAuthConsumer consumer = new OAuthConsumer(null, consumerKey, consumerSecret, provider);
|
||||||
|
final OAuthAccessor accessor = new OAuthAccessor(consumer);
|
||||||
|
|
||||||
|
if (state == State.UNAUTHORIZED) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
final OAuthMessage message = new OAuthMessage("GET", uri, params.entrySet());
|
||||||
|
message.sign(accessor);
|
||||||
|
|
||||||
|
final String finalUri = OAuth.addParameters(message.URL, message.getParameters());
|
||||||
|
method = new GetMethod(finalUri);
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
content = method.getResponseBodyAsString();
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR fetching request token", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (state == State.REQUEST_TOKEN) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
params.put("oauth_token", requestToken);
|
||||||
|
params.put("oauth_token_secret", tokenSecret);
|
||||||
|
accessor.tokenSecret = tokenSecret;
|
||||||
|
|
||||||
|
final OAuthMessage message = new OAuthMessage("POST", uri, params.entrySet());
|
||||||
|
message.sign(accessor);
|
||||||
|
|
||||||
|
final String finalUri = OAuth.addParameters(message.URL, message.getParameters());
|
||||||
|
method = new PostMethod(finalUri);
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
content = method.getResponseBodyAsString();
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR fetching request token", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (state == State.AUTHORIZED) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
params.put("oauth_token", accessToken);
|
||||||
|
params.put("oauth_token_secret", tokenSecret);
|
||||||
|
accessor.tokenSecret = tokenSecret;
|
||||||
|
|
||||||
|
final OAuthMessage message = new OAuthMessage("GET", uri, params.entrySet());
|
||||||
|
message.sign(accessor);
|
||||||
|
|
||||||
|
final String finalUri = OAuth.addParameters(message.URL, message.getParameters());
|
||||||
|
method = new GetMethod(finalUri);
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
content = method.getResponseBodyAsString();
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProponoException("ERROR fetching request token", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
method = null;
|
||||||
|
content = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = null;
|
||||||
|
String secret = null;
|
||||||
|
|
||||||
|
if (content != null) {
|
||||||
|
final String[] settings = content.split("&");
|
||||||
|
for (final String setting2 : settings) {
|
||||||
|
final String[] setting = setting2.split("=");
|
||||||
|
if (setting.length > 1) {
|
||||||
|
if ("oauth_token".equals(setting[0])) {
|
||||||
|
token = setting[1];
|
||||||
|
} else if ("oauth_token_secret".equals(setting[0])) {
|
||||||
|
secret = setting[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO review switch without 'default'
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
|
||||||
|
case UNAUTHORIZED:
|
||||||
|
if (token != null && secret != null) {
|
||||||
|
requestToken = token;
|
||||||
|
tokenSecret = secret;
|
||||||
|
state = State.REQUEST_TOKEN;
|
||||||
|
} else {
|
||||||
|
throw new ProponoException("ERROR: requestToken or tokenSecret is null");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REQUEST_TOKEN:
|
||||||
|
if (method.getStatusCode() == 200) {
|
||||||
|
state = State.AUTHORIZED;
|
||||||
|
} else {
|
||||||
|
throw new ProponoException("ERROR: authorization returned code: " + method.getStatusCode());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUTHORIZED:
|
||||||
|
if (token != null && secret != null) {
|
||||||
|
accessToken = token;
|
||||||
|
tokenSecret = secret;
|
||||||
|
state = State.ACCESS_TOKEN;
|
||||||
|
} else {
|
||||||
|
throw new ProponoException("ERROR: accessToken or tokenSecret is null");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
115
src/main/java/com/rometools/propono/atom/common/AtomService.java
Normal file
115
src/main/java/com/rometools/propono/atom/common/AtomService.java
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jdom2.Document;
|
||||||
|
import org.jdom2.Element;
|
||||||
|
import org.jdom2.Namespace;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models an Atom Publishing Protocol Service Document. Is able to read a Service document from a
|
||||||
|
* JDOM Document and to write Service document out as a JDOM Document.
|
||||||
|
*/
|
||||||
|
public class AtomService {
|
||||||
|
|
||||||
|
private List<Workspace> workspaces = new ArrayList<Workspace>();
|
||||||
|
|
||||||
|
/** Namespace for Atom Syndication Format */
|
||||||
|
public static Namespace ATOM_FORMAT = Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom");
|
||||||
|
|
||||||
|
/** Namespace for Atom Publishing Protocol */
|
||||||
|
public static Namespace ATOM_PROTOCOL = Namespace.getNamespace("app", "http://www.w3.org/2007/app");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new and empty Atom service
|
||||||
|
*/
|
||||||
|
public AtomService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Workspace to service.
|
||||||
|
*/
|
||||||
|
public void addWorkspace(final Workspace workspace) {
|
||||||
|
workspaces.add(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Workspaces available from service.
|
||||||
|
*/
|
||||||
|
public List<Workspace> getWorkspaces() {
|
||||||
|
return workspaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Workspaces of service.
|
||||||
|
*/
|
||||||
|
public void setWorkspaces(final List<Workspace> workspaces) {
|
||||||
|
this.workspaces = workspaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find workspace by title.
|
||||||
|
*
|
||||||
|
* @param title Match this title
|
||||||
|
* @return Matching Workspace or null if none found.
|
||||||
|
*/
|
||||||
|
public Workspace findWorkspace(final String title) {
|
||||||
|
for (final Object element : workspaces) {
|
||||||
|
final Workspace ws = (Workspace) element;
|
||||||
|
if (title.equals(ws.getTitle())) {
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize an Atom service XML document into an object
|
||||||
|
*/
|
||||||
|
public static AtomService documentToService(final Document document) throws ProponoException {
|
||||||
|
final AtomService service = new AtomService();
|
||||||
|
final Element root = document.getRootElement();
|
||||||
|
final List<Element> spaces = root.getChildren("workspace", ATOM_PROTOCOL);
|
||||||
|
for (final Element e : spaces) {
|
||||||
|
service.addWorkspace(Workspace.elementToWorkspace(e));
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize an AtomService object into an XML document
|
||||||
|
*/
|
||||||
|
public Document serviceToDocument() {
|
||||||
|
final AtomService service = this;
|
||||||
|
|
||||||
|
final Document doc = new Document();
|
||||||
|
final Element root = new Element("service", ATOM_PROTOCOL);
|
||||||
|
doc.setRootElement(root);
|
||||||
|
final List<Workspace> spaces = service.getWorkspaces();
|
||||||
|
for (final Workspace space : spaces) {
|
||||||
|
root.addContent(space.workspaceToElement());
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
159
src/main/java/com/rometools/propono/atom/common/Categories.java
Normal file
159
src/main/java/com/rometools/propono/atom/common/Categories.java
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jdom2.Element;
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.atom.Category;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models an Atom protocol Categories element, which may contain ROME Atom
|
||||||
|
* {@link com.rometools.rome.feed.atom.Category} elements.
|
||||||
|
*/
|
||||||
|
public class Categories {
|
||||||
|
|
||||||
|
private final List<Category> categories = new ArrayList<Category>();
|
||||||
|
private String baseURI = null;
|
||||||
|
private Element categoriesElement = null;
|
||||||
|
private String href = null;
|
||||||
|
private String scheme = null;
|
||||||
|
private boolean fixed = false;
|
||||||
|
|
||||||
|
public Categories() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load select from XML element */
|
||||||
|
public Categories(final Element e, final String baseURI) {
|
||||||
|
categoriesElement = e;
|
||||||
|
this.baseURI = baseURI;
|
||||||
|
parseCategoriesElement(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add category list of those specified */
|
||||||
|
public void addCategory(final Category cat) {
|
||||||
|
categories.add(cat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over Category objects
|
||||||
|
*
|
||||||
|
* @return List of ROME Atom {@link com.rometools.rome.feed.atom.Category}
|
||||||
|
*/
|
||||||
|
public List<Category> getCategories() {
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True if clients MUST use one of the categories specified */
|
||||||
|
public boolean isFixed() {
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True if clients MUST use one of the categories specified */
|
||||||
|
public void setFixed(final boolean fixed) {
|
||||||
|
this.fixed = fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Category URI scheme to use for Categories without a scheme */
|
||||||
|
public String getScheme() {
|
||||||
|
return scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Category URI scheme to use for Categories without a scheme */
|
||||||
|
public void setScheme(final String scheme) {
|
||||||
|
this.scheme = scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** URI of out-of-line categories */
|
||||||
|
public String getHref() {
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** URI of out-of-line categories */
|
||||||
|
public void setHref(final String href) {
|
||||||
|
this.href = href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get unresolved URI of the collection, or null if impossible to determine */
|
||||||
|
public String getHrefResolved() {
|
||||||
|
if (Atom10Parser.isAbsoluteURI(href)) {
|
||||||
|
return href;
|
||||||
|
} else if (baseURI != null && categoriesElement != null) {
|
||||||
|
return Atom10Parser.resolveURI(baseURI, categoriesElement, href);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Element categoriesToElement() {
|
||||||
|
final Categories cats = this;
|
||||||
|
final Element catsElem = new Element("categories", AtomService.ATOM_PROTOCOL);
|
||||||
|
catsElem.setAttribute("fixed", cats.isFixed() ? "yes" : "no", AtomService.ATOM_PROTOCOL);
|
||||||
|
if (cats.getScheme() != null) {
|
||||||
|
catsElem.setAttribute("scheme", cats.getScheme(), AtomService.ATOM_PROTOCOL);
|
||||||
|
}
|
||||||
|
if (cats.getHref() != null) {
|
||||||
|
catsElem.setAttribute("href", cats.getHref(), AtomService.ATOM_PROTOCOL);
|
||||||
|
} else {
|
||||||
|
// Loop to create <atom:category> elements
|
||||||
|
for (final Object element : cats.getCategories()) {
|
||||||
|
final Category cat = (Category) element;
|
||||||
|
final Element catElem = new Element("category", AtomService.ATOM_FORMAT);
|
||||||
|
catElem.setAttribute("term", cat.getTerm(), AtomService.ATOM_FORMAT);
|
||||||
|
if (cat.getScheme() != null) { // optional
|
||||||
|
catElem.setAttribute("scheme", cat.getScheme(), AtomService.ATOM_FORMAT);
|
||||||
|
}
|
||||||
|
if (cat.getLabel() != null) { // optional
|
||||||
|
catElem.setAttribute("label", cat.getLabel(), AtomService.ATOM_FORMAT);
|
||||||
|
}
|
||||||
|
catsElem.addContent(catElem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return catsElem;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parseCategoriesElement(final Element catsElem) {
|
||||||
|
|
||||||
|
if (catsElem.getAttribute("href", AtomService.ATOM_PROTOCOL) != null) {
|
||||||
|
setHref(catsElem.getAttribute("href", AtomService.ATOM_PROTOCOL).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (catsElem.getAttribute("fixed", AtomService.ATOM_PROTOCOL) != null) {
|
||||||
|
if ("yes".equals(catsElem.getAttribute("fixed", AtomService.ATOM_PROTOCOL).getValue())) {
|
||||||
|
setFixed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (catsElem.getAttribute("scheme", AtomService.ATOM_PROTOCOL) != null) {
|
||||||
|
setScheme(catsElem.getAttribute("scheme", AtomService.ATOM_PROTOCOL).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop to parse <atom:category> elemenents to Category objects
|
||||||
|
final List<Element> catElems = catsElem.getChildren("category", AtomService.ATOM_FORMAT);
|
||||||
|
for (final Element catElem : catElems) {
|
||||||
|
final Category cat = new Category();
|
||||||
|
cat.setTerm(catElem.getAttributeValue("term", AtomService.ATOM_FORMAT));
|
||||||
|
cat.setLabel(catElem.getAttributeValue("label", AtomService.ATOM_FORMAT));
|
||||||
|
cat.setScheme(catElem.getAttributeValue("scheme", AtomService.ATOM_FORMAT));
|
||||||
|
addCategory(cat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
258
src/main/java/com/rometools/propono/atom/common/Collection.java
Normal file
258
src/main/java/com/rometools/propono/atom/common/Collection.java
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jdom2.Element;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models an Atom workspace collection.
|
||||||
|
*/
|
||||||
|
public class Collection {
|
||||||
|
|
||||||
|
public static final String ENTRY_TYPE = "application/atom+xml;type=entry";
|
||||||
|
|
||||||
|
private final List<Categories> categories = new ArrayList<Categories>();
|
||||||
|
|
||||||
|
private Element collectionElement = null;
|
||||||
|
private String baseURI = null;
|
||||||
|
private String title = null;
|
||||||
|
private String titleType = null; // may be TEXT, HTML, XHTML
|
||||||
|
private List<String> accepts = new ArrayList<String>();
|
||||||
|
private String href = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection MUST have title and href.
|
||||||
|
*
|
||||||
|
* @param title Title for collection
|
||||||
|
* @param titleType Content type of title (null for plain text)
|
||||||
|
* @param href Collection URI.
|
||||||
|
*/
|
||||||
|
public Collection(final String title, final String titleType, final String href) {
|
||||||
|
this.title = title;
|
||||||
|
this.titleType = titleType;
|
||||||
|
this.href = href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load self from XML element */
|
||||||
|
public Collection(final Element e) throws ProponoException {
|
||||||
|
collectionElement = e;
|
||||||
|
parseCollectionElement(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load self from XML element and base URI for resolving relative URIs */
|
||||||
|
public Collection(final Element e, final String baseURI) throws ProponoException {
|
||||||
|
collectionElement = e;
|
||||||
|
this.baseURI = baseURI;
|
||||||
|
parseCollectionElement(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of content-type ranges accepted by collection.
|
||||||
|
*/
|
||||||
|
public List<String> getAccepts() {
|
||||||
|
return accepts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccept(final String accept) {
|
||||||
|
accepts.add(accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccepts(final List<String> accepts) {
|
||||||
|
this.accepts = accepts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The URI of the collection */
|
||||||
|
public String getHref() {
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set URI of collection
|
||||||
|
*/
|
||||||
|
public void setHref(final String href) {
|
||||||
|
this.href = href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get resolved URI of the collection, or null if impossible to determine */
|
||||||
|
public String getHrefResolved() {
|
||||||
|
if (Atom10Parser.isAbsoluteURI(href)) {
|
||||||
|
return href;
|
||||||
|
} else if (baseURI != null && collectionElement != null) {
|
||||||
|
final int lastslash = baseURI.lastIndexOf("/");
|
||||||
|
return Atom10Parser.resolveURI(baseURI.substring(0, lastslash), collectionElement, href);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get resolved URI using collection's baseURI, or null if impossible to determine */
|
||||||
|
public String getHrefResolved(final String relativeUri) {
|
||||||
|
if (Atom10Parser.isAbsoluteURI(relativeUri)) {
|
||||||
|
return relativeUri;
|
||||||
|
} else if (baseURI != null && collectionElement != null) {
|
||||||
|
final int lastslash = baseURI.lastIndexOf("/");
|
||||||
|
return Atom10Parser.resolveURI(baseURI.substring(0, lastslash), collectionElement, relativeUri);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Must have human readable title */
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set title of collection.
|
||||||
|
*/
|
||||||
|
public void setTitle(final String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of title ("text", "html" or "xhtml")
|
||||||
|
*/
|
||||||
|
public String getTitleType() {
|
||||||
|
return titleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of title ("text", "html" or "xhtml")
|
||||||
|
*/
|
||||||
|
public void setTitleType(final String titleType) {
|
||||||
|
this.titleType = titleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Workspace can have multiple Categories objects */
|
||||||
|
public void addCategories(final Categories cats) {
|
||||||
|
categories.add(cats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get categories allowed by collection.
|
||||||
|
*
|
||||||
|
* @return Collection of {@link com.rometools.rome.propono.atom.common.Categories} objects.
|
||||||
|
*/
|
||||||
|
public List<Categories> getCategories() {
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if contentType is accepted by collection.
|
||||||
|
*/
|
||||||
|
public boolean accepts(final String ct) {
|
||||||
|
for (final Object element : accepts) {
|
||||||
|
final String accept = (String) element;
|
||||||
|
if (accept != null && accept.trim().equals("*/*")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final String entryType = "application/atom+xml";
|
||||||
|
final boolean entry = entryType.equals(ct);
|
||||||
|
if (entry && null == accept) {
|
||||||
|
return true;
|
||||||
|
} else if (entry && "entry".equals(accept)) {
|
||||||
|
return true;
|
||||||
|
} else if (entry && entryType.equals(accept)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
final String[] rules = accepts.toArray(new String[accepts.size()]);
|
||||||
|
for (final String rule2 : rules) {
|
||||||
|
String rule = rule2.trim();
|
||||||
|
if (rule.equals(ct)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final int slashstar = rule.indexOf("/*");
|
||||||
|
if (slashstar > 0) {
|
||||||
|
rule = rule.substring(0, slashstar + 1);
|
||||||
|
if (ct.startsWith(rule)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize an AtomService.Collection into an XML element
|
||||||
|
*/
|
||||||
|
public Element collectionToElement() {
|
||||||
|
final Collection collection = this;
|
||||||
|
final Element element = new Element("collection", AtomService.ATOM_PROTOCOL);
|
||||||
|
element.setAttribute("href", collection.getHref());
|
||||||
|
|
||||||
|
final Element titleElem = new Element("title", AtomService.ATOM_FORMAT);
|
||||||
|
titleElem.setText(collection.getTitle());
|
||||||
|
if (collection.getTitleType() != null && !collection.getTitleType().equals("TEXT")) {
|
||||||
|
titleElem.setAttribute("type", collection.getTitleType(), AtomService.ATOM_FORMAT);
|
||||||
|
}
|
||||||
|
element.addContent(titleElem);
|
||||||
|
|
||||||
|
// Loop to create <app:categories> elements
|
||||||
|
for (final Object element2 : collection.getCategories()) {
|
||||||
|
final Categories cats = (Categories) element2;
|
||||||
|
element.addContent(cats.categoriesToElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Object element2 : collection.getAccepts()) {
|
||||||
|
final String range = (String) element2;
|
||||||
|
final Element acceptElem = new Element("accept", AtomService.ATOM_PROTOCOL);
|
||||||
|
acceptElem.setText(range);
|
||||||
|
element.addContent(acceptElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deserialize an Atom service collection XML element into an object */
|
||||||
|
public Collection elementToCollection(final Element element) throws ProponoException {
|
||||||
|
return new Collection(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parseCollectionElement(final Element element) throws ProponoException {
|
||||||
|
setHref(element.getAttribute("href").getValue());
|
||||||
|
|
||||||
|
final Element titleElem = element.getChild("title", AtomService.ATOM_FORMAT);
|
||||||
|
if (titleElem != null) {
|
||||||
|
setTitle(titleElem.getText());
|
||||||
|
if (titleElem.getAttribute("type", AtomService.ATOM_FORMAT) != null) {
|
||||||
|
setTitleType(titleElem.getAttribute("type", AtomService.ATOM_FORMAT).getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Element> acceptElems = element.getChildren("accept", AtomService.ATOM_PROTOCOL);
|
||||||
|
if (acceptElems != null && !acceptElems.isEmpty()) {
|
||||||
|
for (final Element acceptElem : acceptElems) {
|
||||||
|
addAccept(acceptElem.getTextTrim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop to parse <app:categories> element to Categories objects
|
||||||
|
final List<Element> catsElems = element.getChildren("categories", AtomService.ATOM_PROTOCOL);
|
||||||
|
for (final Element catsElem : catsElems) {
|
||||||
|
final Categories cats = new Categories(catsElem, baseURI);
|
||||||
|
addCategories(cats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
148
src/main/java/com/rometools/propono/atom/common/Workspace.java
Normal file
148
src/main/java/com/rometools/propono/atom/common/Workspace.java
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jdom2.Element;
|
||||||
|
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models an Atom workspace.
|
||||||
|
*/
|
||||||
|
public class Workspace {
|
||||||
|
|
||||||
|
private String title = null;
|
||||||
|
private String titleType = null; // may be TEXT, HTML, XHTML
|
||||||
|
private final List<Collection> collections = new ArrayList<Collection>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection MUST have title.
|
||||||
|
*
|
||||||
|
* @param title Title for collection
|
||||||
|
* @param titleType Content type of title (null for plain text)
|
||||||
|
*/
|
||||||
|
public Workspace(final String title, final String titleType) {
|
||||||
|
this.title = title;
|
||||||
|
this.titleType = titleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Workspace(final Element elem) throws ProponoException {
|
||||||
|
parseWorkspaceElement(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Iterate over collections in workspace */
|
||||||
|
public List<Collection> getCollections() {
|
||||||
|
return collections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add new collection to workspace */
|
||||||
|
public void addCollection(final Collection col) {
|
||||||
|
collections.add(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DefaultWorkspace must have a human readable title
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set title of workspace.
|
||||||
|
*/
|
||||||
|
public void setTitle(final String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get title type ("text", "html", "xhtml" or MIME content-type)
|
||||||
|
*/
|
||||||
|
public String getTitleType() {
|
||||||
|
return titleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set title type ("text", "html", "xhtml" or MIME content-type)
|
||||||
|
*/
|
||||||
|
public void setTitleType(final String titleType) {
|
||||||
|
this.titleType = titleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find collection by title and/or content-type.
|
||||||
|
*
|
||||||
|
* @param title Title or null to match all titles.
|
||||||
|
* @param contentType Content-type or null to match all content-types.
|
||||||
|
* @return First Collection that matches title and/or content-type.
|
||||||
|
*/
|
||||||
|
public Collection findCollection(final String title, final String contentType) {
|
||||||
|
for (final Object element : collections) {
|
||||||
|
final Collection col = (Collection) element;
|
||||||
|
if (title != null && col.accepts(contentType)) {
|
||||||
|
return col;
|
||||||
|
} else if (col.accepts(contentType)) {
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deserialize a Atom workspace XML element into an object */
|
||||||
|
public static Workspace elementToWorkspace(final Element element) throws ProponoException {
|
||||||
|
return new Workspace(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize an AtomService.DefaultWorkspace object into an XML element
|
||||||
|
*/
|
||||||
|
public Element workspaceToElement() {
|
||||||
|
final Workspace space = this;
|
||||||
|
|
||||||
|
final Element element = new Element("workspace", AtomService.ATOM_PROTOCOL);
|
||||||
|
|
||||||
|
final Element titleElem = new Element("title", AtomService.ATOM_FORMAT);
|
||||||
|
titleElem.setText(space.getTitle());
|
||||||
|
if (space.getTitleType() != null && !space.getTitleType().equals("TEXT")) {
|
||||||
|
titleElem.setAttribute("type", space.getTitleType(), AtomService.ATOM_FORMAT);
|
||||||
|
}
|
||||||
|
element.addContent(titleElem);
|
||||||
|
|
||||||
|
for (final Collection col : space.getCollections()) {
|
||||||
|
element.addContent(col.collectionToElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deserialize a Atom workspace XML element into an object */
|
||||||
|
protected void parseWorkspaceElement(final Element element) throws ProponoException {
|
||||||
|
final Element titleElem = element.getChild("title", AtomService.ATOM_FORMAT);
|
||||||
|
setTitle(titleElem.getText());
|
||||||
|
if (titleElem.getAttribute("type", AtomService.ATOM_FORMAT) != null) {
|
||||||
|
setTitleType(titleElem.getAttribute("type", AtomService.ATOM_FORMAT).getValue());
|
||||||
|
}
|
||||||
|
final List<Element> collections = element.getChildren("collection", AtomService.ATOM_PROTOCOL);
|
||||||
|
for (final Element e : collections) {
|
||||||
|
addCollection(new Collection(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common.rome;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.module.Module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ROME Extension Module to Atom protocol extensions to Atom format.
|
||||||
|
*/
|
||||||
|
public interface AppModule extends Module {
|
||||||
|
|
||||||
|
public static final String URI = "http://www.w3.org/2007/app";
|
||||||
|
|
||||||
|
/** True if entry is a draft */
|
||||||
|
public Boolean getDraft();
|
||||||
|
|
||||||
|
/** Set to true if entry is a draft */
|
||||||
|
public void setDraft(Boolean draft);
|
||||||
|
|
||||||
|
/** Time of last edit */
|
||||||
|
public Date getEdited();
|
||||||
|
|
||||||
|
/** Set time of last edit */
|
||||||
|
public void setEdited(Date edited);
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common.rome;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.jdom2.Element;
|
||||||
|
import org.jdom2.Namespace;
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.module.Module;
|
||||||
|
import com.rometools.rome.io.ModuleGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates JDOM representation for APP Extension Module.
|
||||||
|
*/
|
||||||
|
public class AppModuleGenerator implements ModuleGenerator {
|
||||||
|
|
||||||
|
private static final Namespace APP_NS = Namespace.getNamespace("app", AppModule.URI);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespaceUri() {
|
||||||
|
return AppModule.URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Set<Namespace> NAMESPACES;
|
||||||
|
|
||||||
|
static {
|
||||||
|
final Set<Namespace> nss = new HashSet<Namespace>();
|
||||||
|
nss.add(APP_NS);
|
||||||
|
NAMESPACES = Collections.unmodifiableSet(nss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get namespaces associated with this module */
|
||||||
|
@Override
|
||||||
|
public Set<Namespace> getNamespaces() {
|
||||||
|
return NAMESPACES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate JDOM element for module and add it to parent element */
|
||||||
|
@Override
|
||||||
|
public void generate(final Module module, final Element parent) {
|
||||||
|
final AppModule m = (AppModule) module;
|
||||||
|
|
||||||
|
if (m.getDraft() != null) {
|
||||||
|
final String draft = m.getDraft().booleanValue() ? "yes" : "no";
|
||||||
|
final Element control = new Element("control", APP_NS);
|
||||||
|
control.addContent(generateSimpleElement("draft", draft));
|
||||||
|
parent.addContent(control);
|
||||||
|
}
|
||||||
|
if (m.getEdited() != null) {
|
||||||
|
final Element edited = new Element("edited", APP_NS);
|
||||||
|
// Inclulde millis in date/time
|
||||||
|
final SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
|
||||||
|
dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
|
edited.addContent(dateFormater.format(m.getEdited()));
|
||||||
|
parent.addContent(edited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element generateSimpleElement(final String name, final String value) {
|
||||||
|
final Element element = new Element(name, APP_NS);
|
||||||
|
element.addContent(value);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
* Copyright 2011 The ROME Teams
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common.rome;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.CopyFrom;
|
||||||
|
import com.rometools.rome.feed.module.ModuleImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean representation of APP module.
|
||||||
|
*/
|
||||||
|
public class AppModuleImpl extends ModuleImpl implements AppModule {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private boolean draft = false;
|
||||||
|
private Date edited = null;
|
||||||
|
|
||||||
|
public AppModuleImpl() {
|
||||||
|
super(AppModule.class, AppModule.URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True if entry is draft */
|
||||||
|
@Override
|
||||||
|
public Boolean getDraft() {
|
||||||
|
return draft ? Boolean.TRUE : Boolean.FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set to true if entry is draft */
|
||||||
|
@Override
|
||||||
|
public void setDraft(final Boolean draft) {
|
||||||
|
this.draft = draft.booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Time of last edit */
|
||||||
|
@Override
|
||||||
|
public Date getEdited() {
|
||||||
|
return edited;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set time of last edit */
|
||||||
|
@Override
|
||||||
|
public void setEdited(final Date edited) {
|
||||||
|
this.edited = edited;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get interface class of module */
|
||||||
|
@Override
|
||||||
|
public Class<AppModule> getInterface() {
|
||||||
|
return AppModule.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Copy from other module */
|
||||||
|
@Override
|
||||||
|
public void copyFrom(final CopyFrom obj) {
|
||||||
|
final AppModule m = (AppModule) obj;
|
||||||
|
setDraft(m.getDraft());
|
||||||
|
setEdited(m.getEdited());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.common.rome;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.jdom2.Element;
|
||||||
|
import org.jdom2.Namespace;
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.module.Module;
|
||||||
|
import com.rometools.rome.io.ModuleParser;
|
||||||
|
import com.rometools.rome.io.impl.DateParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses APP module information from a JDOM element and into <code>AppModule</code> form.
|
||||||
|
*/
|
||||||
|
public class AppModuleParser implements ModuleParser {
|
||||||
|
|
||||||
|
/** Get URI of module namespace */
|
||||||
|
@Override
|
||||||
|
public String getNamespaceUri() {
|
||||||
|
return AppModule.URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get namespace of module */
|
||||||
|
public Namespace getContentNamespace() {
|
||||||
|
return Namespace.getNamespace(AppModule.URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse JDOM element into module */
|
||||||
|
@Override
|
||||||
|
public Module parse(final Element elem, final Locale locale) {
|
||||||
|
final AppModule m = new AppModuleImpl();
|
||||||
|
final Element control = elem.getChild("control", getContentNamespace());
|
||||||
|
if (control != null) {
|
||||||
|
final Element draftElem = control.getChild("draft", getContentNamespace());
|
||||||
|
if (draftElem != null) {
|
||||||
|
if ("yes".equals(draftElem.getText())) {
|
||||||
|
m.setDraft(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
if ("no".equals(draftElem.getText())) {
|
||||||
|
m.setDraft(Boolean.FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Element edited = elem.getChild("edited", getContentNamespace());
|
||||||
|
if (edited != null) {
|
||||||
|
try {
|
||||||
|
m.setEdited(DateParser.parseW3CDateTime(edited.getTextTrim(), locale));
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by {@link com.rometools.rome.propono.atom.server.AtomHandler} and extended by
|
||||||
|
* other Propono Atom exception classes.
|
||||||
|
*/
|
||||||
|
public class AtomException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Construct new exception */
|
||||||
|
public AtomException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception with message */
|
||||||
|
public AtomException(final String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Contruct new exception with message and wrapping existing exception */
|
||||||
|
public AtomException(final String msg, final Throwable t) {
|
||||||
|
super(msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception to wrap existing one. */
|
||||||
|
public AtomException(final Throwable t) {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get HTTP status code associated with exception (HTTP 500 server error) */
|
||||||
|
/**
|
||||||
|
* Get HTTP status associated with exception.
|
||||||
|
*/
|
||||||
|
public int getStatus() {
|
||||||
|
return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
|
}
|
150
src/main/java/com/rometools/propono/atom/server/AtomHandler.java
Normal file
150
src/main/java/com/rometools/propono/atom/server/AtomHandler.java
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.AtomService;
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Feed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for handling single Atom protocol requests.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To create your own Atom protocol implementation you must implement this interface and create a
|
||||||
|
* concrete sub-class of {@link com.rometools.rome.propono.atom.server.AtomHandlerFactory} which is
|
||||||
|
* capable of instantiating it.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface AtomHandler {
|
||||||
|
/**
|
||||||
|
* Get username of authenticated user. Return the username of the authenticated user
|
||||||
|
*/
|
||||||
|
public String getAuthenticatedUsername();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return {@link com.rometools.rome.propono.atom.common.AtomService} object that contains the
|
||||||
|
* {@link com.rometools.rome.propono.atom.common.Workspace} objects available to the currently
|
||||||
|
* authenticated user and within those the
|
||||||
|
* {@link com.rometools.rome.propono.atom.common.Collection} avalaible.
|
||||||
|
*/
|
||||||
|
public AtomService getAtomService(AtomRequest req) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get categories, a list of Categories objects
|
||||||
|
*/
|
||||||
|
public Categories getCategories(AtomRequest req) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return collection or portion of collection specified by request.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public Feed getCollection(AtomRequest req) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store new entry in collection specified by request and return representation of entry as it
|
||||||
|
* is stored on server.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
* @return Location URL of new entry
|
||||||
|
*/
|
||||||
|
public Entry postEntry(AtomRequest req, Entry entry) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get entry specified by request.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public Entry getEntry(AtomRequest req) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get media resource specified by request.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public AtomMediaResource getMediaResource(AtomRequest req) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update entry specified by request and return new entry as represented on the server.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public void putEntry(AtomRequest req, Entry entry) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete entry specified by request.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public void deleteEntry(AtomRequest req) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store media data in collection specified by request, create an Atom media-link entry to store
|
||||||
|
* metadata for the new media file and return that entry to the caller.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
* @param entry New entry initialzied with only title and content type
|
||||||
|
* @return Location URL of new media entry
|
||||||
|
*/
|
||||||
|
public Entry postMedia(AtomRequest req, Entry entry) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the media file part of a media-link entry.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public void putMedia(AtomRequest req) throws AtomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified request represents URI of a Service Document.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public boolean isAtomServiceURI(AtomRequest req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified request represents URI of a Categories Document.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public boolean isCategoriesURI(AtomRequest req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified request represents URI of a collection.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public boolean isCollectionURI(AtomRequest req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified request represents URI of an Atom entry.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public boolean isEntryURI(AtomRequest req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified patrequesthinfo represents media-edit URI.
|
||||||
|
*
|
||||||
|
* @param req Details of HTTP request
|
||||||
|
*/
|
||||||
|
public boolean isMediaEditURI(AtomRequest req);
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a factory that enables the {@link com.rometools.rome.propono.atom.server.AtomServlet} to
|
||||||
|
* obtain an {@link com.rometools.rome.propono.atom.server.AtomHandler} that handles an Atom
|
||||||
|
* request.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To create your own Atom protocol implementation you must sub-class this class with your own
|
||||||
|
* factory that is capable of creating instances of your
|
||||||
|
* {@link com.rometools.rome.propono.atom.server.AtomHandler} impementation.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public abstract class AtomHandlerFactory {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AtomHandlerFactory.class);
|
||||||
|
|
||||||
|
private static final String DEFAULT_PROPERTY_NAME = "com.rometools.propono.atom.server.AtomHandlerFactory";
|
||||||
|
private static final String FALLBACK_IMPL_NAME = "com.rometools.propono.atom.server.impl.FileBasedAtomHandlerFactory";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <p>Protected constructor to prevent instantiation. Use {@link #newInstance()}.</p>
|
||||||
|
*/
|
||||||
|
protected AtomHandlerFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a new instance of a <code>AtomHandlerFactory</code>. This static method creates a new
|
||||||
|
* factory instance. This method uses the following ordered lookup procedure to determine the
|
||||||
|
* <code>AtomHandlerFactory</code> implementation class to load:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* Use the <code>com.rometools.rome.propono.atom.server.AtomHandlerFactory</code> system
|
||||||
|
* property.</li>
|
||||||
|
* <li>
|
||||||
|
* Use the properties file "/propono.properties" in the classpath. This configuration file is in
|
||||||
|
* standard <code>java.util.Properties</code> format and contains the fully qualified name of
|
||||||
|
* the implementation class with the key being the system property defined above.
|
||||||
|
*
|
||||||
|
* The propono.properties file is read only once by Propono and it's values are then cached for
|
||||||
|
* future use. If the file does not exist when the first attempt is made to read from it, no
|
||||||
|
* further attempts are made to check for its existence. It is not possible to change the value
|
||||||
|
* of any property in propono.properties after it has been read for the first time.</li>
|
||||||
|
* <li>
|
||||||
|
* If not available, to determine the classname. The Services API will look for a classname in
|
||||||
|
* the file: <code>META-INF/services/com.rometools.rome.AtomHandlerFactory</code> in jars
|
||||||
|
* available to the runtime.</li>
|
||||||
|
* <li>
|
||||||
|
* Platform default <code>AtomHandlerFactory</code> instance.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Once an application has obtained a reference to a <code>AtomHandlerFactory</code> it can use
|
||||||
|
* the factory to configure and obtain parser instances.
|
||||||
|
*
|
||||||
|
* @return New instance of a <code>AtomHandlerFactory</code>
|
||||||
|
*
|
||||||
|
* @throws FactoryConfigurationError if the implementation is not available or cannot be
|
||||||
|
* instantiated.
|
||||||
|
*/
|
||||||
|
public static AtomHandlerFactory newInstance() {
|
||||||
|
try {
|
||||||
|
return (AtomHandlerFactory) FactoryFinder.find(DEFAULT_PROPERTY_NAME, FALLBACK_IMPL_NAME);
|
||||||
|
} catch (final ConfigurationError e) {
|
||||||
|
LOG.error("An error occured while finding factory", e);
|
||||||
|
throw new FactoryConfigurationError(e.getException(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of a {@link com.rometools.rome.propono.atom.server.AtomHandler} using
|
||||||
|
* the currently configured parameters.
|
||||||
|
*
|
||||||
|
* @return A new instance of a AtomHandler.
|
||||||
|
*
|
||||||
|
* @throws AtomConfigurationException if a AtomHandler cannot be created which satisfies the
|
||||||
|
* configuration requested.
|
||||||
|
*/
|
||||||
|
public abstract AtomHandler newAtomHandler(HttpServletRequest req, HttpServletResponse res);
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.activation.FileTypeMap;
|
||||||
|
import javax.activation.MimetypesFileTypeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a media link entry.
|
||||||
|
*/
|
||||||
|
public class AtomMediaResource {
|
||||||
|
private String contentType = null;
|
||||||
|
private long contentLength = 0;
|
||||||
|
private InputStream inputStream = null;
|
||||||
|
private Date lastModified = null;
|
||||||
|
private static FileTypeMap map = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// TODO: figure out why PNG is missing from Java MIME types
|
||||||
|
map = FileTypeMap.getDefaultFileTypeMap();
|
||||||
|
if (map instanceof MimetypesFileTypeMap) {
|
||||||
|
try {
|
||||||
|
((MimetypesFileTypeMap) map).addMimeTypes("image/png png PNG");
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AtomMediaResource(final File resource) throws FileNotFoundException {
|
||||||
|
contentType = map.getContentType(resource.getName());
|
||||||
|
contentLength = resource.length();
|
||||||
|
lastModified = new Date(resource.lastModified());
|
||||||
|
inputStream = new FileInputStream(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AtomMediaResource(final String name, final long length, final Date lastModified, final InputStream is) throws FileNotFoundException {
|
||||||
|
contentType = map.getContentType(name);
|
||||||
|
contentLength = length;
|
||||||
|
this.lastModified = lastModified;
|
||||||
|
inputStream = is;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(final String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getContentLength() {
|
||||||
|
return contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentLength(final long contentLength) {
|
||||||
|
this.contentLength = contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputStream(final InputStream inputStream) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastModified() {
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastModified(final Date lastModified) {
|
||||||
|
this.lastModified = lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception to be thrown by <code>AtomHandler</code> implementations in the case that a user is not
|
||||||
|
* authorized to access a resource.
|
||||||
|
*/
|
||||||
|
public class AtomNotAuthorizedException extends AtomException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Construct new exception */
|
||||||
|
public AtomNotAuthorizedException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception with message */
|
||||||
|
public AtomNotAuthorizedException(final String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception with message and root cause */
|
||||||
|
public AtomNotAuthorizedException(final String msg, final Throwable t) {
|
||||||
|
super(msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception to wrap root cause */
|
||||||
|
public AtomNotAuthorizedException(final Throwable t) {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get HTTP status code of exception (HTTP 403 unauthorized) */
|
||||||
|
@Override
|
||||||
|
public int getStatus() {
|
||||||
|
return HttpServletResponse.SC_UNAUTHORIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by AtomHandler in that case a resource is not found.
|
||||||
|
*/
|
||||||
|
public class AtomNotFoundException extends AtomException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Construct new exception */
|
||||||
|
public AtomNotFoundException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception with message */
|
||||||
|
public AtomNotFoundException(final String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception with message and root cause */
|
||||||
|
public AtomNotFoundException(final String msg, final Throwable t) {
|
||||||
|
super(msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct new exception with root cause */
|
||||||
|
public AtomNotFoundException(final Throwable t) {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get HTTP status code associated with exception (404 not found) */
|
||||||
|
@Override
|
||||||
|
public int getStatus() {
|
||||||
|
return HttpServletResponse.SC_NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
137
src/main/java/com/rometools/propono/atom/server/AtomRequest.java
Normal file
137
src/main/java/com/rometools/propono/atom/server/AtomRequest.java
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents HTTP request to be processed by AtomHandler.
|
||||||
|
*/
|
||||||
|
public interface AtomRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns any extra path information associated with the URL the client sent when it made this
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
public String getPathInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the query string that is contained in the request URL after the path.
|
||||||
|
*/
|
||||||
|
public String getQueryString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the login of the user making this request, if the user has been authenticated, or
|
||||||
|
* null if the user has not been authenticated.
|
||||||
|
*/
|
||||||
|
public String getRemoteUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a boolean indicating whether the authenticated user is included in the specified
|
||||||
|
* logical "role".
|
||||||
|
*/
|
||||||
|
public boolean isUserInRole(String arg0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a java.security.Principal object containing the name of the current authenticated
|
||||||
|
* user.
|
||||||
|
*/
|
||||||
|
public Principal getUserPrincipal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the part of this request's URL from the protocol name up to the query string in the
|
||||||
|
* first line of the HTTP request.
|
||||||
|
*/
|
||||||
|
public String getRequestURI();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconstructs the URL the client used to make the request.
|
||||||
|
*/
|
||||||
|
public StringBuffer getRequestURL();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length, in bytes, of the request body and made available by the input stream, or
|
||||||
|
* -1 if the length is not known.
|
||||||
|
*/
|
||||||
|
public int getContentLength();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the MIME type of the body of the request, or null if the type is not known.
|
||||||
|
*/
|
||||||
|
public String getContentType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of a request parameter as a String, or null if the parameter does not
|
||||||
|
* exist.
|
||||||
|
*/
|
||||||
|
public String getParameter(String arg0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Enumeration of String objects containing the names of the parameters contained in
|
||||||
|
* this request.
|
||||||
|
*/
|
||||||
|
public Enumeration<String> getParameterNames();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of String objects containing all of the values the given request parameter
|
||||||
|
* has, or null if the parameter does not exist.
|
||||||
|
*/
|
||||||
|
public String[] getParameterValues(String arg0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a java.util.Map of the parameters of this request.
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getParameterMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the body of the request as binary data using a ServletInputStream.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the specified request header as a long value that represents a Date
|
||||||
|
* object.
|
||||||
|
*/
|
||||||
|
public long getDateHeader(String arg0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the specified request header as a String.
|
||||||
|
*/
|
||||||
|
public String getHeader(String arg0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the values of the specified request header as an Enumeration of String objects.
|
||||||
|
*/
|
||||||
|
public Enumeration<String> getHeaders(String arg0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an enumeration of all the header names this request contains.
|
||||||
|
*/
|
||||||
|
public Enumeration<String> getHeaderNames();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the specified request header as an int.
|
||||||
|
*/
|
||||||
|
public int getIntHeader(String arg0);
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default request implementation.
|
||||||
|
*/
|
||||||
|
public class AtomRequestImpl implements AtomRequest {
|
||||||
|
|
||||||
|
private HttpServletRequest wrapped = null;
|
||||||
|
|
||||||
|
public AtomRequestImpl(final HttpServletRequest wrapped) {
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPathInfo() {
|
||||||
|
return wrapped.getPathInfo() != null ? wrapped.getPathInfo() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryString() {
|
||||||
|
return wrapped.getQueryString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteUser() {
|
||||||
|
return wrapped.getRemoteUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUserInRole(final String arg0) {
|
||||||
|
return wrapped.isUserInRole(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getUserPrincipal() {
|
||||||
|
return wrapped.getUserPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRequestURI() {
|
||||||
|
return wrapped.getRequestURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringBuffer getRequestURL() {
|
||||||
|
return wrapped.getRequestURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getContentLength() {
|
||||||
|
return wrapped.getContentLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return wrapped.getContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParameter(final String arg0) {
|
||||||
|
return wrapped.getParameter(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Enumeration<String> getParameterNames() {
|
||||||
|
return wrapped.getParameterNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getParameterValues(final String arg0) {
|
||||||
|
return wrapped.getParameterValues(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Map<String, Object> getParameterMap() {
|
||||||
|
return wrapped.getParameterMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return wrapped.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDateHeader(final String arg0) {
|
||||||
|
return wrapped.getDateHeader(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(final String arg0) {
|
||||||
|
return wrapped.getHeader(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Enumeration<String> getHeaders(final String arg0) {
|
||||||
|
return wrapped.getHeaders(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Enumeration<String> getHeaderNames() {
|
||||||
|
return wrapped.getHeaderNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntHeader(final String arg0) {
|
||||||
|
return wrapped.getIntHeader(arg0);
|
||||||
|
}
|
||||||
|
}
|
371
src/main/java/com/rometools/propono/atom/server/AtomServlet.java
Normal file
371
src/main/java/com/rometools/propono/atom/server/AtomServlet.java
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. 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. For additional information regarding
|
||||||
|
* copyright in this work, please see the NOTICE file in the top level
|
||||||
|
* directory of this distribution.
|
||||||
|
*/
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.servlet.ServletConfig;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.jdom2.Document;
|
||||||
|
import org.jdom2.output.Format;
|
||||||
|
import org.jdom2.output.XMLOutputter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.AtomService;
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.utils.Utilities;
|
||||||
|
import com.rometools.rome.feed.atom.Content;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Feed;
|
||||||
|
import com.rometools.rome.feed.atom.Link;
|
||||||
|
import com.rometools.rome.io.WireFeedOutput;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Generator;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atom Servlet implements Atom protocol by calling an
|
||||||
|
* {@link com.rometools.rome.propono.atom.server.AtomHandler} implementation. This servlet takes
|
||||||
|
* care of parsing incoming XML into ROME Atom {@link com.rometools.rome.feed.atom.Entry} objects,
|
||||||
|
* passing those to the handler and serializing to the response the entries and feeds returned by
|
||||||
|
* the handler.
|
||||||
|
*/
|
||||||
|
public class AtomServlet extends HttpServlet {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get feed type support by Servlet, "atom_1.0"
|
||||||
|
*/
|
||||||
|
public static final String FEED_TYPE = "atom_1.0";
|
||||||
|
private static String contextDirPath = null;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AtomServlet.class);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Atom10Parser.setResolveURIs(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Create an Atom request handler. TODO: make AtomRequestHandler implementation configurable.
|
||||||
|
*/
|
||||||
|
private AtomHandler createAtomRequestHandler(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
|
||||||
|
final AtomHandlerFactory ahf = AtomHandlerFactory.newInstance();
|
||||||
|
return ahf.newAtomHandler(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Handles an Atom GET by calling handler and writing results to response.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doGet(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException {
|
||||||
|
LOG.debug("Entering");
|
||||||
|
final AtomHandler handler = createAtomRequestHandler(req, res);
|
||||||
|
final String userName = handler.getAuthenticatedUsername();
|
||||||
|
if (userName != null) {
|
||||||
|
final AtomRequest areq = new AtomRequestImpl(req);
|
||||||
|
try {
|
||||||
|
if (handler.isAtomServiceURI(areq)) {
|
||||||
|
// return an Atom Service document
|
||||||
|
final AtomService service = handler.getAtomService(areq);
|
||||||
|
final Document doc = service.serviceToDocument();
|
||||||
|
res.setContentType("application/atomsvc+xml; charset=utf-8");
|
||||||
|
final Writer writer = res.getWriter();
|
||||||
|
final XMLOutputter outputter = new XMLOutputter();
|
||||||
|
outputter.setFormat(Format.getPrettyFormat());
|
||||||
|
outputter.output(doc, writer);
|
||||||
|
writer.close();
|
||||||
|
res.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
} else if (handler.isCategoriesURI(areq)) {
|
||||||
|
final Categories cats = handler.getCategories(areq);
|
||||||
|
res.setContentType("application/xml");
|
||||||
|
final Writer writer = res.getWriter();
|
||||||
|
final Document catsDoc = new Document();
|
||||||
|
catsDoc.setRootElement(cats.categoriesToElement());
|
||||||
|
final XMLOutputter outputter = new XMLOutputter();
|
||||||
|
outputter.output(catsDoc, writer);
|
||||||
|
writer.close();
|
||||||
|
res.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
} else if (handler.isCollectionURI(areq)) {
|
||||||
|
// return a collection
|
||||||
|
final Feed col = handler.getCollection(areq);
|
||||||
|
col.setFeedType(FEED_TYPE);
|
||||||
|
final WireFeedOutput wireFeedOutput = new WireFeedOutput();
|
||||||
|
final Document feedDoc = wireFeedOutput.outputJDom(col);
|
||||||
|
res.setContentType("application/atom+xml; charset=utf-8");
|
||||||
|
final Writer writer = res.getWriter();
|
||||||
|
final XMLOutputter outputter = new XMLOutputter();
|
||||||
|
outputter.setFormat(Format.getPrettyFormat());
|
||||||
|
outputter.output(feedDoc, writer);
|
||||||
|
writer.close();
|
||||||
|
res.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
} else if (handler.isEntryURI(areq)) {
|
||||||
|
// return an entry
|
||||||
|
final Entry entry = handler.getEntry(areq);
|
||||||
|
if (entry != null) {
|
||||||
|
res.setContentType("application/atom+xml; type=entry; charset=utf-8");
|
||||||
|
final Writer writer = res.getWriter();
|
||||||
|
Atom10Generator.serializeEntry(entry, writer);
|
||||||
|
writer.close();
|
||||||
|
} else {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
} else if (handler.isMediaEditURI(areq)) {
|
||||||
|
final AtomMediaResource entry = handler.getMediaResource(areq);
|
||||||
|
res.setContentType(entry.getContentType());
|
||||||
|
res.setContentLength((int) entry.getContentLength());
|
||||||
|
Utilities.copyInputToOutput(entry.getInputStream(), res.getOutputStream());
|
||||||
|
res.getOutputStream().flush();
|
||||||
|
res.getOutputStream().close();
|
||||||
|
} else {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
} catch (final AtomException ae) {
|
||||||
|
res.sendError(ae.getStatus(), ae.getMessage());
|
||||||
|
LOG.debug("An error occured while processing GET", ae);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||||
|
LOG.debug("An error occured while processing GET", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
|
||||||
|
res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
LOG.debug("Exiting");
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Handles an Atom POST by calling handler to identify URI, reading/parsing data, calling
|
||||||
|
* handler and writing results to response.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doPost(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException {
|
||||||
|
LOG.debug("Entering");
|
||||||
|
final AtomHandler handler = createAtomRequestHandler(req, res);
|
||||||
|
final String userName = handler.getAuthenticatedUsername();
|
||||||
|
if (userName != null) {
|
||||||
|
final AtomRequest areq = new AtomRequestImpl(req);
|
||||||
|
try {
|
||||||
|
if (handler.isCollectionURI(areq)) {
|
||||||
|
|
||||||
|
if (req.getContentType().startsWith("application/atom+xml")) {
|
||||||
|
|
||||||
|
// parse incoming entry
|
||||||
|
final Entry entry = Atom10Parser.parseEntry(new BufferedReader(new InputStreamReader(req.getInputStream(), "UTF-8")), null, Locale.US);
|
||||||
|
|
||||||
|
// call handler to post it
|
||||||
|
final Entry newEntry = handler.postEntry(areq, entry);
|
||||||
|
|
||||||
|
// set Location and Content-Location headers
|
||||||
|
for (final Object element : newEntry.getOtherLinks()) {
|
||||||
|
final Link link = (Link) element;
|
||||||
|
if ("edit".equals(link.getRel())) {
|
||||||
|
res.addHeader("Location", link.getHrefResolved());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final Object element : newEntry.getAlternateLinks()) {
|
||||||
|
final Link link = (Link) element;
|
||||||
|
if ("alternate".equals(link.getRel())) {
|
||||||
|
res.addHeader("Content-Location", link.getHrefResolved());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write entry back out to response
|
||||||
|
res.setStatus(HttpServletResponse.SC_CREATED);
|
||||||
|
res.setContentType("application/atom+xml; type=entry; charset=utf-8");
|
||||||
|
|
||||||
|
final Writer writer = res.getWriter();
|
||||||
|
Atom10Generator.serializeEntry(newEntry, writer);
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
} else if (req.getContentType() != null) {
|
||||||
|
|
||||||
|
// get incoming title and slug from HTTP header
|
||||||
|
final String title = areq.getHeader("Title");
|
||||||
|
|
||||||
|
// create new entry for resource, set title and type
|
||||||
|
final Entry resource = new Entry();
|
||||||
|
resource.setTitle(title);
|
||||||
|
final Content content = new Content();
|
||||||
|
content.setType(areq.getContentType());
|
||||||
|
resource.setContents(Collections.singletonList(content));
|
||||||
|
|
||||||
|
// hand input stream off to hander to post file
|
||||||
|
final Entry newEntry = handler.postMedia(areq, resource);
|
||||||
|
|
||||||
|
// set Location and Content-Location headers
|
||||||
|
for (final Object element : newEntry.getOtherLinks()) {
|
||||||
|
final Link link = (Link) element;
|
||||||
|
if ("edit".equals(link.getRel())) {
|
||||||
|
res.addHeader("Location", link.getHrefResolved());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final Object element : newEntry.getAlternateLinks()) {
|
||||||
|
final Link link = (Link) element;
|
||||||
|
if ("alternate".equals(link.getRel())) {
|
||||||
|
res.addHeader("Content-Location", link.getHrefResolved());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setStatus(HttpServletResponse.SC_CREATED);
|
||||||
|
res.setContentType("application/atom+xml; type=entry; charset=utf-8");
|
||||||
|
|
||||||
|
final Writer writer = res.getWriter();
|
||||||
|
Atom10Generator.serializeEntry(newEntry, writer);
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
res.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "No content-type specified in request");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
res.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid collection specified in request");
|
||||||
|
}
|
||||||
|
} catch (final AtomException ae) {
|
||||||
|
res.sendError(ae.getStatus(), ae.getMessage());
|
||||||
|
LOG.debug("An error occured while processing POST", ae);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||||
|
LOG.debug("An error occured while processing POST", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
|
||||||
|
res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
LOG.debug("Exiting");
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Handles an Atom PUT by calling handler to identify URI, reading/parsing data, calling handler
|
||||||
|
* and writing results to response.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doPut(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException {
|
||||||
|
LOG.debug("Entering");
|
||||||
|
final AtomHandler handler = createAtomRequestHandler(req, res);
|
||||||
|
final String userName = handler.getAuthenticatedUsername();
|
||||||
|
if (userName != null) {
|
||||||
|
final AtomRequest areq = new AtomRequestImpl(req);
|
||||||
|
try {
|
||||||
|
if (handler.isEntryURI(areq)) {
|
||||||
|
|
||||||
|
// parse incoming entry
|
||||||
|
final Entry unsavedEntry = Atom10Parser.parseEntry(new BufferedReader(new InputStreamReader(req.getInputStream(), "UTF-8")), null,
|
||||||
|
Locale.US);
|
||||||
|
|
||||||
|
// call handler to put entry
|
||||||
|
handler.putEntry(areq, unsavedEntry);
|
||||||
|
|
||||||
|
res.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
|
||||||
|
} else if (handler.isMediaEditURI(areq)) {
|
||||||
|
|
||||||
|
// hand input stream to handler
|
||||||
|
handler.putMedia(areq);
|
||||||
|
|
||||||
|
res.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
} catch (final AtomException ae) {
|
||||||
|
res.sendError(ae.getStatus(), ae.getMessage());
|
||||||
|
LOG.debug("An error occured while processing PUT", ae);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||||
|
LOG.debug("An error occured while processing PUT", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
|
||||||
|
// Wanted to use sendError() here but Tomcat sends 403 forbidden
|
||||||
|
// when I do that, so sticking with setStatus() for time being.
|
||||||
|
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
LOG.debug("Exiting");
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Handle Atom DELETE by calling appropriate handler.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doDelete(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException {
|
||||||
|
LOG.debug("Entering");
|
||||||
|
final AtomHandler handler = createAtomRequestHandler(req, res);
|
||||||
|
final String userName = handler.getAuthenticatedUsername();
|
||||||
|
if (userName != null) {
|
||||||
|
final AtomRequest areq = new AtomRequestImpl(req);
|
||||||
|
try {
|
||||||
|
if (handler.isEntryURI(areq)) {
|
||||||
|
handler.deleteEntry(areq);
|
||||||
|
res.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
} else {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
} catch (final AtomException ae) {
|
||||||
|
res.sendError(ae.getStatus(), ae.getMessage());
|
||||||
|
LOG.debug("An error occured while processing DELETE", ae);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||||
|
LOG.debug("An error occured while processing DELETE", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
|
||||||
|
// Wanted to use sendError() here but Tomcat sends 403 forbidden
|
||||||
|
// when I do that, so sticking with setStatus() for time being.
|
||||||
|
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
LOG.debug("Exiting");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize servlet.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void init(final ServletConfig config) throws ServletException {
|
||||||
|
super.init(config);
|
||||||
|
contextDirPath = getServletContext().getRealPath("/");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get absolute path to Servlet context directory.
|
||||||
|
*/
|
||||||
|
public static String getContextDirPath() {
|
||||||
|
return contextDirPath;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
class ConfigurationError extends Error {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Exception exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance with the specified detail string and exception.
|
||||||
|
*/
|
||||||
|
ConfigurationError(final String msg, final Exception x) {
|
||||||
|
super(msg);
|
||||||
|
exception = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a problem with configuration with the
|
||||||
|
* {@link com.rometools.rome.propono.atom.server.AtomHandlerFactory} exists. This error will
|
||||||
|
* typically be thrown when the class of a parser factory specified in the system properties cannot
|
||||||
|
* be found or instantiated.
|
||||||
|
*/
|
||||||
|
public class FactoryConfigurationError extends Error {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>Exception</code> that represents the error.
|
||||||
|
*/
|
||||||
|
private final Exception exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new <code>FactoryConfigurationError</code> with no detail mesage.
|
||||||
|
*/
|
||||||
|
public FactoryConfigurationError() {
|
||||||
|
super();
|
||||||
|
exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new <code>FactoryConfigurationError</code> with the <code>String </code> specified
|
||||||
|
* as an error message.
|
||||||
|
*
|
||||||
|
* @param msg The error message for the exception.
|
||||||
|
*/
|
||||||
|
public FactoryConfigurationError(final String msg) {
|
||||||
|
super(msg);
|
||||||
|
exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new <code>FactoryConfigurationError</code> with a given <code>Exception</code> base
|
||||||
|
* cause of the error.
|
||||||
|
*
|
||||||
|
* @param e The exception to be encapsulated in a FactoryConfigurationError.
|
||||||
|
*/
|
||||||
|
public FactoryConfigurationError(final Exception e) {
|
||||||
|
super(e.toString());
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new <code>FactoryConfigurationError</code> with the given <code>Exception</code>
|
||||||
|
* base cause and detail message.
|
||||||
|
*
|
||||||
|
* @param e The exception to be encapsulated in a FactoryConfigurationError
|
||||||
|
* @param msg The detail message.
|
||||||
|
*/
|
||||||
|
public FactoryConfigurationError(final Exception e, final String msg) {
|
||||||
|
super(msg);
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the message (if any) for this error . If there is no message for the exception and
|
||||||
|
* there is an encapsulated exception then the message of that exception, if it exists will be
|
||||||
|
* returned. Else the name of the encapsulated exception will be returned.
|
||||||
|
*
|
||||||
|
* @return The error message.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
final String message = super.getMessage();
|
||||||
|
|
||||||
|
if (message == null && exception != null) {
|
||||||
|
return exception.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the actual exception (if any) that caused this exception to be raised.
|
||||||
|
*
|
||||||
|
* @return The encapsulated exception, or null if there is none.
|
||||||
|
*/
|
||||||
|
public Exception getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,248 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find {@link com.rometools.rome.propono.atom.server.AtomHandlerFactory} based on properties
|
||||||
|
* files.
|
||||||
|
*/
|
||||||
|
class FactoryFinder {
|
||||||
|
|
||||||
|
private static boolean debug = false;
|
||||||
|
private static Properties cacheProps = new Properties();
|
||||||
|
private static SecuritySupport ss = new SecuritySupport();
|
||||||
|
private static boolean firstTime = true;
|
||||||
|
|
||||||
|
private static void dPrint(final String msg) {
|
||||||
|
if (debug) {
|
||||||
|
System.err.println("Propono: " + msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of a class using the specified ClassLoader and optionally fall back to the
|
||||||
|
* current ClassLoader if not found.
|
||||||
|
*
|
||||||
|
* @param className Name of the concrete class corresponding to the service provider
|
||||||
|
*
|
||||||
|
* @param cl ClassLoader to use to load the class, null means to use the bootstrap ClassLoader
|
||||||
|
*
|
||||||
|
* @param doFallback true if the current ClassLoader should be tried as a fallback if the class
|
||||||
|
* is not found using cl
|
||||||
|
*/
|
||||||
|
private static Object newInstance(final String className, ClassLoader cl, final boolean doFallback) throws ConfigurationError {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class<?> providerClass;
|
||||||
|
if (cl == null) {
|
||||||
|
// If classloader is null Use the bootstrap ClassLoader.
|
||||||
|
// Thus Class.forName(String) will use the current
|
||||||
|
// ClassLoader which will be the bootstrap ClassLoader.
|
||||||
|
providerClass = Class.forName(className);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
providerClass = cl.loadClass(className);
|
||||||
|
} catch (final ClassNotFoundException x) {
|
||||||
|
if (doFallback) {
|
||||||
|
// Fall back to current classloader
|
||||||
|
cl = FactoryFinder.class.getClassLoader();
|
||||||
|
providerClass = cl.loadClass(className);
|
||||||
|
} else {
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object instance = providerClass.newInstance();
|
||||||
|
dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl);
|
||||||
|
return instance;
|
||||||
|
} catch (final ClassNotFoundException x) {
|
||||||
|
throw new ConfigurationError("Provider " + className + " not found", x);
|
||||||
|
} catch (final Exception x) {
|
||||||
|
throw new ConfigurationError("Provider " + className + " could not be instantiated: " + x, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the implementation Class object in the specified order. Main entry point.
|
||||||
|
*
|
||||||
|
* @return Class object of factory, never null
|
||||||
|
*
|
||||||
|
* @param factoryId Name of the factory to find, same as a property name
|
||||||
|
* @param fallbackClassName Implementation class name, if nothing else is found. Use null to
|
||||||
|
* mean no fallback.
|
||||||
|
*
|
||||||
|
* Package private so this code can be shared.
|
||||||
|
*/
|
||||||
|
static Object find(final String factoryId, final String fallbackClassName) throws ConfigurationError {
|
||||||
|
|
||||||
|
// Figure out which ClassLoader to use for loading the provider
|
||||||
|
// class. If there is a Context ClassLoader then use it.
|
||||||
|
|
||||||
|
ClassLoader classLoader = ss.getContextClassLoader();
|
||||||
|
|
||||||
|
if (classLoader == null) {
|
||||||
|
// if we have no Context ClassLoader
|
||||||
|
// so use the current ClassLoader
|
||||||
|
classLoader = FactoryFinder.class.getClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
dPrint("find factoryId =" + factoryId);
|
||||||
|
|
||||||
|
// Use the system property first
|
||||||
|
try {
|
||||||
|
final String systemProp = ss.getSystemProperty(factoryId);
|
||||||
|
if (systemProp != null) {
|
||||||
|
dPrint("found system property, value=" + systemProp);
|
||||||
|
return newInstance(systemProp, classLoader, true);
|
||||||
|
}
|
||||||
|
} catch (final SecurityException se) {
|
||||||
|
// if first option fails due to any reason we should try next option in the
|
||||||
|
// look up algorithm.
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to read from /propono.properties
|
||||||
|
try {
|
||||||
|
final String configFile = "/propono.properties";
|
||||||
|
String factoryClassName = null;
|
||||||
|
if (firstTime) {
|
||||||
|
synchronized (cacheProps) {
|
||||||
|
if (firstTime) {
|
||||||
|
try {
|
||||||
|
final InputStream is = FactoryFinder.class.getResourceAsStream(configFile);
|
||||||
|
firstTime = false;
|
||||||
|
if (is != null) {
|
||||||
|
dPrint("Read properties file: " + configFile);
|
||||||
|
cacheProps.load(is);
|
||||||
|
}
|
||||||
|
} catch (final Exception intentionallyIgnored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
factoryClassName = cacheProps.getProperty(factoryId);
|
||||||
|
|
||||||
|
if (factoryClassName != null) {
|
||||||
|
dPrint("found in $java.home/propono.properties, value=" + factoryClassName);
|
||||||
|
return newInstance(factoryClassName, classLoader, true);
|
||||||
|
}
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
if (debug) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try Jar Service Provider Mechanism
|
||||||
|
final Object provider = findJarServiceProvider(factoryId);
|
||||||
|
if (provider != null) {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
if (fallbackClassName == null) {
|
||||||
|
throw new ConfigurationError("Provider for " + factoryId + " cannot be found", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
dPrint("loaded from fallback value: " + fallbackClassName);
|
||||||
|
return newInstance(fallbackClassName, classLoader, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to find provider using Jar Service Provider Mechanism
|
||||||
|
* @return instance of provider class if found or null
|
||||||
|
*/
|
||||||
|
private static Object findJarServiceProvider(final String factoryId) throws ConfigurationError {
|
||||||
|
|
||||||
|
final String serviceId = "META-INF/services/" + factoryId;
|
||||||
|
InputStream is = null;
|
||||||
|
|
||||||
|
// First try the Context ClassLoader
|
||||||
|
ClassLoader cl = ss.getContextClassLoader();
|
||||||
|
if (cl != null) {
|
||||||
|
is = ss.getResourceAsStream(cl, serviceId);
|
||||||
|
|
||||||
|
// If no provider found then try the current ClassLoader
|
||||||
|
if (is == null) {
|
||||||
|
cl = FactoryFinder.class.getClassLoader();
|
||||||
|
is = ss.getResourceAsStream(cl, serviceId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No Context ClassLoader, try the current
|
||||||
|
// ClassLoader
|
||||||
|
cl = FactoryFinder.class.getClassLoader();
|
||||||
|
is = ss.getResourceAsStream(cl, serviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is == null) {
|
||||||
|
// No provider found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
|
||||||
|
|
||||||
|
// Read the service provider name in UTF-8 as specified in
|
||||||
|
// the jar spec. Unfortunately this fails in Microsoft
|
||||||
|
// VJ++, which does not implement the UTF-8
|
||||||
|
// encoding. Theoretically, we should simply let it fail in
|
||||||
|
// that case, since the JVM is obviously broken if it
|
||||||
|
// doesn't support such a basic standard. But since there
|
||||||
|
// are still some users attempting to use VJ++ for
|
||||||
|
// development, we have dropped in a fallback which makes a
|
||||||
|
// second attempt using the platform's default encoding. In
|
||||||
|
// VJ++ this is apparently ASCII, which is a subset of
|
||||||
|
// UTF-8... and since the strings we'll be reading here are
|
||||||
|
// also primarily limited to the 7-bit ASCII range (at
|
||||||
|
// least, in English versions), this should work well
|
||||||
|
// enough to keep us on the air until we're ready to
|
||||||
|
// officially decommit from VJ++. [Edited comment from
|
||||||
|
// jkesselm]
|
||||||
|
BufferedReader rd;
|
||||||
|
try {
|
||||||
|
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
||||||
|
} catch (final java.io.UnsupportedEncodingException e) {
|
||||||
|
rd = new BufferedReader(new InputStreamReader(is));
|
||||||
|
}
|
||||||
|
|
||||||
|
String factoryClassName = null;
|
||||||
|
try {
|
||||||
|
// XXX Does not handle all possible input as specified by the
|
||||||
|
// Jar Service Provider specification
|
||||||
|
factoryClassName = rd.readLine();
|
||||||
|
rd.close();
|
||||||
|
} catch (final IOException x) {
|
||||||
|
// No provider found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (factoryClassName != null && !"".equals(factoryClassName)) {
|
||||||
|
dPrint("found in resource, value=" + factoryClassName);
|
||||||
|
|
||||||
|
// Note: here we do not want to fall back to the current
|
||||||
|
// ClassLoader because we want to avoid the case where the
|
||||||
|
// resource file was found using one ClassLoader and the
|
||||||
|
// provider class was instantiated using a different one.
|
||||||
|
return newInstance(factoryClassName, cl, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No provider found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
import java.security.PrivilegedActionException;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is duplicated for each subpackage, it is package private and therefore is not exposed
|
||||||
|
* as part of the public API.
|
||||||
|
*/
|
||||||
|
class SecuritySupport {
|
||||||
|
|
||||||
|
ClassLoader getContextClassLoader() {
|
||||||
|
final PrivilegedAction<ClassLoader> action = new PrivilegedAction<ClassLoader>() {
|
||||||
|
@Override
|
||||||
|
public ClassLoader run() {
|
||||||
|
ClassLoader cl = null;
|
||||||
|
try {
|
||||||
|
cl = Thread.currentThread().getContextClassLoader();
|
||||||
|
} catch (final SecurityException ex) {
|
||||||
|
}
|
||||||
|
return cl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return AccessController.doPrivileged(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSystemProperty(final String propName) {
|
||||||
|
final PrivilegedAction<String> action = new PrivilegedAction<String>() {
|
||||||
|
@Override
|
||||||
|
public String run() {
|
||||||
|
return System.getProperty(propName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return AccessController.doPrivileged(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream getFileInputStream(final File file) throws FileNotFoundException {
|
||||||
|
try {
|
||||||
|
final PrivilegedExceptionAction<FileInputStream> action = new PrivilegedExceptionAction<FileInputStream>() {
|
||||||
|
@Override
|
||||||
|
public FileInputStream run() throws FileNotFoundException {
|
||||||
|
return new FileInputStream(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return AccessController.doPrivileged(action);
|
||||||
|
} catch (final PrivilegedActionException e) {
|
||||||
|
throw (FileNotFoundException) e.getException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream getResourceAsStream(final ClassLoader cl, final String name) {
|
||||||
|
final PrivilegedAction<InputStream> action = new PrivilegedAction<InputStream>() {
|
||||||
|
@Override
|
||||||
|
public InputStream run() {
|
||||||
|
InputStream ris;
|
||||||
|
if (cl == null) {
|
||||||
|
ris = ClassLoader.getSystemResourceAsStream(name);
|
||||||
|
} else {
|
||||||
|
ris = cl.getResourceAsStream(name);
|
||||||
|
}
|
||||||
|
return ris;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return AccessController.doPrivileged(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean doesFileExist(final File f) {
|
||||||
|
final PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean run() {
|
||||||
|
return f.exists();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return AccessController.doPrivileged(action).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,469 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.AtomService;
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.atom.server.AtomException;
|
||||||
|
import com.rometools.propono.atom.server.AtomHandler;
|
||||||
|
import com.rometools.propono.atom.server.AtomMediaResource;
|
||||||
|
import com.rometools.propono.atom.server.AtomRequest;
|
||||||
|
import com.rometools.propono.atom.server.AtomServlet;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Feed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File-based {@link com.rometools.rome.propono.atom.server.AtomHandler} implementation that stores
|
||||||
|
* entries and media-entries to disk. Implemented using
|
||||||
|
* {@link com.rometools.rome.propono.atom.server.impl.FileBasedAtomService}.
|
||||||
|
*/
|
||||||
|
public class FileBasedAtomHandler implements AtomHandler {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(FileBasedAtomHandler.class);
|
||||||
|
|
||||||
|
private String userName = null;
|
||||||
|
private String atomProtocolURL = null;
|
||||||
|
private String contextURI = null;
|
||||||
|
private FileBasedAtomService service = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct handler to handle one request.
|
||||||
|
*
|
||||||
|
* @param req Request to be handled.
|
||||||
|
*/
|
||||||
|
public FileBasedAtomHandler(final HttpServletRequest req) {
|
||||||
|
this(req, AtomServlet.getContextDirPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contruct handler for one request, using specified file storage directory.
|
||||||
|
*
|
||||||
|
* @param req Request to be handled.
|
||||||
|
* @param uploaddir File storage upload dir.
|
||||||
|
*/
|
||||||
|
public FileBasedAtomHandler(final HttpServletRequest req, final String uploaddir) {
|
||||||
|
LOG.debug("ctor");
|
||||||
|
|
||||||
|
userName = authenticateBASIC(req);
|
||||||
|
|
||||||
|
atomProtocolURL = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath() + req.getServletPath();
|
||||||
|
|
||||||
|
contextURI = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath();
|
||||||
|
|
||||||
|
try {
|
||||||
|
service = new FileBasedAtomService(userName, uploaddir, contextURI, req.getContextPath(), req.getServletPath());
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw new RuntimeException("ERROR creating FileBasedAtomService", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method used for validating user. Developers can overwrite this method and use credentials
|
||||||
|
* stored in Database or LDAP to confirm if the user is allowed to access this service.
|
||||||
|
*
|
||||||
|
* @param login user submitted login id
|
||||||
|
* @param password user submitted password
|
||||||
|
*/
|
||||||
|
public boolean validateUser(final String login, final String password) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get username of authenticated user
|
||||||
|
*
|
||||||
|
* @return User name.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getAuthenticatedUsername() {
|
||||||
|
// For now return userName as the login id entered for authorization
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get base URI of Atom protocol implementation.
|
||||||
|
*
|
||||||
|
* @return Base URI of Atom protocol implemenation.
|
||||||
|
*/
|
||||||
|
public String getAtomProtocolURL() {
|
||||||
|
if (atomProtocolURL == null) {
|
||||||
|
return "app";
|
||||||
|
} else {
|
||||||
|
return atomProtocolURL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return introspection document
|
||||||
|
*
|
||||||
|
* @throws com.rometools.rome.propono.atom.server.AtomException Unexpected exception.
|
||||||
|
* @return AtomService object with workspaces and collections.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AtomService getAtomService(final AtomRequest areq) throws AtomException {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns null because we use in-line categories.
|
||||||
|
*
|
||||||
|
* @throws com.rometools.rome.propono.atom.server.AtomException Unexpected exception.
|
||||||
|
* @return Categories object
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Categories getCategories(final AtomRequest areq) throws AtomException {
|
||||||
|
LOG.debug("getCollection");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
return col.getCategories(true).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get collection specified by pathinfo.
|
||||||
|
*
|
||||||
|
* @param areq Details of HTTP request
|
||||||
|
* @return ROME feed representing collection.
|
||||||
|
* @throws com.rometools.rome.propono.atom.server.AtomException Invalid collection or other
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Feed getCollection(final AtomRequest areq) throws AtomException {
|
||||||
|
LOG.debug("getCollection");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
return col.getFeedDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new entry specified by pathInfo and posted entry. We save the submitted Atom entry
|
||||||
|
* verbatim, but we do set the id and reset the update time.
|
||||||
|
*
|
||||||
|
* @param entry Entry to be added to collection.
|
||||||
|
* @param areq Details of HTTP request
|
||||||
|
* @throws com.rometools.rome.propono.atom.server.AtomException On invalid collection or other
|
||||||
|
* error.
|
||||||
|
* @return Entry as represented on server.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Entry postEntry(final AtomRequest areq, final Entry entry) throws AtomException {
|
||||||
|
LOG.debug("postEntry");
|
||||||
|
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
try {
|
||||||
|
return col.addEntry(entry);
|
||||||
|
|
||||||
|
} catch (final Exception fe) {
|
||||||
|
fe.printStackTrace();
|
||||||
|
throw new AtomException(fe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get entry specified by pathInfo.
|
||||||
|
*
|
||||||
|
* @param areq Details of HTTP request
|
||||||
|
* @throws com.rometools.rome.propono.atom.server.AtomException On invalid pathinfo or other
|
||||||
|
* error.
|
||||||
|
* @return ROME Entry object.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Entry getEntry(final AtomRequest areq) throws AtomException {
|
||||||
|
LOG.debug("getEntry");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final String fileName = pathInfo[2];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
try {
|
||||||
|
return col.getEntry(fileName);
|
||||||
|
|
||||||
|
} catch (final Exception re) {
|
||||||
|
if (re instanceof AtomException) {
|
||||||
|
throw (AtomException) re;
|
||||||
|
}
|
||||||
|
throw new AtomException("ERROR: getting entry", re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update entry specified by pathInfo and posted entry.
|
||||||
|
*
|
||||||
|
* @param entry
|
||||||
|
* @param areq Details of HTTP request
|
||||||
|
* @throws com.rometools.rome.propono.atom.server.AtomException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void putEntry(final AtomRequest areq, final Entry entry) throws AtomException {
|
||||||
|
LOG.debug("putEntry");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final String fileName = pathInfo[2];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
try {
|
||||||
|
col.updateEntry(entry, fileName);
|
||||||
|
|
||||||
|
} catch (final Exception fe) {
|
||||||
|
throw new AtomException(fe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete entry specified by pathInfo.
|
||||||
|
*
|
||||||
|
* @param areq Details of HTTP request
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void deleteEntry(final AtomRequest areq) throws AtomException {
|
||||||
|
LOG.debug("deleteEntry");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final String fileName = pathInfo[2];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
try {
|
||||||
|
col.deleteEntry(fileName);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
final String msg = "ERROR in atom.deleteResource";
|
||||||
|
LOG.error(msg, e);
|
||||||
|
throw new AtomException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store media data in collection specified by pathInfo, create an Atom media-link entry to
|
||||||
|
* store metadata for the new media file and return that entry to the caller.
|
||||||
|
*
|
||||||
|
* @param areq Details of HTTP request
|
||||||
|
* @param entry New entry initialzied with only title and content type
|
||||||
|
* @return Location URL of new media entry
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Entry postMedia(final AtomRequest areq, final Entry entry) throws AtomException {
|
||||||
|
|
||||||
|
// get incoming slug from HTTP header
|
||||||
|
final String slug = areq.getHeader("Slug");
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("postMedia - title: " + entry.getTitle() + " slug:" + slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final File tempFile = null;
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
try {
|
||||||
|
col.addMediaEntry(entry, slug, areq.getInputStream());
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
final String msg = "ERROR reading posted file";
|
||||||
|
LOG.error(msg, e);
|
||||||
|
throw new AtomException(msg, e);
|
||||||
|
} finally {
|
||||||
|
if (tempFile != null) {
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final Exception re) {
|
||||||
|
throw new AtomException("ERROR: posting media");
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the media file part of a media-link entry.
|
||||||
|
*
|
||||||
|
* @param areq Details of HTTP request Assuming pathInfo of form /user-name/resource/name
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void putMedia(final AtomRequest areq) throws AtomException {
|
||||||
|
|
||||||
|
LOG.debug("putMedia");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final String fileName = pathInfo[3];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
try {
|
||||||
|
col.updateMediaEntry(fileName, areq.getContentType(), areq.getInputStream());
|
||||||
|
|
||||||
|
} catch (final Exception re) {
|
||||||
|
throw new AtomException("ERROR: posting media");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AtomMediaResource getMediaResource(final AtomRequest areq) throws AtomException {
|
||||||
|
LOG.debug("putMedia");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final String fileName = pathInfo[3];
|
||||||
|
final FileBasedCollection col = service.findCollectionByHandle(handle, collection);
|
||||||
|
try {
|
||||||
|
return col.getMediaResource(fileName);
|
||||||
|
|
||||||
|
} catch (final Exception re) {
|
||||||
|
throw new AtomException("ERROR: posting media");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified pathinfo represents URI of service doc.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAtomServiceURI(final AtomRequest areq) {
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
if (pathInfo.length == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified pathinfo represents URI of category doc.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isCategoriesURI(final AtomRequest areq) {
|
||||||
|
LOG.debug("isCategoriesDocumentURI");
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
if (pathInfo.length == 3 && "categories".equals(pathInfo[2])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified pathinfo represents URI of a collection.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isCollectionURI(final AtomRequest areq) {
|
||||||
|
LOG.debug("isCollectionURI");
|
||||||
|
// workspace/collection-plural
|
||||||
|
// if length is 2 and points to a valid collection then YES
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
if (pathInfo.length == 2) {
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
if (service.findCollectionByHandle(handle, collection) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified pathinfo represents URI of an Atom entry.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isEntryURI(final AtomRequest areq) {
|
||||||
|
LOG.debug("isEntryURI");
|
||||||
|
// workspace/collection-singular/fsid
|
||||||
|
// if length is 3 and points to a valid collection then YES
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
if (pathInfo.length == 3) {
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
if (service.findCollectionByHandle(handle, collection) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if specified pathinfo represents media-edit URI.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isMediaEditURI(final AtomRequest areq) {
|
||||||
|
LOG.debug("isMediaEditURI");
|
||||||
|
// workspace/collection-singular/fsid/media/fsid
|
||||||
|
// if length is 4, points to a valid collection and fsid is mentioned
|
||||||
|
// twice then YES
|
||||||
|
final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
|
||||||
|
if (pathInfo.length == 4) {
|
||||||
|
final String handle = pathInfo[0];
|
||||||
|
final String collection = pathInfo[1];
|
||||||
|
final String media = pathInfo[2];
|
||||||
|
// final String fsid = pathInfo[3];
|
||||||
|
if (service.findCollectionByHandle(handle, collection) != null && media.equals("media")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BASIC authentication.
|
||||||
|
*/
|
||||||
|
public String authenticateBASIC(final HttpServletRequest request) {
|
||||||
|
LOG.debug("authenticateBASIC");
|
||||||
|
boolean valid = false;
|
||||||
|
String userID = null;
|
||||||
|
String password = null;
|
||||||
|
try {
|
||||||
|
final String authHeader = request.getHeader("Authorization");
|
||||||
|
if (authHeader != null) {
|
||||||
|
final StringTokenizer st = new StringTokenizer(authHeader);
|
||||||
|
if (st.hasMoreTokens()) {
|
||||||
|
final String basic = st.nextToken();
|
||||||
|
if (basic.equalsIgnoreCase("Basic")) {
|
||||||
|
final String credentials = st.nextToken();
|
||||||
|
final String userPass = new String(Base64.decodeBase64(credentials.getBytes()));
|
||||||
|
final int p = userPass.indexOf(":");
|
||||||
|
if (p != -1) {
|
||||||
|
userID = userPass.substring(0, p);
|
||||||
|
password = userPass.substring(p + 1);
|
||||||
|
|
||||||
|
// Validate the User.
|
||||||
|
valid = validateUser(userID, password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
LOG.debug("An error occured while processing Basic authentication", e);
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
// For now assume userID as userName
|
||||||
|
return userID;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server.impl;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.server.AtomHandler;
|
||||||
|
import com.rometools.propono.atom.server.AtomHandlerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends {@link com.rometools.rome.propono.atom.server.AtomHandlerFactory} to create and return
|
||||||
|
* {@link com.rometools.rome.propono.atom.server.impl.FileBasedAtomHandler}.
|
||||||
|
*/
|
||||||
|
public class FileBasedAtomHandlerFactory extends AtomHandlerFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new AtomHandler.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AtomHandler newAtomHandler(final HttpServletRequest req, final HttpServletResponse res) {
|
||||||
|
return new FileBasedAtomHandler(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server.impl;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.AtomService;
|
||||||
|
import com.rometools.propono.utils.Utilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File based Atom service. Supports one workspace per user. Collections in workspace are defined in
|
||||||
|
* /propono.properties, for example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* # Define list of collections to be offered
|
||||||
|
* propono.atomserver.filebased.collections=entries,gifimages
|
||||||
|
*
|
||||||
|
* # Defines 'entries' collection, accepts entries
|
||||||
|
* propono.atomserver.filebased.collection.entries.title=Entries
|
||||||
|
* propono.atomserver.filebased.collection.entries.singular=entry
|
||||||
|
* propono.atomserver.filebased.collection.entries.plural=entries
|
||||||
|
* propono.atomserver.filebased.collection.entries.accept=application/atom+xml;type=entry
|
||||||
|
* propono.atomserver.filebased.collection.entries.categories=general,category1,category2
|
||||||
|
*
|
||||||
|
* # Defines 'gifimages' collection, accepts only GIF files
|
||||||
|
* propono.atomserver.filebased.collection.gifimages.title=GIF Images
|
||||||
|
* propono.atomserver.filebased.collection.gifimages.singular=gif
|
||||||
|
* propono.atomserver.filebased.collection.gifimages.plural=gifs
|
||||||
|
* propono.atomserver.filebased.collection.gifimages.accept=image/gif
|
||||||
|
* propono.atomserver.filebased.collection.gifimages.categories=general,category1,category2
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* If no such properties are found, then service will fall back to two collections: 'entries' for
|
||||||
|
* Atom entries and 'resources' for any content-type.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>URI structure used for accessing collections and entries</b>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Collection feed (URI allows GET to get collection, POST to add to it)<br/>
|
||||||
|
* <code>[servlet-context-uri]/app/[workspace-handle]/[collection-plural] </code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Collection entry (URI allows GET, PUT and DELETE)<br/>
|
||||||
|
* <code>[servlet-context-uri]/app/[workspace-handle]/[collection-singular]/[entryid] </code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Collection entry media (URI allows GET, PUT and DELETE)<br/>
|
||||||
|
* <code>[servlet-context-uri]/app/[workspace-handle]/[collection-singular]/media/[entryid]</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Categories URI if not using inline categories (URI allows GET)<br/>
|
||||||
|
* <code>[servlet-context-uri]/app/[workspace-handle]/[collection-plural]/categories</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>Directory structure used to store collections and entries</b>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Collection feed (kept constantly up to date)<br/>
|
||||||
|
* <code>[servlet-context-dir]/[workspace-handle]/[collection-plural]/feed.xml</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Collection entry (individual entries also stored as entry.xml files)<br/>
|
||||||
|
* <code>[servlet-context-dir]/[workspace-handle]/[collection-plural]/id/entry.xml</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Collection entry media (media file stored under entry directory)<br/>
|
||||||
|
* <code>[servlet-context-dir]/[workspace-handle]/[collection-plural]/id/media/id</code>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class FileBasedAtomService extends AtomService {
|
||||||
|
|
||||||
|
private final Map<String, FileBasedWorkspace> workspaceMap = new TreeMap<String, FileBasedWorkspace>();
|
||||||
|
private final Map<String, FileBasedCollection> collectionMap = new TreeMap<String, FileBasedCollection>();
|
||||||
|
private static Properties cacheProps = new Properties();
|
||||||
|
private boolean firstTime = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of FileBasedAtomService.
|
||||||
|
*/
|
||||||
|
public FileBasedAtomService(final String userName, final String baseDir, final String contextURI, final String contextPath, final String servletPath)
|
||||||
|
throws Exception {
|
||||||
|
final String workspaceHandle = userName;
|
||||||
|
|
||||||
|
// One workspace per user
|
||||||
|
final FileBasedWorkspace workspace = new FileBasedWorkspace(workspaceHandle, baseDir);
|
||||||
|
workspaceMap.put(userName, workspace);
|
||||||
|
|
||||||
|
if (firstTime) {
|
||||||
|
synchronized (cacheProps) {
|
||||||
|
final InputStream is = getClass().getResourceAsStream("/propono.properties");
|
||||||
|
if (is != null) {
|
||||||
|
cacheProps.load(is);
|
||||||
|
}
|
||||||
|
firstTime = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// can't find propono.properties, so use system props instead
|
||||||
|
if (cacheProps == null) {
|
||||||
|
cacheProps = System.getProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String relativeURIsString = cacheProps.getProperty("propono.atomserver.filebased.relativeURIs");
|
||||||
|
final boolean relativeURIs = "true".equals(relativeURIsString);
|
||||||
|
|
||||||
|
final String inlineCategoriesString = cacheProps.getProperty("propono.atomserver.filebased.inlineCategories");
|
||||||
|
final boolean inlineCategories = "true".equals(inlineCategoriesString);
|
||||||
|
|
||||||
|
final String colnames = cacheProps.getProperty("propono.atomserver.filebased.collections");
|
||||||
|
if (colnames != null) {
|
||||||
|
|
||||||
|
// collections specified in propono.properties, use those
|
||||||
|
|
||||||
|
final String[] colarray = Utilities.stringToStringArray(colnames, ",");
|
||||||
|
for (final String element : colarray) {
|
||||||
|
final String prefix = "propono.atomserver.filebased.collection." + element + ".";
|
||||||
|
final String collectionTitle = cacheProps.getProperty(prefix + "title");
|
||||||
|
final String collectionSingular = cacheProps.getProperty(prefix + "singular");
|
||||||
|
final String collectionPlural = cacheProps.getProperty(prefix + "plural");
|
||||||
|
final String collectionAccept = cacheProps.getProperty(prefix + "accept");
|
||||||
|
|
||||||
|
final String catNamesString = cacheProps.getProperty(prefix + "categories");
|
||||||
|
final String[] catNames = Utilities.stringToStringArray(catNamesString, ",");
|
||||||
|
|
||||||
|
final FileBasedCollection entries = new FileBasedCollection(collectionTitle, workspaceHandle, collectionPlural, collectionSingular,
|
||||||
|
collectionAccept, inlineCategories, catNames, relativeURIs, contextURI, contextPath, servletPath, baseDir);
|
||||||
|
workspace.addCollection(entries);
|
||||||
|
// want to be able to look up collection by singular and plural names
|
||||||
|
collectionMap.put(workspaceHandle + "|" + collectionSingular, entries);
|
||||||
|
collectionMap.put(workspaceHandle + "|" + collectionPlural, entries);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Fallback to two collections. One collection for accepting entries
|
||||||
|
// and other collection for other ( resources/uploaded images etc.)
|
||||||
|
|
||||||
|
final String[] catNames = new String[] { "general", "category1", "category2" };
|
||||||
|
|
||||||
|
final FileBasedCollection entries = new FileBasedCollection("Entries", workspaceHandle, "entries", "entry", "application/atom+xml;type=entry",
|
||||||
|
inlineCategories, catNames, relativeURIs, contextURI, contextPath, servletPath, baseDir);
|
||||||
|
workspace.addCollection(entries);
|
||||||
|
// want to be able to look up collection by singular and plural names
|
||||||
|
collectionMap.put(workspaceHandle + "|entry", entries);
|
||||||
|
collectionMap.put(workspaceHandle + "|entries", entries);
|
||||||
|
|
||||||
|
final FileBasedCollection resources = new FileBasedCollection("Resources", workspaceHandle, "resources", "resource", "*/*", inlineCategories,
|
||||||
|
catNames, relativeURIs, contextURI, contextPath, servletPath, baseDir);
|
||||||
|
// want to be able to look up collection by singular and plural names
|
||||||
|
workspace.addCollection(resources);
|
||||||
|
collectionMap.put(workspaceHandle + "|resource", resources);
|
||||||
|
collectionMap.put(workspaceHandle + "|resources", resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorkspaces().add(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find workspace by handle, returns null of not found.
|
||||||
|
*/
|
||||||
|
FileBasedWorkspace findWorkspaceByHandle(final String handle) {
|
||||||
|
return workspaceMap.get(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileBasedCollection findCollectionByHandle(final String handle, final String collection) {
|
||||||
|
return collectionMap.get(handle + "|" + collection);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,815 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server.impl;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import javax.activation.FileTypeMap;
|
||||||
|
import javax.activation.MimetypesFileTypeMap;
|
||||||
|
|
||||||
|
import org.jdom2.Document;
|
||||||
|
import org.jdom2.output.XMLOutputter;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.atom.common.Collection;
|
||||||
|
import com.rometools.propono.atom.common.rome.AppModule;
|
||||||
|
import com.rometools.propono.atom.common.rome.AppModuleImpl;
|
||||||
|
import com.rometools.propono.atom.server.AtomException;
|
||||||
|
import com.rometools.propono.atom.server.AtomMediaResource;
|
||||||
|
import com.rometools.propono.atom.server.AtomNotFoundException;
|
||||||
|
import com.rometools.propono.utils.Utilities;
|
||||||
|
import com.rometools.rome.feed.WireFeed;
|
||||||
|
import com.rometools.rome.feed.atom.Category;
|
||||||
|
import com.rometools.rome.feed.atom.Content;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Feed;
|
||||||
|
import com.rometools.rome.feed.atom.Link;
|
||||||
|
import com.rometools.rome.feed.module.Module;
|
||||||
|
import com.rometools.rome.io.FeedException;
|
||||||
|
import com.rometools.rome.io.WireFeedInput;
|
||||||
|
import com.rometools.rome.io.WireFeedOutput;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Generator;
|
||||||
|
import com.rometools.rome.io.impl.Atom10Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File based Atom collection implementation. This is the heart of the file-based Atom service
|
||||||
|
* implementation. It provides methods for adding, getting updating and deleting Atom entries and
|
||||||
|
* media entries.
|
||||||
|
*/
|
||||||
|
public class FileBasedCollection extends Collection {
|
||||||
|
|
||||||
|
private String handle = null;
|
||||||
|
private String singular = null;
|
||||||
|
private String collection = null;
|
||||||
|
|
||||||
|
private boolean inlineCats = false;
|
||||||
|
private String[] catNames = null;
|
||||||
|
|
||||||
|
private boolean relativeURIs = false;
|
||||||
|
private String contextURI = null;
|
||||||
|
private String servletPath = null;
|
||||||
|
private String baseDir = null;
|
||||||
|
|
||||||
|
private static final String FEED_TYPE = "atom_1.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct by providing title (plain text, no HTML), a workspace handle, a plural collection
|
||||||
|
* name (e.g. entries), a singular collection name (e.g. entry), the base directory for file
|
||||||
|
* storage, the content-type range accepted by the collection and the root Atom protocol URI for
|
||||||
|
* the service.
|
||||||
|
*
|
||||||
|
* @param title Title of collection (plain text, no HTML)
|
||||||
|
* @param handle Workspace handle
|
||||||
|
* @param collection Collection handle, plural
|
||||||
|
* @param singular Collection handle, singular
|
||||||
|
* @param accept Content type range accepted by collection
|
||||||
|
* @param inlineCats True for inline categories
|
||||||
|
* @param catNames Category names for this workspace
|
||||||
|
* @param baseDir Base directory for file storage
|
||||||
|
* @param relativeURIs True for relative URIs
|
||||||
|
* @param contextURI Absolute URI of context that hosts APP service
|
||||||
|
* @param contextPath Context path of APP service (e.g. "/sample-atomserver")
|
||||||
|
* @param servletPath Servlet path of APP service (e.g. "/app")
|
||||||
|
*/
|
||||||
|
public FileBasedCollection(final String title, final String handle, final String collection, final String singular, final String accept,
|
||||||
|
final boolean inlineCats, final String[] catNames, final boolean relativeURIs, final String contextURI, final String contextPath,
|
||||||
|
final String servletPath, final String baseDir) {
|
||||||
|
super(title, "text", relativeURIs ? servletPath.substring(1) + "/" + handle + "/" + collection : contextURI + servletPath + "/" + handle + "/"
|
||||||
|
+ collection);
|
||||||
|
|
||||||
|
this.handle = handle;
|
||||||
|
this.collection = collection;
|
||||||
|
this.singular = singular;
|
||||||
|
this.inlineCats = inlineCats;
|
||||||
|
this.catNames = catNames;
|
||||||
|
this.baseDir = baseDir;
|
||||||
|
this.relativeURIs = relativeURIs;
|
||||||
|
this.contextURI = contextURI;
|
||||||
|
this.servletPath = servletPath;
|
||||||
|
|
||||||
|
addAccept(accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get feed document representing collection.
|
||||||
|
*
|
||||||
|
* @throws com.rometools.rome.propono.atom.server.AtomException On error retrieving feed file.
|
||||||
|
* @return Atom Feed representing collection.
|
||||||
|
*/
|
||||||
|
public Feed getFeedDocument() throws AtomException {
|
||||||
|
InputStream in = null;
|
||||||
|
synchronized (FileStore.getFileStore()) {
|
||||||
|
in = FileStore.getFileStore().getFileInputStream(getFeedPath());
|
||||||
|
if (in == null) {
|
||||||
|
in = createDefaultFeedDocument(contextURI + servletPath + "/" + handle + "/" + collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final WireFeedInput input = new WireFeedInput();
|
||||||
|
final WireFeed wireFeed = input.build(new InputStreamReader(in, "UTF-8"));
|
||||||
|
return (Feed) wireFeed;
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw new AtomException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of one Categories object containing categories allowed by collection.
|
||||||
|
*
|
||||||
|
* @param inline True if Categories object should contain collection of in-line Categories
|
||||||
|
* objects or false if it should set the Href for out-of-line categories.
|
||||||
|
*/
|
||||||
|
public List<Categories> getCategories(final boolean inline) {
|
||||||
|
final Categories cats = new Categories();
|
||||||
|
cats.setFixed(true);
|
||||||
|
cats.setScheme(contextURI + "/" + handle + "/" + singular);
|
||||||
|
if (inline) {
|
||||||
|
for (final String catName : catNames) {
|
||||||
|
final Category cat = new Category();
|
||||||
|
cat.setTerm(catName);
|
||||||
|
cats.addCategory(cat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cats.setHref(getCategoriesURI());
|
||||||
|
}
|
||||||
|
return Collections.singletonList(cats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of one Categories object containing categories allowed by collection, returns
|
||||||
|
* in-line categories if collection set to use in-line categories.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Categories> getCategories() {
|
||||||
|
return getCategories(inlineCats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add entry to collection.
|
||||||
|
*
|
||||||
|
* @param entry Entry to be added to collection. Entry will be saved to disk in a directory
|
||||||
|
* under the collection's directory and the path will follow the pattern
|
||||||
|
* [collection-plural]/[entryid]/entry.xml. The entry will be added to the
|
||||||
|
* collection's feed in [collection-plural]/feed.xml.
|
||||||
|
* @throws java.lang.Exception On error.
|
||||||
|
* @return Entry as it exists on the server.
|
||||||
|
*/
|
||||||
|
public Entry addEntry(final Entry entry) throws Exception {
|
||||||
|
synchronized (FileStore.getFileStore()) {
|
||||||
|
final Feed f = getFeedDocument();
|
||||||
|
|
||||||
|
final String fsid = FileStore.getFileStore().getNextId();
|
||||||
|
updateTimestamps(entry);
|
||||||
|
|
||||||
|
// Save entry to file
|
||||||
|
final String entryPath = getEntryPath(fsid);
|
||||||
|
|
||||||
|
final OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
|
||||||
|
updateEntryAppLinks(entry, fsid, true);
|
||||||
|
Atom10Generator.serializeEntry(entry, new OutputStreamWriter(os, "UTF-8"));
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
|
||||||
|
// Update feed file
|
||||||
|
updateEntryAppLinks(entry, fsid, false);
|
||||||
|
updateFeedDocumentWithNewEntry(f, entry);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add media entry to collection. Accepts a media file to be added to collection. The file will
|
||||||
|
* be saved to disk in a directory under the collection's directory and the path will follow the
|
||||||
|
* pattern <code>[collection-plural]/[entryid]/media/[entryid]</code>. An Atom entry will be
|
||||||
|
* created to store metadata for the entry and it will exist at the path
|
||||||
|
* <code>[collection-plural]/[entryid]/entry.xml</code>. The entry will be added to the
|
||||||
|
* collection's feed in [collection-plural]/feed.xml.
|
||||||
|
*
|
||||||
|
* @param entry Entry object
|
||||||
|
* @param slug String to be used in file-name
|
||||||
|
* @param is Source of media data
|
||||||
|
* @throws java.lang.Exception On Error
|
||||||
|
* @return Location URI of entry
|
||||||
|
*/
|
||||||
|
public String addMediaEntry(final Entry entry, final String slug, final InputStream is) throws Exception {
|
||||||
|
synchronized (FileStore.getFileStore()) {
|
||||||
|
|
||||||
|
// Save media file temp file
|
||||||
|
final Content content = entry.getContents().get(0);
|
||||||
|
if (entry.getTitle() == null) {
|
||||||
|
entry.setTitle(slug);
|
||||||
|
}
|
||||||
|
final String fileName = createFileName(slug != null ? slug : entry.getTitle(), content.getType());
|
||||||
|
final File tempFile = File.createTempFile(fileName, "tmp");
|
||||||
|
final FileOutputStream fos = new FileOutputStream(tempFile);
|
||||||
|
Utilities.copyInputToOutput(is, fos);
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
// Save media file
|
||||||
|
final FileInputStream fis = new FileInputStream(tempFile);
|
||||||
|
saveMediaFile(fileName, content.getType(), tempFile.length(), fis);
|
||||||
|
fis.close();
|
||||||
|
final File resourceFile = new File(getEntryMediaPath(fileName));
|
||||||
|
|
||||||
|
// Create media-link entry
|
||||||
|
updateTimestamps(entry);
|
||||||
|
|
||||||
|
// Save media-link entry
|
||||||
|
final String entryPath = getEntryPath(fileName);
|
||||||
|
final OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
|
||||||
|
updateMediaEntryAppLinks(entry, resourceFile.getName(), true);
|
||||||
|
Atom10Generator.serializeEntry(entry, new OutputStreamWriter(os, "UTF-8"));
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
|
||||||
|
// Update feed with new entry
|
||||||
|
final Feed f = getFeedDocument();
|
||||||
|
updateMediaEntryAppLinks(entry, resourceFile.getName(), false);
|
||||||
|
updateFeedDocumentWithNewEntry(f, entry);
|
||||||
|
|
||||||
|
return getEntryEditURI(fileName, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an entry from the collection.
|
||||||
|
*
|
||||||
|
* @param fsid Internal ID of entry to be returned
|
||||||
|
* @throws java.lang.Exception On error
|
||||||
|
* @return Entry specified by fileName/ID
|
||||||
|
*/
|
||||||
|
public Entry getEntry(String fsid) throws Exception {
|
||||||
|
if (fsid.endsWith(".media-link")) {
|
||||||
|
fsid = fsid.substring(0, fsid.length() - ".media-link".length());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String entryPath = getEntryPath(fsid);
|
||||||
|
|
||||||
|
checkExistence(entryPath);
|
||||||
|
final InputStream in = FileStore.getFileStore().getFileInputStream(entryPath);
|
||||||
|
|
||||||
|
final Entry entry;
|
||||||
|
final File resource = new File(fsid);
|
||||||
|
if (resource.exists()) {
|
||||||
|
entry = loadAtomResourceEntry(in, resource);
|
||||||
|
updateMediaEntryAppLinks(entry, fsid, true);
|
||||||
|
} else {
|
||||||
|
entry = loadAtomEntry(in);
|
||||||
|
updateEntryAppLinks(entry, fsid, true);
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get media resource wrapping a file.
|
||||||
|
*/
|
||||||
|
public AtomMediaResource getMediaResource(final String fileName) throws Exception {
|
||||||
|
final String filePath = getEntryMediaPath(fileName);
|
||||||
|
final File resource = new File(filePath);
|
||||||
|
return new AtomMediaResource(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an entry in the collection.
|
||||||
|
*
|
||||||
|
* @param entry Updated entry to be stored
|
||||||
|
* @param fsid Internal ID of entry
|
||||||
|
* @throws java.lang.Exception On error
|
||||||
|
*/
|
||||||
|
public void updateEntry(final Entry entry, String fsid) throws Exception {
|
||||||
|
synchronized (FileStore.getFileStore()) {
|
||||||
|
|
||||||
|
final Feed f = getFeedDocument();
|
||||||
|
|
||||||
|
if (fsid.endsWith(".media-link")) {
|
||||||
|
fsid = fsid.substring(0, fsid.length() - ".media-link".length());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimestamps(entry);
|
||||||
|
|
||||||
|
updateEntryAppLinks(entry, fsid, false);
|
||||||
|
updateFeedDocumentWithExistingEntry(f, entry);
|
||||||
|
|
||||||
|
final String entryPath = getEntryPath(fsid);
|
||||||
|
final OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
|
||||||
|
updateEntryAppLinks(entry, fsid, true);
|
||||||
|
Atom10Generator.serializeEntry(entry, new OutputStreamWriter(os, "UTF-8"));
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update media associated with a media-link entry.
|
||||||
|
*
|
||||||
|
* @param fileName Internal ID of entry being updated
|
||||||
|
* @param contentType Content type of data
|
||||||
|
* @param is Source of updated data
|
||||||
|
* @throws java.lang.Exception On error
|
||||||
|
* @return Updated Entry as it exists on server
|
||||||
|
*/
|
||||||
|
public Entry updateMediaEntry(final String fileName, final String contentType, final InputStream is) throws Exception {
|
||||||
|
synchronized (FileStore.getFileStore()) {
|
||||||
|
|
||||||
|
final File tempFile = File.createTempFile(fileName, "tmp");
|
||||||
|
final FileOutputStream fos = new FileOutputStream(tempFile);
|
||||||
|
Utilities.copyInputToOutput(is, fos);
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
// Update media file
|
||||||
|
final FileInputStream fis = new FileInputStream(tempFile);
|
||||||
|
saveMediaFile(fileName, contentType, tempFile.length(), fis);
|
||||||
|
fis.close();
|
||||||
|
final File resourceFile = new File(getEntryMediaPath(fileName));
|
||||||
|
|
||||||
|
// Load media-link entry to return
|
||||||
|
final String entryPath = getEntryPath(fileName);
|
||||||
|
final InputStream in = FileStore.getFileStore().getFileInputStream(entryPath);
|
||||||
|
final Entry atomEntry = loadAtomResourceEntry(in, resourceFile);
|
||||||
|
|
||||||
|
updateTimestamps(atomEntry);
|
||||||
|
updateMediaEntryAppLinks(atomEntry, fileName, false);
|
||||||
|
|
||||||
|
// Update feed with new entry
|
||||||
|
final Feed f = getFeedDocument();
|
||||||
|
updateFeedDocumentWithExistingEntry(f, atomEntry);
|
||||||
|
|
||||||
|
// Save updated media-link entry
|
||||||
|
final OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
|
||||||
|
updateMediaEntryAppLinks(atomEntry, fileName, true);
|
||||||
|
Atom10Generator.serializeEntry(atomEntry, new OutputStreamWriter(os, "UTF-8"));
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
|
||||||
|
return atomEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an entry and any associated media file.
|
||||||
|
*
|
||||||
|
* @param fsid Internal ID of entry
|
||||||
|
* @throws java.lang.Exception On error
|
||||||
|
*/
|
||||||
|
public void deleteEntry(final String fsid) throws Exception {
|
||||||
|
synchronized (FileStore.getFileStore()) {
|
||||||
|
|
||||||
|
// Remove entry from Feed
|
||||||
|
final Feed feed = getFeedDocument();
|
||||||
|
updateFeedDocumentRemovingEntry(feed, fsid);
|
||||||
|
|
||||||
|
final String entryFilePath = getEntryPath(fsid);
|
||||||
|
FileStore.getFileStore().deleteFile(entryFilePath);
|
||||||
|
|
||||||
|
final String entryMediaPath = getEntryMediaPath(fsid);
|
||||||
|
if (entryMediaPath != null) {
|
||||||
|
FileStore.getFileStore().deleteFile(entryMediaPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String entryDirPath = getEntryDirPath(fsid);
|
||||||
|
FileStore.getFileStore().deleteDirectory(entryDirPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(500L);
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFeedDocumentWithNewEntry(final Feed f, final Entry e) throws AtomException {
|
||||||
|
boolean inserted = false;
|
||||||
|
for (int i = 0; i < f.getEntries().size(); i++) {
|
||||||
|
final Entry entry = f.getEntries().get(i);
|
||||||
|
final AppModule mod = (AppModule) entry.getModule(AppModule.URI);
|
||||||
|
final AppModule newMod = (AppModule) e.getModule(AppModule.URI);
|
||||||
|
if (newMod.getEdited().before(mod.getEdited())) {
|
||||||
|
f.getEntries().add(i, e);
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inserted) {
|
||||||
|
f.getEntries().add(0, e);
|
||||||
|
}
|
||||||
|
updateFeedDocument(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFeedDocumentRemovingEntry(final Feed f, final String id) throws AtomException {
|
||||||
|
final Entry e = findEntry("urn:uuid:" + id, f);
|
||||||
|
f.getEntries().remove(e);
|
||||||
|
updateFeedDocument(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFeedDocumentWithExistingEntry(final Feed f, final Entry e) throws AtomException {
|
||||||
|
final Entry old = findEntry(e.getId(), f);
|
||||||
|
f.getEntries().remove(old);
|
||||||
|
|
||||||
|
boolean inserted = false;
|
||||||
|
for (int i = 0; i < f.getEntries().size(); i++) {
|
||||||
|
final Entry entry = f.getEntries().get(i);
|
||||||
|
final AppModule entryAppModule = (AppModule) entry.getModule(AppModule.URI);
|
||||||
|
final AppModule eAppModule = (AppModule) entry.getModule(AppModule.URI);
|
||||||
|
if (eAppModule.getEdited().before(entryAppModule.getEdited())) {
|
||||||
|
f.getEntries().add(i, e);
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inserted) {
|
||||||
|
f.getEntries().add(0, e);
|
||||||
|
}
|
||||||
|
updateFeedDocument(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Entry findEntry(final String id, final Feed feed) {
|
||||||
|
for (final Entry entry : feed.getEntries()) {
|
||||||
|
if (id.equals(entry.getId())) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFeedDocument(final Feed f) throws AtomException {
|
||||||
|
try {
|
||||||
|
synchronized (FileStore.getFileStore()) {
|
||||||
|
final WireFeedOutput wireFeedOutput = new WireFeedOutput();
|
||||||
|
final Document feedDoc = wireFeedOutput.outputJDom(f);
|
||||||
|
final XMLOutputter outputter = new XMLOutputter();
|
||||||
|
// outputter.setFormat(Format.getPrettyFormat());
|
||||||
|
final OutputStream fos = FileStore.getFileStore().getFileOutputStream(getFeedPath());
|
||||||
|
outputter.output(feedDoc, new OutputStreamWriter(fos, "UTF-8"));
|
||||||
|
}
|
||||||
|
} catch (final FeedException fex) {
|
||||||
|
throw new AtomException(fex);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new AtomException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream createDefaultFeedDocument(final String uri) throws AtomException {
|
||||||
|
|
||||||
|
final Feed f = new Feed();
|
||||||
|
f.setTitle("Feed");
|
||||||
|
f.setId(uri);
|
||||||
|
f.setFeedType(FEED_TYPE);
|
||||||
|
|
||||||
|
final Link selfLink = new Link();
|
||||||
|
selfLink.setRel("self");
|
||||||
|
selfLink.setHref(uri);
|
||||||
|
f.getOtherLinks().add(selfLink);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final WireFeedOutput wireFeedOutput = new WireFeedOutput();
|
||||||
|
final Document feedDoc = wireFeedOutput.outputJDom(f);
|
||||||
|
final XMLOutputter outputter = new XMLOutputter();
|
||||||
|
// outputter.setFormat(Format.getCompactFormat());
|
||||||
|
final OutputStream fos = FileStore.getFileStore().getFileOutputStream(getFeedPath());
|
||||||
|
outputter.output(feedDoc, new OutputStreamWriter(fos, "UTF-8"));
|
||||||
|
|
||||||
|
} catch (final FeedException ex) {
|
||||||
|
throw new AtomException(ex);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new AtomException(ex);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return FileStore.getFileStore().getFileInputStream(getFeedPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Entry loadAtomResourceEntry(final InputStream in, final File file) {
|
||||||
|
try {
|
||||||
|
final Entry entry = Atom10Parser.parseEntry(new BufferedReader(new InputStreamReader(in)), null, Locale.US);
|
||||||
|
updateMediaEntryAppLinks(entry, file.getName(), true);
|
||||||
|
return entry;
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEntryAppLinks(final Entry entry, final String fsid, final boolean singleEntry) {
|
||||||
|
|
||||||
|
entry.setId("urn:uuid:" + fsid);
|
||||||
|
|
||||||
|
// Look for existing alt links and the alt link
|
||||||
|
Link altLink = null;
|
||||||
|
List<Link> altLinks = entry.getAlternateLinks();
|
||||||
|
if (altLinks != null) {
|
||||||
|
for (final Link link : altLinks) {
|
||||||
|
if (link.getRel() == null || "alternate".equals(link.getRel())) {
|
||||||
|
altLink = link;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No alt links found, so add them now
|
||||||
|
altLinks = new ArrayList<Link>();
|
||||||
|
entry.setAlternateLinks(altLinks);
|
||||||
|
}
|
||||||
|
// The alt link not found, so add it now
|
||||||
|
if (altLink == null) {
|
||||||
|
altLink = new Link();
|
||||||
|
altLinks.add(altLink);
|
||||||
|
}
|
||||||
|
// Set correct value for the alt link
|
||||||
|
altLink.setRel("alternate");
|
||||||
|
altLink.setHref(getEntryViewURI(fsid));
|
||||||
|
|
||||||
|
// Look for existing other links and the edit link
|
||||||
|
Link editLink = null;
|
||||||
|
List<Link> otherLinks = entry.getOtherLinks();
|
||||||
|
if (otherLinks != null) {
|
||||||
|
for (final Link link : otherLinks) {
|
||||||
|
if ("edit".equals(link.getRel())) {
|
||||||
|
editLink = link;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No other links found, so add them now
|
||||||
|
otherLinks = new ArrayList<Link>();
|
||||||
|
entry.setOtherLinks(otherLinks);
|
||||||
|
}
|
||||||
|
// The edit link not found, so add it now
|
||||||
|
if (editLink == null) {
|
||||||
|
editLink = new Link();
|
||||||
|
otherLinks.add(editLink);
|
||||||
|
}
|
||||||
|
// Set correct value for the edit link
|
||||||
|
editLink.setRel("edit");
|
||||||
|
editLink.setHref(getEntryEditURI(fsid, relativeURIs, singleEntry));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMediaEntryAppLinks(final Entry entry, final String fileName, final boolean singleEntry) {
|
||||||
|
|
||||||
|
// TODO: figure out why PNG is missing from Java MIME types
|
||||||
|
final FileTypeMap map = FileTypeMap.getDefaultFileTypeMap();
|
||||||
|
if (map instanceof MimetypesFileTypeMap) {
|
||||||
|
try {
|
||||||
|
((MimetypesFileTypeMap) map).addMimeTypes("image/png png PNG");
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.setId(getEntryMediaViewURI(fileName));
|
||||||
|
entry.setTitle(fileName);
|
||||||
|
entry.setUpdated(new Date());
|
||||||
|
|
||||||
|
final List<Link> otherlinks = new ArrayList<Link>();
|
||||||
|
entry.setOtherLinks(otherlinks);
|
||||||
|
|
||||||
|
final Link editlink = new Link();
|
||||||
|
editlink.setRel("edit");
|
||||||
|
editlink.setHref(getEntryEditURI(fileName, relativeURIs, singleEntry));
|
||||||
|
otherlinks.add(editlink);
|
||||||
|
|
||||||
|
final Link editMedialink = new Link();
|
||||||
|
editMedialink.setRel("edit-media");
|
||||||
|
editMedialink.setHref(getEntryMediaEditURI(fileName, relativeURIs, singleEntry));
|
||||||
|
otherlinks.add(editMedialink);
|
||||||
|
|
||||||
|
final Content content = entry.getContents().get(0);
|
||||||
|
content.setSrc(getEntryMediaViewURI(fileName));
|
||||||
|
final List<Content> contents = new ArrayList<Content>();
|
||||||
|
contents.add(content);
|
||||||
|
entry.setContents(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Rome Atom entry based on a Roller entry. Content is escaped. Link is stored as
|
||||||
|
* rel=alternate link.
|
||||||
|
*/
|
||||||
|
private Entry loadAtomEntry(final InputStream in) {
|
||||||
|
try {
|
||||||
|
return Atom10Parser.parseEntry(new BufferedReader(new InputStreamReader(in, "UTF-8")), null, Locale.US);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update existing or add new app:edited.
|
||||||
|
*/
|
||||||
|
private void updateTimestamps(final Entry entry) {
|
||||||
|
// We're not differenting between an update and an edit (yet)
|
||||||
|
entry.setUpdated(new Date());
|
||||||
|
|
||||||
|
AppModule appModule = (AppModule) entry.getModule(AppModule.URI);
|
||||||
|
if (appModule == null) {
|
||||||
|
appModule = new AppModuleImpl();
|
||||||
|
final List<Module> modules = entry.getModules() == null ? new ArrayList<Module>() : entry.getModules();
|
||||||
|
modules.add(appModule);
|
||||||
|
entry.setModules(modules);
|
||||||
|
}
|
||||||
|
appModule.setEdited(entry.getUpdated());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save file to website's resource directory.
|
||||||
|
*
|
||||||
|
* @param handle Weblog handle to save to
|
||||||
|
* @param name Name of file to save
|
||||||
|
* @param size Size of file to be saved
|
||||||
|
* @param is Read file from input stream
|
||||||
|
*/
|
||||||
|
private void saveMediaFile(final String name, final String contentType, final long size, final InputStream is) throws AtomException {
|
||||||
|
|
||||||
|
final byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
final File dirPath = new File(getEntryMediaPath(name));
|
||||||
|
if (!dirPath.getParentFile().exists()) {
|
||||||
|
dirPath.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
OutputStream bos = null;
|
||||||
|
try {
|
||||||
|
bos = new FileOutputStream(dirPath.getAbsolutePath());
|
||||||
|
while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {
|
||||||
|
bos.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new AtomException("ERROR uploading file", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
bos.flush();
|
||||||
|
bos.close();
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file name for a file based on a weblog handle, title string and a content-type.
|
||||||
|
*
|
||||||
|
* @param handle Weblog handle
|
||||||
|
* @param title Title to be used as basis for file name (or null)
|
||||||
|
* @param contentType Content type of file (must not be null)
|
||||||
|
*
|
||||||
|
* If a title is specified, the method will apply the same create-anchor logic we use
|
||||||
|
* for weblog entries to create a file name based on the title.
|
||||||
|
*
|
||||||
|
* If title is null, the base file name will be the weblog handle plus a YYYYMMDDHHSS
|
||||||
|
* timestamp.
|
||||||
|
*
|
||||||
|
* The extension will be formed by using the part of content type that comes after he
|
||||||
|
* slash.
|
||||||
|
*
|
||||||
|
* For example: weblog.handle = "daveblog" title = "Port Antonio" content-type =
|
||||||
|
* "image/jpg" Would result in port_antonio.jpg
|
||||||
|
*
|
||||||
|
* Another example: weblog.handle = "daveblog" title = null content-type =
|
||||||
|
* "image/jpg" Might result in daveblog-200608201034.jpg
|
||||||
|
*/
|
||||||
|
private String createFileName(final String title, final String contentType) {
|
||||||
|
|
||||||
|
if (handle == null) {
|
||||||
|
throw new IllegalArgumentException("weblog handle cannot be null");
|
||||||
|
}
|
||||||
|
if (contentType == null) {
|
||||||
|
throw new IllegalArgumentException("contentType cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = null;
|
||||||
|
|
||||||
|
final SimpleDateFormat sdf = new SimpleDateFormat();
|
||||||
|
sdf.applyPattern("yyyyMMddHHssSSS");
|
||||||
|
|
||||||
|
// Determine the extension based on the contentType. This is a hack.
|
||||||
|
// The info we need to map from contentType to file extension is in
|
||||||
|
// JRE/lib/content-type.properties, but Java Activation doesn't provide
|
||||||
|
// a way to do a reverse mapping or to get at the data.
|
||||||
|
final String[] typeTokens = contentType.split("/");
|
||||||
|
final String ext = typeTokens[1];
|
||||||
|
|
||||||
|
if (title != null && !title.trim().equals("")) {
|
||||||
|
// We've got a title, so use it to build file name
|
||||||
|
final String base = Utilities.replaceNonAlphanumeric(title, ' ');
|
||||||
|
final StringTokenizer toker = new StringTokenizer(base);
|
||||||
|
String tmp = null;
|
||||||
|
int count = 0;
|
||||||
|
while (toker.hasMoreTokens() && count < 5) {
|
||||||
|
String s = toker.nextToken();
|
||||||
|
s = s.toLowerCase();
|
||||||
|
tmp = tmp == null ? s : tmp + "_" + s;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
fileName = tmp + "-" + sdf.format(new Date()) + "." + ext;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// No title or text, so instead we'll use the item's date
|
||||||
|
// in YYYYMMDD format to form the file name
|
||||||
|
fileName = handle + "-" + sdf.format(new Date()) + "." + ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ URI methods
|
||||||
|
|
||||||
|
private String getEntryEditURI(final String fsid, final boolean relative, final boolean singleEntry) {
|
||||||
|
String entryURI = null;
|
||||||
|
if (relative) {
|
||||||
|
if (singleEntry) {
|
||||||
|
entryURI = fsid;
|
||||||
|
} else {
|
||||||
|
entryURI = singular + "/" + fsid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entryURI = contextURI + servletPath + "/" + handle + "/" + singular + "/" + fsid;
|
||||||
|
}
|
||||||
|
return entryURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEntryViewURI(final String fsid) {
|
||||||
|
return contextURI + "/" + handle + "/" + collection + "/" + fsid + "/entry.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEntryMediaEditURI(final String fsid, final boolean relative, final boolean singleEntry) {
|
||||||
|
String entryURI = null;
|
||||||
|
if (relative) {
|
||||||
|
if (singleEntry) {
|
||||||
|
entryURI = "media/" + fsid;
|
||||||
|
} else {
|
||||||
|
entryURI = singular + "/media/" + fsid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entryURI = contextURI + servletPath + "/" + handle + "/" + singular + "/media/" + fsid;
|
||||||
|
}
|
||||||
|
return entryURI;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEntryMediaViewURI(final String fsid) {
|
||||||
|
return contextURI + "/" + handle + "/" + collection + "/" + fsid + "/media/" + fsid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCategoriesURI() {
|
||||||
|
if (!relativeURIs) {
|
||||||
|
return contextURI + servletPath + "/" + handle + "/" + singular + "/categories";
|
||||||
|
} else {
|
||||||
|
return servletPath + "/" + handle + "/" + singular + "/categories";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------- File path methods
|
||||||
|
|
||||||
|
private String getBaseDir() {
|
||||||
|
return baseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFeedPath() {
|
||||||
|
return getBaseDir() + handle + File.separator + collection + File.separator + "feed.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEntryDirPath(final String id) {
|
||||||
|
return getBaseDir() + handle + File.separator + collection + File.separator + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEntryPath(final String id) {
|
||||||
|
return getEntryDirPath(id) + File.separator + "entry.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEntryMediaPath(final String id) {
|
||||||
|
return getEntryDirPath(id) + File.separator + "media" + File.separator + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkExistence(final String path) throws AtomNotFoundException {
|
||||||
|
if (!FileStore.getFileStore().exists(path)) {
|
||||||
|
throw new AtomNotFoundException("Entry does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server.impl;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.Workspace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File based Atom service Workspace.
|
||||||
|
*/
|
||||||
|
public class FileBasedWorkspace extends Workspace {
|
||||||
|
|
||||||
|
/** Creates a new instance of FileBasedWorkspace */
|
||||||
|
public FileBasedWorkspace(final String handle, final String baseDir) {
|
||||||
|
super(handle, "text");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server.impl;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class which helps in handling File persistence related operations.
|
||||||
|
*/
|
||||||
|
class FileStore {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(FileStore.class);
|
||||||
|
|
||||||
|
private static final FileStore fileStore = new FileStore();
|
||||||
|
|
||||||
|
public String getNextId() {
|
||||||
|
// return UUID.randomUUID().toString(); // requires JDK 1.5
|
||||||
|
return RandomStringUtils.randomAlphanumeric(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exists(final String path) {
|
||||||
|
final File f = new File(path);
|
||||||
|
return f.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getFileInputStream(final String path) {
|
||||||
|
LOG.debug("getFileContents path: " + path);
|
||||||
|
try {
|
||||||
|
return new BufferedInputStream(new FileInputStream(path));
|
||||||
|
} catch (final FileNotFoundException e) {
|
||||||
|
LOG.debug(" File not found: " + path);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream getFileOutputStream(final String path) {
|
||||||
|
LOG.debug("getFileOutputStream path: " + path);
|
||||||
|
try {
|
||||||
|
final File f = new File(path);
|
||||||
|
f.getParentFile().mkdirs();
|
||||||
|
return new BufferedOutputStream(new FileOutputStream(f));
|
||||||
|
} catch (final FileNotFoundException e) {
|
||||||
|
LOG.debug(" File not found: " + path);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createOrUpdateFile(final String path, final InputStream content) throws FileNotFoundException, IOException {
|
||||||
|
LOG.debug("createOrUpdateFile path: " + path);
|
||||||
|
final File f = new File(path);
|
||||||
|
f.mkdirs();
|
||||||
|
final FileOutputStream out = new FileOutputStream(f);
|
||||||
|
|
||||||
|
final byte[] buffer = new byte[2048];
|
||||||
|
int read;
|
||||||
|
while ((read = content.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteFile(final String path) {
|
||||||
|
LOG.debug("deleteFile path: " + path);
|
||||||
|
final File f = new File(path);
|
||||||
|
if (!f.delete()) {
|
||||||
|
LOG.debug(" Failed to delete: " + f.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileStore getFileStore() {
|
||||||
|
return fileStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteDirectory(final String path) {
|
||||||
|
return deleteDirectory(new File(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteDirectory(final File path) {
|
||||||
|
LOG.debug("deleteDirectory path: " + path);
|
||||||
|
if (path.exists()) {
|
||||||
|
final File[] files = path.listFiles();
|
||||||
|
for (final File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
deleteDirectory(file);
|
||||||
|
} else {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path.delete();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base implementation of a blog entry.
|
||||||
|
*/
|
||||||
|
public abstract class BaseBlogEntry implements BlogEntry {
|
||||||
|
protected String id = null;
|
||||||
|
protected Person author = null;
|
||||||
|
protected Content content = null;
|
||||||
|
protected String title = null;
|
||||||
|
protected String permalink = null;
|
||||||
|
protected String summary = null;
|
||||||
|
protected Date modificationDate = null;
|
||||||
|
protected Date publicationDate = null;
|
||||||
|
protected List<Category> categories = new ArrayList<Category>();
|
||||||
|
protected boolean draft = false;
|
||||||
|
private Blog blog = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contruct abstract blog entry.
|
||||||
|
*/
|
||||||
|
public BaseBlogEntry(final Blog blog) {
|
||||||
|
this.blog = blog;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getPermalink() {
|
||||||
|
return permalink;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPermalink(final String permalink) {
|
||||||
|
this.permalink = permalink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Person getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setAuthor(final Person author) {
|
||||||
|
this.author = author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Content getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setContent(final Content content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean getDraft() {
|
||||||
|
return draft;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setDraft(final boolean draft) {
|
||||||
|
this.draft = draft;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Date getPublicationDate() {
|
||||||
|
return publicationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setPublicationDate(final Date pubDate) {
|
||||||
|
publicationDate = pubDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Date getModificationDate() {
|
||||||
|
return modificationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setModificationDate(final Date date) {
|
||||||
|
modificationDate = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setTitle(final String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSummary() {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setSummary(final String summary) {
|
||||||
|
this.summary = summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Category> getCategories() {
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setCategories(final List<Category> categories) {
|
||||||
|
this.categories = categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Blog getBlog() {
|
||||||
|
return blog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBlog(final Blog blog) {
|
||||||
|
this.blog = blog;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation, returns id.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
235
src/main/java/com/rometools/propono/blogclient/Blog.java
Normal file
235
src/main/java/com/rometools/propono/blogclient/Blog.java
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Represents a blog, which has collections of entries and resources. You can access the collections
|
||||||
|
* using the getCollections() and getCollection(String name) methods, which return Blog.Collection
|
||||||
|
* objects, which you can use to create, retrieve update or delete entries within a collection.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface Blog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token can be used to fetch this blog again from getBlog() method.
|
||||||
|
*
|
||||||
|
* @return Blog object specified by token.
|
||||||
|
*/
|
||||||
|
public String getToken();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of this blog.
|
||||||
|
*
|
||||||
|
* @return Display name of this blog.
|
||||||
|
*/
|
||||||
|
public String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single BlogEntry (or BlogResource) by token.
|
||||||
|
*
|
||||||
|
* @param token Token from blog entry's getToken() method.
|
||||||
|
* @throws com.rometools.rome.propono.blogclient.BlogClientException On error fetching the blog
|
||||||
|
* entry.
|
||||||
|
* @return Blog entry specified by token.
|
||||||
|
*/
|
||||||
|
public BlogEntry getEntry(String token) throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets listing of entry and resource collections available in the blog, including the primary
|
||||||
|
* collections.
|
||||||
|
*
|
||||||
|
* @throws BlogClientException On error fetching collections.
|
||||||
|
* @return List of Blog.Collection objects.
|
||||||
|
*/
|
||||||
|
public List<Collection> getCollections() throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get collection by token.
|
||||||
|
*
|
||||||
|
* @param token Token from a collection's getToken() method.
|
||||||
|
* @throws BlogClientException On error fetching collection.
|
||||||
|
* @return Blog.Collection object.
|
||||||
|
*/
|
||||||
|
public Collection getCollection(String token) throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an entry or resource collection on a blog server.
|
||||||
|
*/
|
||||||
|
public interface Collection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get blog that contains this collection.
|
||||||
|
*
|
||||||
|
* @return Blog that contains this collection.
|
||||||
|
*/
|
||||||
|
public Blog getBlog();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of collection.
|
||||||
|
*
|
||||||
|
* @return Title of collecton.
|
||||||
|
*/
|
||||||
|
public String getTitle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token that can be used to fetch collection.
|
||||||
|
*
|
||||||
|
* @return Token that can be used to fetch collection.
|
||||||
|
*/
|
||||||
|
public String getToken();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content-types accepted by collection.
|
||||||
|
*
|
||||||
|
* @return Comma-separated list of content-types accepted.
|
||||||
|
*/
|
||||||
|
public List<String> getAccepts();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if collection will accept a content-type.
|
||||||
|
*
|
||||||
|
* @param contentType Content-type to be considered.
|
||||||
|
* @return True of content type will be accepted, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean accepts(String contentType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return categories allowed by colletion.
|
||||||
|
*
|
||||||
|
* @throws BlogClientException On error fetching categories.
|
||||||
|
* @return List of BlogEntry.Category objects for this collection.
|
||||||
|
*/
|
||||||
|
public List<BlogEntry.Category> getCategories() throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create but do not save new entry in collection. To save entry, call its save() method.
|
||||||
|
*
|
||||||
|
* @throws BlogClientException On error creating entry.
|
||||||
|
* @return New BlogEntry object.
|
||||||
|
*/
|
||||||
|
public BlogEntry newEntry() throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create but do not save new resource in collection. To save resource, call its save()
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param name Name of new resource.
|
||||||
|
* @param contentType MIME content-type of new resource.
|
||||||
|
* @param bytes Data for new resource.
|
||||||
|
* @throws BlogClientException On error creating entry.
|
||||||
|
* @return New BlogResource object,
|
||||||
|
*/
|
||||||
|
public BlogResource newResource(String name, String contentType, byte[] bytes) throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get iterator over entries/resources in this collection.
|
||||||
|
*
|
||||||
|
* @return List of BlogEntry objects, some may be BlogResources.
|
||||||
|
* @throws BlogClientException On error fetching entries/resources.
|
||||||
|
*/
|
||||||
|
public Iterator<BlogEntry> getEntries() throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save or update a BlogEntry in this collection by adding it to this collection and then
|
||||||
|
* calling it's entry.save() method.
|
||||||
|
*
|
||||||
|
* @param entry BlogEntry to be saved.
|
||||||
|
* @throws BlogClientException On error saving entry.
|
||||||
|
* @return URI of entry.
|
||||||
|
*/
|
||||||
|
public String saveEntry(BlogEntry entry) throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save or update resource in this collection
|
||||||
|
*
|
||||||
|
* @param resource BlogResource to be saved.
|
||||||
|
* @throws BlogClientException On error saving resource.
|
||||||
|
* @return URI of resource.
|
||||||
|
*/
|
||||||
|
public String saveResource(BlogResource resource) throws BlogClientException;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated primary collection methods, instead use collections directly.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get iterator over entries in primary entries collection (the first collection that accepts
|
||||||
|
* entries). Note that entries may be partial, so don't try to update and save them: to update
|
||||||
|
* and entry, first fetch it with getEntry(), change fields, then call entry.save();
|
||||||
|
*
|
||||||
|
* @return To iterate over all entries in collection.
|
||||||
|
* @throws BlogClientException On failure or if there is no primary entries collection.
|
||||||
|
*
|
||||||
|
* @deprecated Instead use collections directly.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public Iterator<BlogEntry> getEntries() throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get entries in primary resources collection (the first collection that accepts anything other
|
||||||
|
* than entries).
|
||||||
|
*
|
||||||
|
* @throws BlogClientException On failure or if there is no primary resources collection.
|
||||||
|
* @return To iterate over all resojrces in collection.
|
||||||
|
*
|
||||||
|
* @deprecated Instead use collections directly.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public Iterator<BlogEntry> getResources() throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create but do not save it to server new BlogEntry in primary entries collection (the first
|
||||||
|
* collection found that accepts entries). To save the entry to the server to a collection, use
|
||||||
|
* the entry's save() method.
|
||||||
|
*
|
||||||
|
* @throws BlogClientException On error or if there is no primary entries collection.
|
||||||
|
* @return Unsaved BlogEntry in primary entries collection.
|
||||||
|
*
|
||||||
|
* @deprecated Instead use collections directly.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public BlogEntry newEntry() throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create but do not save it to server new BlogResource in primary resources collection (the
|
||||||
|
* first collection found that accepts resources). To save the resource to the server to a
|
||||||
|
* collection, use the resource's save() method.
|
||||||
|
*
|
||||||
|
* @param name Name of resource to be saved.
|
||||||
|
* @param type MIME content type of resource data.
|
||||||
|
* @param bytes Bytes of resource data.
|
||||||
|
* @throws BlogClientException On error or if there is no primary respurces collection.
|
||||||
|
* @return Unsaved BlogEntry in primary resources collection.
|
||||||
|
*
|
||||||
|
* @deprecated Instead use collections directly.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public BlogResource newResource(String name, String type, byte[] bytes) throws BlogClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of available BlogEntry.Category in primary entries collection.
|
||||||
|
*
|
||||||
|
* @throws BlogClientException On error or if there is no primary entries collection.
|
||||||
|
* @return List of BlogEntry.Category objects.
|
||||||
|
*
|
||||||
|
* @deprecated Instead use collections directly.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public List<BlogEntry.Category> getCategories() throws BlogClientException;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Blog Client exception, the library throws these instead of implementation specific
|
||||||
|
* exceptions.
|
||||||
|
*/
|
||||||
|
public class BlogClientException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new exception
|
||||||
|
*
|
||||||
|
* @param msg Text message that explains exception
|
||||||
|
*/
|
||||||
|
public BlogClientException(final String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new exception which wraps a throwable.
|
||||||
|
*
|
||||||
|
* @param msg Text message that explains exception
|
||||||
|
* @param t Throwable to be wrapped by exception
|
||||||
|
*/
|
||||||
|
public BlogClientException(final String msg, final Throwable t) {
|
||||||
|
super(msg, t);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A BlogConnection is a single-user connection to a blog server where the user has access to
|
||||||
|
* multiple blogs, which are each represented by a Blog interface.
|
||||||
|
*/
|
||||||
|
public interface BlogConnection {
|
||||||
|
|
||||||
|
/** Returns collection of blogs available from this connection */
|
||||||
|
public abstract List<Blog> getBlogs();
|
||||||
|
|
||||||
|
/** Get blog by token */
|
||||||
|
public abstract Blog getBlog(String token);
|
||||||
|
|
||||||
|
/** Set appkey (optional, needed by some blog servers) */
|
||||||
|
public void setAppkey(String appkey);
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point to the Blogapps blog client library.
|
||||||
|
*/
|
||||||
|
public class BlogConnectionFactory {
|
||||||
|
|
||||||
|
// BlogConnection implementations must:
|
||||||
|
// 1) implement BlogConnection
|
||||||
|
// 2) privide contructor that accepts three strings args: url, username and password.
|
||||||
|
|
||||||
|
// TODO: make implementations configurable
|
||||||
|
private static String ATOMPROTOCOL_IMPL_CLASS = "com.rometools.propono.blogclient.atomprotocol.AtomConnection";
|
||||||
|
|
||||||
|
private static String METAWEBLOG_IMPL_CLASS = "com.rometools.propono.blogclient.metaweblog.MetaWeblogConnection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a connection to a blog server.
|
||||||
|
*
|
||||||
|
* @param type Connection type, must be "atom" or "metaweblog"
|
||||||
|
* @param url End-point URL to connect to
|
||||||
|
* @param username Username for login to blog server
|
||||||
|
* @param password Password for login to blog server
|
||||||
|
*/
|
||||||
|
public static BlogConnection getBlogConnection(final String type, final String url, final String username, final String password)
|
||||||
|
throws BlogClientException {
|
||||||
|
BlogConnection blogConnection = null;
|
||||||
|
if (type == null || type.equals("metaweblog")) {
|
||||||
|
blogConnection = createBlogConnection(METAWEBLOG_IMPL_CLASS, url, username, password);
|
||||||
|
} else if (type.equals("atom")) {
|
||||||
|
blogConnection = createBlogConnection(ATOMPROTOCOL_IMPL_CLASS, url, username, password);
|
||||||
|
} else {
|
||||||
|
throw new BlogClientException("Type must be 'atom' or 'metaweblog'");
|
||||||
|
}
|
||||||
|
return blogConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlogConnection createBlogConnection(final String className, final String url, final String username, final String password)
|
||||||
|
throws BlogClientException {
|
||||||
|
|
||||||
|
Class<?> conClass;
|
||||||
|
try {
|
||||||
|
conClass = Class.forName(className);
|
||||||
|
} catch (final ClassNotFoundException ex) {
|
||||||
|
throw new BlogClientException("BlogConnection impl. class not found: " + className, ex);
|
||||||
|
}
|
||||||
|
final Class<?>[] args = new Class[] { String.class, String.class, String.class };
|
||||||
|
Constructor<?> ctor;
|
||||||
|
try {
|
||||||
|
ctor = conClass.getConstructor(args);
|
||||||
|
return (BlogConnection) ctor.newInstance(new Object[] { url, username, password });
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw new BlogClientException("ERROR instantiating BlogConnection impl.", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
261
src/main/java/com/rometools/propono/blogclient/BlogEntry.java
Normal file
261
src/main/java/com/rometools/propono/blogclient/BlogEntry.java
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single blog entry.
|
||||||
|
*/
|
||||||
|
public interface BlogEntry {
|
||||||
|
|
||||||
|
/** Get token, which can be used to fetch the blog entry */
|
||||||
|
public String getToken();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save this entry to it's collection. If this is a new entry and does not have a collection
|
||||||
|
* yet, then save() will save it to the primary collection.
|
||||||
|
*/
|
||||||
|
public void save() throws BlogClientException;
|
||||||
|
|
||||||
|
/** Delete this entry from blog server */
|
||||||
|
public void delete() throws BlogClientException;
|
||||||
|
|
||||||
|
/** Permanent link to this entry (assigned by server) */
|
||||||
|
public String getPermalink();
|
||||||
|
|
||||||
|
/** Blog is associated with a blog */
|
||||||
|
public Blog getBlog();
|
||||||
|
|
||||||
|
/** Get categories, a list of BlogEntry.Category objects */
|
||||||
|
public List<Category> getCategories();
|
||||||
|
|
||||||
|
/** Set categories, a list of BlogEntry.Category objects */
|
||||||
|
public void setCategories(List<Category> categories);
|
||||||
|
|
||||||
|
/** Get globally unique ID of this blog entry */
|
||||||
|
public String getId();
|
||||||
|
|
||||||
|
/** Get title of this blog entry */
|
||||||
|
public String getTitle();
|
||||||
|
|
||||||
|
/** Set title of this blog entry */
|
||||||
|
public void setTitle(String title);
|
||||||
|
|
||||||
|
/** Get summary of this blog entry */
|
||||||
|
public String getSummary();
|
||||||
|
|
||||||
|
/** Set summary of this blog entry */
|
||||||
|
public void setSummary(String summary);
|
||||||
|
|
||||||
|
/** Get content of this blog entry */
|
||||||
|
public Content getContent();
|
||||||
|
|
||||||
|
/** Set content of this blog entry */
|
||||||
|
public void setContent(Content content);
|
||||||
|
|
||||||
|
/** Get draft status of this entry */
|
||||||
|
public boolean getDraft();
|
||||||
|
|
||||||
|
/** Set draft status of this entry */
|
||||||
|
public void setDraft(boolean draft);
|
||||||
|
|
||||||
|
/** Get author of this entry */
|
||||||
|
public Person getAuthor();
|
||||||
|
|
||||||
|
/** Set author of this entry */
|
||||||
|
public void setAuthor(Person author);
|
||||||
|
|
||||||
|
/** Set publish date of this entry */
|
||||||
|
public Date getPublicationDate();
|
||||||
|
|
||||||
|
/** Get publish date of this entry */
|
||||||
|
public void setPublicationDate(Date date);
|
||||||
|
|
||||||
|
/** Get update date of this entry */
|
||||||
|
public Date getModificationDate();
|
||||||
|
|
||||||
|
/** Set update date of this entry */
|
||||||
|
public void setModificationDate(Date date);
|
||||||
|
|
||||||
|
/** Represents blog entry content */
|
||||||
|
public class Content {
|
||||||
|
String type = "html";
|
||||||
|
String value = null;
|
||||||
|
String src = null;
|
||||||
|
|
||||||
|
/** Construct content */
|
||||||
|
public Content() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct content with value (and type="html") */
|
||||||
|
public Content(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get value of content if in-line */
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set value of content if in-line */
|
||||||
|
public void setValue(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get type of content, either "text", "html", "xhtml" or a MIME content-type. Defaults to
|
||||||
|
* HTML.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set type of content, either "text", "html", "xhtml" or a MIME content-type. Defaults to
|
||||||
|
* HTML.
|
||||||
|
*/
|
||||||
|
public void setType(final String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get URI of content if out-of-line */
|
||||||
|
public String getSrc() {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set URI of content if out-of-line */
|
||||||
|
public void setSrc(final String src) {
|
||||||
|
this.src = src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents a blog author or contributor */
|
||||||
|
public class Person {
|
||||||
|
String name;
|
||||||
|
String email;
|
||||||
|
String url;
|
||||||
|
|
||||||
|
/** Get person's email */
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set person's email */
|
||||||
|
public void setEmail(final String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get person's name */
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set person's name */
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get person's URL */
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set person's URL */
|
||||||
|
public void setUrl(final String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns person's name */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents a weblog category */
|
||||||
|
public class Category {
|
||||||
|
String id;
|
||||||
|
String name;
|
||||||
|
String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new Catetory
|
||||||
|
*/
|
||||||
|
public Category() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new category with name.
|
||||||
|
*/
|
||||||
|
public Category(final String id) {
|
||||||
|
this.id = id;
|
||||||
|
name = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if categories are equal based on id.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
final Category other = (Category) obj;
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getId() != null && other.getId() != null && getId().equals(other.getId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get category id */
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set category id */
|
||||||
|
public void setId(final String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get category display name */
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set category display name */
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get URL of category domain */
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set URL of category domain */
|
||||||
|
public void setUrl(final String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return category's name or id for display */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name != null ? name : id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a file that has been uploaded to a blog.
|
||||||
|
* <p />
|
||||||
|
* Resources are modeled as a type of BlogEntry, but be aware: not all servers can save resource
|
||||||
|
* metadata (i.e. title, category, author, etc.). MetaWeblog based servers can't save metadata at
|
||||||
|
* all and Atom protocol servers are not required to preserve uploaded file metadata.
|
||||||
|
*/
|
||||||
|
public interface BlogResource extends BlogEntry {
|
||||||
|
|
||||||
|
/** Get resource name (name is required) */
|
||||||
|
public String getName();
|
||||||
|
|
||||||
|
/** Get resource as stream, using content.src as URL */
|
||||||
|
public InputStream getAsStream() throws BlogClientException;
|
||||||
|
|
||||||
|
/** Update resource by immediately uploading new bytes to server */
|
||||||
|
public void update(byte[] newBytes) throws BlogClientException;
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.atomprotocol;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.client.ClientAtomService;
|
||||||
|
import com.rometools.propono.atom.client.ClientCollection;
|
||||||
|
import com.rometools.propono.atom.client.ClientEntry;
|
||||||
|
import com.rometools.propono.atom.client.ClientMediaEntry;
|
||||||
|
import com.rometools.propono.atom.client.ClientWorkspace;
|
||||||
|
import com.rometools.propono.blogclient.Blog;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogResource;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atom protocol implementation of the BlogClient Blog interface.
|
||||||
|
*/
|
||||||
|
public class AtomBlog implements Blog {
|
||||||
|
|
||||||
|
private String name = null;
|
||||||
|
private ClientAtomService service;
|
||||||
|
private ClientWorkspace workspace = null;
|
||||||
|
private AtomCollection entriesCollection = null;
|
||||||
|
private AtomCollection resourcesCollection = null;
|
||||||
|
private final Map<String, AtomCollection> collections = new TreeMap<String, AtomCollection>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create AtomBlog using specified HTTPClient, user account and workspace, called by
|
||||||
|
* AtomConnection. Fetches Atom Service document and creates an AtomCollection object for each
|
||||||
|
* collection found. The first entry collection is considered the primary entry collection. And
|
||||||
|
* the first resource collection is considered the primary resource collection.
|
||||||
|
*/
|
||||||
|
AtomBlog(final ClientAtomService service, final ClientWorkspace workspace) {
|
||||||
|
|
||||||
|
setService(service);
|
||||||
|
setWorkspace(workspace);
|
||||||
|
name = workspace.getTitle();
|
||||||
|
final List<com.rometools.propono.atom.common.Collection> collect = workspace.getCollections();
|
||||||
|
final Iterator<com.rometools.propono.atom.common.Collection> members = collect.iterator();
|
||||||
|
|
||||||
|
while (members.hasNext()) {
|
||||||
|
final ClientCollection col = (ClientCollection) members.next();
|
||||||
|
if (col.accepts("entry") && entriesCollection == null) {
|
||||||
|
// first entry collection is primary entry collection
|
||||||
|
entriesCollection = new AtomCollection(this, col);
|
||||||
|
} else if (!col.accepts("entry") && resourcesCollection == null) {
|
||||||
|
// first non-entry collection is primary resource collection
|
||||||
|
resourcesCollection = new AtomCollection(this, col);
|
||||||
|
}
|
||||||
|
collections.put(col.getHrefResolved(), new AtomCollection(this, col));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String display of blog, returns name.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getToken() {
|
||||||
|
return entriesCollection.getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry newEntry() throws BlogClientException {
|
||||||
|
if (entriesCollection == null) {
|
||||||
|
throw new BlogClientException("No entry collection");
|
||||||
|
}
|
||||||
|
return entriesCollection.newEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry getEntry(final String token) throws BlogClientException {
|
||||||
|
ClientEntry clientEntry = null;
|
||||||
|
try {
|
||||||
|
clientEntry = getService().getEntry(token);
|
||||||
|
} catch (final ProponoException ex) {
|
||||||
|
throw new BlogClientException("ERROR: fetching entry", ex);
|
||||||
|
}
|
||||||
|
if (clientEntry != null && clientEntry instanceof ClientMediaEntry) {
|
||||||
|
return new AtomResource(this, (ClientMediaEntry) clientEntry);
|
||||||
|
} else if (clientEntry != null && clientEntry instanceof ClientEntry) {
|
||||||
|
return new AtomEntry(this, clientEntry);
|
||||||
|
} else {
|
||||||
|
throw new BlogClientException("ERROR: unknown object type returned");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<BlogEntry> getEntries() throws BlogClientException {
|
||||||
|
if (entriesCollection == null) {
|
||||||
|
throw new BlogClientException("No primary entry collection");
|
||||||
|
}
|
||||||
|
return new AtomEntryIterator(entriesCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<BlogEntry> getResources() throws BlogClientException {
|
||||||
|
if (resourcesCollection == null) {
|
||||||
|
throw new BlogClientException("No primary entry collection");
|
||||||
|
}
|
||||||
|
return new AtomEntryIterator(resourcesCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
String saveEntry(final BlogEntry entry) throws BlogClientException {
|
||||||
|
if (entriesCollection == null) {
|
||||||
|
throw new BlogClientException("No primary entry collection");
|
||||||
|
}
|
||||||
|
return entriesCollection.saveEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteEntry(final BlogEntry entry) throws BlogClientException {
|
||||||
|
if (entriesCollection == null) {
|
||||||
|
throw new BlogClientException("No primary entry collection");
|
||||||
|
}
|
||||||
|
entriesCollection.deleteEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<BlogEntry.Category> getCategories() throws BlogClientException {
|
||||||
|
if (entriesCollection == null) {
|
||||||
|
throw new BlogClientException("No primary entry collection");
|
||||||
|
}
|
||||||
|
return entriesCollection.getCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogResource newResource(final String name, final String contentType, final byte[] bytes) throws BlogClientException {
|
||||||
|
if (resourcesCollection == null) {
|
||||||
|
throw new BlogClientException("No resource collection");
|
||||||
|
}
|
||||||
|
return resourcesCollection.newResource(name, contentType, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
String saveResource(final BlogResource res) throws BlogClientException {
|
||||||
|
if (resourcesCollection == null) {
|
||||||
|
throw new BlogClientException("No primary resource collection");
|
||||||
|
}
|
||||||
|
return resourcesCollection.saveResource(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteResource(final BlogResource resource) throws BlogClientException {
|
||||||
|
deleteEntry(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Collection> getCollections() throws BlogClientException {
|
||||||
|
return new ArrayList<Collection>(collections.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Blog.Collection getCollection(final String token) throws BlogClientException {
|
||||||
|
return collections.get(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientAtomService getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setService(final ClientAtomService service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientWorkspace getWorkspace() {
|
||||||
|
return workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWorkspace(final ClientWorkspace workspace) {
|
||||||
|
this.workspace = workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.atomprotocol;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.client.ClientAtomService;
|
||||||
|
import com.rometools.propono.atom.client.ClientCollection;
|
||||||
|
import com.rometools.propono.atom.client.ClientEntry;
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.blogclient.Blog;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogResource;
|
||||||
|
import com.rometools.rome.feed.atom.Category;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atom protocol implementation of BlogClient Blog.Collection.
|
||||||
|
*/
|
||||||
|
public class AtomCollection implements Blog.Collection {
|
||||||
|
|
||||||
|
private Blog blog = null;
|
||||||
|
private List<BlogEntry.Category> categories = new ArrayList<BlogEntry.Category>();
|
||||||
|
|
||||||
|
private ClientCollection clientCollection = null;
|
||||||
|
|
||||||
|
AtomCollection(final AtomBlog blog, final ClientCollection col) {
|
||||||
|
this.blog = blog;
|
||||||
|
clientCollection = col;
|
||||||
|
for (final Object element : col.getCategories()) {
|
||||||
|
final Categories cats = (Categories) element;
|
||||||
|
for (final Object element2 : cats.getCategories()) {
|
||||||
|
final Category cat = (Category) element2;
|
||||||
|
final BlogEntry.Category blogCat = new BlogEntry.Category(cat.getTerm());
|
||||||
|
blogCat.setName(cat.getLabel());
|
||||||
|
blogCat.setUrl(cat.getScheme());
|
||||||
|
getCategories().add(blogCat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return getClientCollection().getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getToken() {
|
||||||
|
return getClientCollection().getHrefResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getAccepts() {
|
||||||
|
return getClientCollection().getAccepts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean accepts(final String ct) {
|
||||||
|
return getClientCollection().accepts(ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<BlogEntry> getEntries() throws BlogClientException {
|
||||||
|
return new AtomEntryIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry newEntry() throws BlogClientException {
|
||||||
|
final AtomBlog atomBlog = (AtomBlog) getBlog();
|
||||||
|
final BlogEntry entry = new AtomEntry(atomBlog, this);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogResource newResource(final String name, final String contentType, final byte[] bytes) throws BlogClientException {
|
||||||
|
return new AtomResource(this, name, contentType, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String saveResource(final BlogResource res) throws BlogClientException {
|
||||||
|
((AtomResource) res).setCollection(this);
|
||||||
|
res.save();
|
||||||
|
return res.getContent().getSrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String saveEntry(final BlogEntry entry) throws BlogClientException {
|
||||||
|
((AtomEntry) entry).setCollection(this);
|
||||||
|
entry.save();
|
||||||
|
return entry.getPermalink();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteEntry(final BlogEntry entry) throws BlogClientException {
|
||||||
|
try {
|
||||||
|
final ClientAtomService service = ((AtomBlog) getBlog()).getService();
|
||||||
|
final ClientEntry clientEntry = service.getEntry(entry.getToken());
|
||||||
|
clientEntry.remove();
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR deleting entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Blog getBlog() {
|
||||||
|
return blog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBlog(final AtomBlog blog) {
|
||||||
|
this.blog = blog;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<BlogEntry.Category> getCategories() {
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCategories(final List<BlogEntry.Category> categories) {
|
||||||
|
this.categories = categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientCollection getClientCollection() {
|
||||||
|
return clientCollection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.atomprotocol;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.client.AtomClientFactory;
|
||||||
|
import com.rometools.propono.atom.client.BasicAuthStrategy;
|
||||||
|
import com.rometools.propono.atom.client.ClientAtomService;
|
||||||
|
import com.rometools.propono.atom.client.ClientWorkspace;
|
||||||
|
import com.rometools.propono.atom.common.Workspace;
|
||||||
|
import com.rometools.propono.blogclient.Blog;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atom protocol of BlogConnection. Connects to Atom server, creates AtomBlog object for each Atom
|
||||||
|
* workspace found and within each blog a collection for each Atom collection found.
|
||||||
|
*/
|
||||||
|
public class AtomConnection implements BlogConnection {
|
||||||
|
|
||||||
|
private final Map<String, Blog> blogs = new HashMap<String, Blog>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Atom blog client instance for specified URL and user account.
|
||||||
|
*
|
||||||
|
* @param uri End-point URL of Atom service
|
||||||
|
* @param username Username of account
|
||||||
|
* @param password Password of account
|
||||||
|
*/
|
||||||
|
public AtomConnection(final String uri, final String username, final String password) throws BlogClientException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
final ClientAtomService service = AtomClientFactory.getAtomService(uri, new BasicAuthStrategy(username, password));
|
||||||
|
final Iterator<Workspace> iter = service.getWorkspaces().iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
final ClientWorkspace workspace = (ClientWorkspace) iter.next();
|
||||||
|
final Blog blog = new AtomBlog(service, workspace);
|
||||||
|
blogs.put(blog.getToken(), blog);
|
||||||
|
}
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw new BlogClientException("Error connecting to blog server", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Blog> getBlogs() {
|
||||||
|
return new ArrayList<Blog>(blogs.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Blog getBlog(final String token) {
|
||||||
|
return blogs.get(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setAppkey(final String appkey) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.atomprotocol;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.client.ClientEntry;
|
||||||
|
import com.rometools.propono.atom.common.rome.AppModule;
|
||||||
|
import com.rometools.propono.atom.common.rome.AppModuleImpl;
|
||||||
|
import com.rometools.propono.blogclient.BaseBlogEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.feed.atom.Entry;
|
||||||
|
import com.rometools.rome.feed.atom.Link;
|
||||||
|
import com.rometools.rome.feed.module.Module;
|
||||||
|
import com.rometools.rome.feed.synd.SyndPerson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atom protocol implementation of BlogEntry.
|
||||||
|
*/
|
||||||
|
public class AtomEntry extends BaseBlogEntry implements BlogEntry {
|
||||||
|
|
||||||
|
String editURI = null;
|
||||||
|
AtomCollection collection = null;
|
||||||
|
|
||||||
|
AtomEntry(final AtomBlog blog, final AtomCollection collection) throws BlogClientException {
|
||||||
|
super(blog);
|
||||||
|
this.collection = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomEntry(final AtomCollection collection, final ClientEntry entry) throws BlogClientException {
|
||||||
|
this((AtomBlog) collection.getBlog(), collection);
|
||||||
|
// clientEntry = entry;
|
||||||
|
copyFromRomeEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomEntry(final AtomBlog blog, final ClientEntry entry) throws BlogClientException {
|
||||||
|
super(blog);
|
||||||
|
// clientEntry = entry;
|
||||||
|
copyFromRomeEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToken() {
|
||||||
|
return editURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomCollection getCollection() {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCollection(final AtomCollection collection) {
|
||||||
|
this.collection = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if entry's token's are equal.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (o instanceof AtomEntry) {
|
||||||
|
final AtomEntry other = (AtomEntry) o;
|
||||||
|
if (other.getToken() != null && getToken() != null) {
|
||||||
|
return other.getToken().equals(getToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() throws BlogClientException {
|
||||||
|
final boolean create = getToken() == null;
|
||||||
|
if (create && getCollection() == null) {
|
||||||
|
throw new BlogClientException("Cannot save entry, no collection");
|
||||||
|
} else if (create) {
|
||||||
|
try {
|
||||||
|
final ClientEntry clientEntry = collection.getClientCollection().createEntry();
|
||||||
|
copyToRomeEntry(clientEntry);
|
||||||
|
collection.getClientCollection().addEntry(clientEntry);
|
||||||
|
copyFromRomeEntry(clientEntry);
|
||||||
|
} catch (final ProponoException ex) {
|
||||||
|
throw new BlogClientException("Error saving entry", ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
final ClientEntry clientEntry = ((AtomBlog) getBlog()).getService().getEntry(getToken());
|
||||||
|
copyToRomeEntry(clientEntry);
|
||||||
|
clientEntry.update();
|
||||||
|
copyFromRomeEntry(clientEntry);
|
||||||
|
} catch (final ProponoException ex) {
|
||||||
|
throw new BlogClientException("Error updating entry", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() throws BlogClientException {
|
||||||
|
if (getToken() == null) {
|
||||||
|
throw new BlogClientException("Cannot delete unsaved entry");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final ClientEntry clientEntry = ((AtomBlog) getBlog()).getService().getEntry(editURI);
|
||||||
|
clientEntry.remove();
|
||||||
|
} catch (final ProponoException ex) {
|
||||||
|
throw new BlogClientException("Error removing entry", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyFromRomeEntry(final ClientEntry entry) {
|
||||||
|
id = entry.getId();
|
||||||
|
title = entry.getTitle();
|
||||||
|
editURI = entry.getEditURI();
|
||||||
|
final List<Link> altlinks = entry.getAlternateLinks();
|
||||||
|
if (altlinks != null) {
|
||||||
|
for (final Link link : altlinks) {
|
||||||
|
if ("alternate".equals(link.getRel()) || link.getRel() == null) {
|
||||||
|
permalink = link.getHrefResolved();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final List<com.rometools.rome.feed.atom.Content> contents = entry.getContents();
|
||||||
|
com.rometools.rome.feed.atom.Content romeContent = null;
|
||||||
|
if (contents != null && !contents.isEmpty()) {
|
||||||
|
romeContent = contents.get(0);
|
||||||
|
}
|
||||||
|
if (romeContent != null) {
|
||||||
|
content = new BlogEntry.Content(romeContent.getValue());
|
||||||
|
content.setType(romeContent.getType());
|
||||||
|
content.setSrc(romeContent.getSrc());
|
||||||
|
}
|
||||||
|
if (entry.getCategories() != null) {
|
||||||
|
final List<Category> cats = new ArrayList<Category>();
|
||||||
|
final List<com.rometools.rome.feed.atom.Category> romeCats = entry.getCategories();
|
||||||
|
for (final com.rometools.rome.feed.atom.Category romeCat : romeCats) {
|
||||||
|
final BlogEntry.Category cat = new BlogEntry.Category();
|
||||||
|
cat.setId(romeCat.getTerm());
|
||||||
|
cat.setUrl(romeCat.getScheme());
|
||||||
|
cat.setName(romeCat.getLabel());
|
||||||
|
cats.add(cat);
|
||||||
|
}
|
||||||
|
categories = cats;
|
||||||
|
}
|
||||||
|
final List<SyndPerson> authors = entry.getAuthors();
|
||||||
|
if (authors != null && !authors.isEmpty()) {
|
||||||
|
final com.rometools.rome.feed.atom.Person romeAuthor = (com.rometools.rome.feed.atom.Person) authors.get(0);
|
||||||
|
if (romeAuthor != null) {
|
||||||
|
author = new Person();
|
||||||
|
author.setName(romeAuthor.getName());
|
||||||
|
author.setEmail(romeAuthor.getEmail());
|
||||||
|
author.setUrl(romeAuthor.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publicationDate = entry.getPublished();
|
||||||
|
modificationDate = entry.getModified();
|
||||||
|
|
||||||
|
final AppModule control = (AppModule) entry.getModule(AppModule.URI);
|
||||||
|
if (control != null && control.getDraft() != null) {
|
||||||
|
draft = control.getDraft().booleanValue();
|
||||||
|
} else {
|
||||||
|
draft = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry copyToRomeEntry(final ClientEntry entry) {
|
||||||
|
if (id != null) {
|
||||||
|
entry.setId(id);
|
||||||
|
}
|
||||||
|
entry.setTitle(title);
|
||||||
|
if (author != null) {
|
||||||
|
final com.rometools.rome.feed.atom.Person person = new com.rometools.rome.feed.atom.Person();
|
||||||
|
person.setName(author.getName());
|
||||||
|
person.setEmail(author.getEmail());
|
||||||
|
person.setUrl(author.getUrl());
|
||||||
|
final List<SyndPerson> authors = new ArrayList<SyndPerson>();
|
||||||
|
authors.add(person);
|
||||||
|
entry.setAuthors(authors);
|
||||||
|
}
|
||||||
|
if (content != null) {
|
||||||
|
final com.rometools.rome.feed.atom.Content romeContent = new com.rometools.rome.feed.atom.Content();
|
||||||
|
romeContent.setValue(content.getValue());
|
||||||
|
romeContent.setType(content.getType());
|
||||||
|
final List<com.rometools.rome.feed.atom.Content> contents = new ArrayList<com.rometools.rome.feed.atom.Content>();
|
||||||
|
contents.add(romeContent);
|
||||||
|
entry.setContents(contents);
|
||||||
|
}
|
||||||
|
if (categories != null) {
|
||||||
|
final List<com.rometools.rome.feed.atom.Category> romeCats = new ArrayList<com.rometools.rome.feed.atom.Category>();
|
||||||
|
for (final Category cat : categories) {
|
||||||
|
final com.rometools.rome.feed.atom.Category romeCategory = new com.rometools.rome.feed.atom.Category();
|
||||||
|
romeCategory.setTerm(cat.getId());
|
||||||
|
romeCategory.setScheme(cat.getUrl());
|
||||||
|
romeCategory.setLabel(cat.getName());
|
||||||
|
romeCats.add(romeCategory);
|
||||||
|
}
|
||||||
|
entry.setCategories(romeCats);
|
||||||
|
}
|
||||||
|
entry.setPublished(publicationDate == null ? new Date() : publicationDate);
|
||||||
|
entry.setModified(modificationDate == null ? new Date() : modificationDate);
|
||||||
|
|
||||||
|
final List<Module> modules = new ArrayList<Module>();
|
||||||
|
final AppModule control = new AppModuleImpl();
|
||||||
|
control.setDraft(new Boolean(draft));
|
||||||
|
modules.add(control);
|
||||||
|
entry.setModules(modules);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.atomprotocol;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.client.ClientEntry;
|
||||||
|
import com.rometools.propono.atom.client.ClientMediaEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atom protocol implementation of BlogClient entry iterator.
|
||||||
|
*/
|
||||||
|
public class AtomEntryIterator implements Iterator<BlogEntry> {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AtomEntryIterator.class);
|
||||||
|
|
||||||
|
private Iterator<ClientEntry> iterator = null;
|
||||||
|
private AtomCollection collection = null;
|
||||||
|
|
||||||
|
AtomEntryIterator(final AtomCollection collection) throws BlogClientException {
|
||||||
|
try {
|
||||||
|
this.collection = collection;
|
||||||
|
iterator = collection.getClientCollection().getEntries();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR fetching collection", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if more entries are available.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return iterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get next entry.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry next() {
|
||||||
|
try {
|
||||||
|
final ClientEntry entry = iterator.next();
|
||||||
|
if (entry instanceof ClientMediaEntry) {
|
||||||
|
return new AtomResource(collection, (ClientMediaEntry) entry);
|
||||||
|
} else {
|
||||||
|
return new AtomEntry(collection, entry);
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
LOG.error("An error occured while fetching entry", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove is not supported.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
// optional method, not implemented
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.atomprotocol;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.client.ClientAtomService;
|
||||||
|
import com.rometools.propono.atom.client.ClientCollection;
|
||||||
|
import com.rometools.propono.atom.client.ClientEntry;
|
||||||
|
import com.rometools.propono.atom.client.ClientMediaEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogResource;
|
||||||
|
import com.rometools.rome.feed.atom.Link;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atom protocol implementation of BlogResource.
|
||||||
|
*/
|
||||||
|
public class AtomResource extends AtomEntry implements BlogResource {
|
||||||
|
|
||||||
|
private AtomCollection collection;
|
||||||
|
private byte[] bytes;
|
||||||
|
|
||||||
|
AtomResource(final AtomCollection collection, final String name, final String contentType, final byte[] bytes) throws BlogClientException {
|
||||||
|
super((AtomBlog) collection.getBlog(), collection);
|
||||||
|
this.collection = collection;
|
||||||
|
this.bytes = bytes;
|
||||||
|
final BlogEntry.Content rcontent = new BlogEntry.Content();
|
||||||
|
rcontent.setType(contentType);
|
||||||
|
setContent(rcontent);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomResource(final AtomCollection collection, final ClientMediaEntry entry) throws BlogClientException {
|
||||||
|
super(collection, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomResource(final AtomBlog blog, final ClientMediaEntry entry) throws BlogClientException {
|
||||||
|
super(blog, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public InputStream getAsStream() throws BlogClientException {
|
||||||
|
try {
|
||||||
|
return null; // ((ClientMediaEntry)clientEntry).getAsStream();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("Error creating entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void save() throws BlogClientException {
|
||||||
|
try {
|
||||||
|
if (getToken() == null) {
|
||||||
|
final ClientAtomService clientService = ((AtomBlog) getBlog()).getService();
|
||||||
|
final ClientCollection clientCollection = collection.getClientCollection();
|
||||||
|
|
||||||
|
final ClientMediaEntry clientEntry = new ClientMediaEntry(clientService, clientCollection, getTitle(), "", getContent().getType(), getBytes());
|
||||||
|
|
||||||
|
copyToRomeEntry(clientEntry);
|
||||||
|
collection.getClientCollection().addEntry(clientEntry);
|
||||||
|
editURI = clientEntry.getEditURI();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
final ClientAtomService clientService = ((AtomBlog) getBlog()).getService();
|
||||||
|
final ClientMediaEntry clientEntry = (ClientMediaEntry) clientService.getEntry(editURI);
|
||||||
|
clientEntry.update();
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("Error creating entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void update(final byte[] newBytes) throws BlogClientException {
|
||||||
|
try {
|
||||||
|
// ((ClientMediaEntry)clientEntry).setBytes(newBytes);
|
||||||
|
// clientEntry.update();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("Error creating entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void copyFromRomeEntry(final ClientEntry entry) {
|
||||||
|
super.copyFromRomeEntry(entry);
|
||||||
|
final List<Link> links = entry.getOtherLinks();
|
||||||
|
if (links != null) {
|
||||||
|
for (final Link link : links) {
|
||||||
|
if ("edit-media".equals(link.getRel())) {
|
||||||
|
id = link.getHrefResolved();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1,467 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.metaweblog;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
|
||||||
|
import com.rometools.propono.blogclient.Blog;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry.Category;
|
||||||
|
import com.rometools.propono.blogclient.BlogResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blog implementation that uses a mix of Blogger and MetaWeblog API methods.
|
||||||
|
*/
|
||||||
|
public class MetaWeblogBlog implements Blog {
|
||||||
|
|
||||||
|
private final String blogid;
|
||||||
|
private final String name;
|
||||||
|
private final URL url;
|
||||||
|
private final String userName;
|
||||||
|
private final String password;
|
||||||
|
private final Map<String, MetaWeblogBlogCollection> collections;
|
||||||
|
|
||||||
|
private String appkey = "dummy";
|
||||||
|
private XmlRpcClient xmlRpcClient = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getToken() {
|
||||||
|
return blogid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation of blog, returns the name.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmlRpcClient getXmlRpcClient() {
|
||||||
|
|
||||||
|
if (xmlRpcClient == null) {
|
||||||
|
final XmlRpcClientConfigImpl xmlrpcConfig = new XmlRpcClientConfigImpl();
|
||||||
|
xmlrpcConfig.setServerURL(url);
|
||||||
|
xmlRpcClient = new XmlRpcClient();
|
||||||
|
xmlRpcClient.setConfig(xmlrpcConfig);
|
||||||
|
}
|
||||||
|
return xmlRpcClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaWeblogBlog(final String blogid, final String name, final URL url, final String userName, final String password) {
|
||||||
|
this.blogid = blogid;
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
this.userName = userName;
|
||||||
|
this.password = password;
|
||||||
|
collections = new TreeMap<String, MetaWeblogBlogCollection>();
|
||||||
|
collections.put("entries", new MetaWeblogBlogCollection(this, "entries", "Entries", "entry"));
|
||||||
|
collections.put("resources", new MetaWeblogBlogCollection(this, "resources", "Resources", "*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaWeblogBlog(final String blogId, final String name, final URL url, final String userName, final String password, final String appkey) {
|
||||||
|
this(blogId, name, url, userName, password);
|
||||||
|
this.appkey = appkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry newEntry() {
|
||||||
|
return new MetaWeblogEntry(this, new HashMap<String, Object>());
|
||||||
|
}
|
||||||
|
|
||||||
|
String saveEntry(final BlogEntry entry) throws BlogClientException {
|
||||||
|
final Blog.Collection col = collections.get("entries");
|
||||||
|
return col.saveEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry getEntry(final String id) throws BlogClientException {
|
||||||
|
try {
|
||||||
|
final Object[] params = new Object[] { id, userName, password };
|
||||||
|
final Object response = getXmlRpcClient().execute("metaWeblog.getPost", params);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> result = (Map<String, Object>) response;
|
||||||
|
return new MetaWeblogEntry(this, result);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: XML-RPC error getting entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteEntry(final String id) throws BlogClientException {
|
||||||
|
try {
|
||||||
|
getXmlRpcClient().execute("blogger.deletePost", new Object[] { appkey, id, userName, password, Boolean.FALSE });
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: XML-RPC error getting entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<BlogEntry> getEntries() throws BlogClientException {
|
||||||
|
return new EntryIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogResource newResource(final String name, final String contentType, final byte[] bytes) throws BlogClientException {
|
||||||
|
return new MetaWeblogResource(this, name, contentType, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
String saveResource(final MetaWeblogResource resource) throws BlogClientException {
|
||||||
|
final Blog.Collection col = collections.get("resources");
|
||||||
|
return col.saveResource(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlogResource getResource(final String token) throws BlogClientException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public NoOpIterator<BlogEntry> getResources() throws BlogClientException {
|
||||||
|
return new NoOpIterator<BlogEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteResource(final BlogResource resource) throws BlogClientException {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Category> getCategories() throws BlogClientException {
|
||||||
|
|
||||||
|
final ArrayList<Category> ret = new ArrayList<Category>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
final Object result = getXmlRpcClient().execute("metaWeblog.getCategories", new Object[] { blogid, userName, password });
|
||||||
|
|
||||||
|
if (result != null && result instanceof HashMap) {
|
||||||
|
|
||||||
|
// Standard MetaWeblog API style: struct of struts
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> catsmap = (Map<String, Object>) result;
|
||||||
|
|
||||||
|
final Set<String> keys = catsmap.keySet();
|
||||||
|
for (final String key : keys) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> catmap = (Map<String, Object>) catsmap.get(key);
|
||||||
|
final BlogEntry.Category category = new BlogEntry.Category(key);
|
||||||
|
final String description = (String) catmap.get("description");
|
||||||
|
category.setName(description);
|
||||||
|
// catmap.get("htmlUrl");
|
||||||
|
// catmap.get("rssUrl");
|
||||||
|
ret.add(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (result != null && result instanceof Object[]) {
|
||||||
|
// Wordpress style: array of structs
|
||||||
|
final Object[] array = (Object[]) result;
|
||||||
|
for (final Object map : array) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> catmap = (Map<String, Object>) map;
|
||||||
|
final String categoryId = (String) catmap.get("categoryId");
|
||||||
|
final String categoryName = (String) catmap.get("categoryName");
|
||||||
|
final BlogEntry.Category category = new BlogEntry.Category(categoryId);
|
||||||
|
category.setName(categoryName);
|
||||||
|
ret.add(category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> createPostStructure(final BlogEntry entry) {
|
||||||
|
return ((MetaWeblogEntry) entry).toPostStructure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Collection> getCollections() throws BlogClientException {
|
||||||
|
return new ArrayList<Collection>(collections.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Blog.Collection getCollection(final String token) throws BlogClientException {
|
||||||
|
return collections.get(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** MetaWeblog API impplementation of Blog.Collection */
|
||||||
|
public class MetaWeblogBlogCollection implements Blog.Collection {
|
||||||
|
private String accept = null;
|
||||||
|
private String title = null;
|
||||||
|
private String token = null;
|
||||||
|
private Blog blog = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param token Identifier for collection, unique within blog
|
||||||
|
* @param title Title of collection
|
||||||
|
* @param accept Content types accepted, either "entry" or "*"
|
||||||
|
*/
|
||||||
|
public MetaWeblogBlogCollection(final Blog blog, final String token, final String title, final String accept) {
|
||||||
|
this.blog = blog;
|
||||||
|
this.accept = accept;
|
||||||
|
this.title = title;
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getAccepts() {
|
||||||
|
return Collections.singletonList(accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogResource newResource(final String name, final String contentType, final byte[] bytes) throws BlogClientException {
|
||||||
|
return blog.newResource(name, contentType, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry newEntry() throws BlogClientException {
|
||||||
|
return blog.newEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean accepts(final String ct) {
|
||||||
|
if (accept.equals("*")) {
|
||||||
|
// everything accepted
|
||||||
|
return true;
|
||||||
|
} else if (accept.equals("entry") && ct.equals("application/metaweblog+xml")) {
|
||||||
|
// entries only accepted and "application/metaweblog+xml" means entry
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<BlogEntry> getEntries() throws BlogClientException {
|
||||||
|
Iterator<BlogEntry> ret = null;
|
||||||
|
if (accept.equals("entry")) {
|
||||||
|
ret = MetaWeblogBlog.this.getEntries();
|
||||||
|
} else {
|
||||||
|
ret = getResources();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String saveEntry(final BlogEntry entry) throws BlogClientException {
|
||||||
|
String ret = entry.getId();
|
||||||
|
if (entry.getId() == null) {
|
||||||
|
try {
|
||||||
|
ret = (String) getXmlRpcClient().execute("metaWeblog.newPost",
|
||||||
|
new Object[] { blogid, userName, password, createPostStructure(entry), new Boolean(!entry.getDraft()) });
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: XML-RPC error saving new entry", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
getXmlRpcClient().execute("metaWeblog.editPost",
|
||||||
|
new Object[] { entry.getId(), userName, password, createPostStructure(entry), new Boolean(!entry.getDraft()) });
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: XML-RPC error updating entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String saveResource(final BlogResource res) throws BlogClientException {
|
||||||
|
final MetaWeblogResource resource = (MetaWeblogResource) res;
|
||||||
|
try {
|
||||||
|
final HashMap<String, Object> resmap = new HashMap<String, Object>();
|
||||||
|
resmap.put("name", resource.getName());
|
||||||
|
resmap.put("type", resource.getContent().getType());
|
||||||
|
resmap.put("bits", resource.getBytes());
|
||||||
|
final Object[] params = new Object[] { blogid, userName, password, resmap };
|
||||||
|
final Object response = getXmlRpcClient().execute("metaWeblog.newMediaObject", params);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> result = (Map<String, Object>) response;
|
||||||
|
final String url = (String) result.get("url");
|
||||||
|
res.getContent().setSrc(url);
|
||||||
|
return url;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: loading or uploading file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Category> getCategories() throws BlogClientException {
|
||||||
|
return MetaWeblogBlog.this.getCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Blog getBlog() {
|
||||||
|
return blog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Iterates over MetaWeblog API entries.
|
||||||
|
*/
|
||||||
|
public class EntryIterator implements Iterator<BlogEntry> {
|
||||||
|
|
||||||
|
private int pos = 0;
|
||||||
|
private boolean eod = false;
|
||||||
|
private static final int BUFSIZE = 30;
|
||||||
|
private List<Map<String, Object>> results = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator for looping over MetaWeblog API entries.
|
||||||
|
*/
|
||||||
|
public EntryIterator() throws BlogClientException {
|
||||||
|
getNextEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if more entries are avialable.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (pos == results.size() && !eod) {
|
||||||
|
try {
|
||||||
|
getNextEntries();
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos < results.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get next entry.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlogEntry next() {
|
||||||
|
return new MetaWeblogEntry(MetaWeblogBlog.this, results.get(pos++));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove is not implemented.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getNextEntries() throws BlogClientException {
|
||||||
|
final int requestSize = pos + BUFSIZE;
|
||||||
|
try {
|
||||||
|
final Object[] params = new Object[] { blogid, userName, password, new Integer(requestSize) };
|
||||||
|
final Object response = getXmlRpcClient().execute("metaWeblog.getRecentPosts", params);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object>[] resultsArray = (Map<String, Object>[]) response;
|
||||||
|
results = Arrays.asList(resultsArray);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: XML-RPC error getting entry", e);
|
||||||
|
}
|
||||||
|
if (results.size() < requestSize) {
|
||||||
|
eod = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.metaweblog;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
|
||||||
|
import com.rometools.propono.blogclient.Blog;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BlogClient implementation that uses a mix of Blogger and MetaWeblog API methods.
|
||||||
|
*/
|
||||||
|
public class MetaWeblogConnection implements BlogConnection {
|
||||||
|
|
||||||
|
private URL url = null;
|
||||||
|
private String userName = null;
|
||||||
|
private String password = null;
|
||||||
|
private String appkey = "null";
|
||||||
|
private Map<String, MetaWeblogBlog> blogs = null;
|
||||||
|
|
||||||
|
private XmlRpcClient xmlRpcClient = null;
|
||||||
|
|
||||||
|
public MetaWeblogConnection(final String url, final String userName, final String password) throws BlogClientException {
|
||||||
|
this.userName = userName;
|
||||||
|
this.password = password;
|
||||||
|
try {
|
||||||
|
this.url = new URL(url);
|
||||||
|
blogs = createBlogMap();
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
throw new BlogClientException("ERROR connecting to server", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmlRpcClient getXmlRpcClient() {
|
||||||
|
if (xmlRpcClient == null) {
|
||||||
|
final XmlRpcClientConfigImpl xmlrpcConfig = new XmlRpcClientConfigImpl();
|
||||||
|
xmlrpcConfig.setServerURL(url);
|
||||||
|
xmlRpcClient = new XmlRpcClient();
|
||||||
|
xmlRpcClient.setConfig(xmlrpcConfig);
|
||||||
|
}
|
||||||
|
return xmlRpcClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Blog> getBlogs() {
|
||||||
|
return new ArrayList<Blog>(blogs.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
private Map<String, MetaWeblogBlog> createBlogMap() throws XmlRpcException, IOException {
|
||||||
|
final Map<String, MetaWeblogBlog> blogMap = new HashMap<String, MetaWeblogBlog>();
|
||||||
|
final Object[] results = (Object[]) getXmlRpcClient().execute("blogger.getUsersBlogs", new Object[] { appkey, userName, password });
|
||||||
|
for (final Object result : results) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> blog = (Map<String, Object>) result;
|
||||||
|
final String blogid = (String) blog.get("blogid");
|
||||||
|
final String name = (String) blog.get("blogName");
|
||||||
|
blogMap.put(blogid, new MetaWeblogBlog(blogid, name, url, userName, password));
|
||||||
|
}
|
||||||
|
return blogMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Blog getBlog(final String token) {
|
||||||
|
return blogs.get(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setAppkey(final String appkey) {
|
||||||
|
this.appkey = appkey;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.metaweblog;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.rometools.propono.blogclient.BaseBlogEntry;
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaWeblog API implementation of an entry.
|
||||||
|
*/
|
||||||
|
public class MetaWeblogEntry extends BaseBlogEntry {
|
||||||
|
|
||||||
|
MetaWeblogEntry(final MetaWeblogBlog blog, final Map<String, Object> entryMap) {
|
||||||
|
super(blog);
|
||||||
|
id = (String) entryMap.get("postid");
|
||||||
|
|
||||||
|
content = new Content((String) entryMap.get("description"));
|
||||||
|
|
||||||
|
// let's pretend MetaWeblog API has a content-type
|
||||||
|
content.setType("application/metaweblog+xml");
|
||||||
|
|
||||||
|
// no way to tell if entry is draft or not
|
||||||
|
draft = false;
|
||||||
|
|
||||||
|
title = (String) entryMap.get("title");
|
||||||
|
publicationDate = (Date) entryMap.get("dateCreated");
|
||||||
|
permalink = (String) entryMap.get("permaLink");
|
||||||
|
|
||||||
|
// AlexisMP: fix to get the author value populated.
|
||||||
|
author.setName((String) entryMap.get("userid"));
|
||||||
|
author.setEmail((String) entryMap.get("author"));
|
||||||
|
|
||||||
|
categories = new ArrayList<Category>();
|
||||||
|
final Object[] catArray = (Object[]) entryMap.get("categories");
|
||||||
|
if (catArray != null) {
|
||||||
|
for (final Object element : catArray) {
|
||||||
|
final Category cat = new Category((String) element);
|
||||||
|
categories.add(cat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getToken() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if tokens are equal
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (o instanceof MetaWeblogEntry) {
|
||||||
|
final MetaWeblogEntry other = (MetaWeblogEntry) o;
|
||||||
|
if (other.id != null && id != null) {
|
||||||
|
return other.id.equals(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void save() throws BlogClientException {
|
||||||
|
id = ((MetaWeblogBlog) getBlog()).saveEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void delete() throws BlogClientException {
|
||||||
|
((MetaWeblogBlog) getBlog()).deleteEntry(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> toPostStructure() {
|
||||||
|
final Map<String, Object> struct = new HashMap<String, Object>();
|
||||||
|
if (getTitle() != null) {
|
||||||
|
struct.put("title", getTitle());
|
||||||
|
}
|
||||||
|
if (getContent() != null && getContent().getValue() != null) {
|
||||||
|
struct.put("description", getContent().getValue());
|
||||||
|
}
|
||||||
|
if (getCategories() != null && !getCategories().isEmpty()) {
|
||||||
|
final List<String> catArray = new ArrayList<String>();
|
||||||
|
final List<Category> cats = getCategories();
|
||||||
|
for (int i = 0; i < cats.size(); i++) {
|
||||||
|
final BlogEntry.Category cat = cats.get(i);
|
||||||
|
catArray.add(cat.getName());
|
||||||
|
}
|
||||||
|
struct.put("categories", catArray);
|
||||||
|
}
|
||||||
|
if (getPublicationDate() != null) {
|
||||||
|
struct.put("dateCreated", getPublicationDate());
|
||||||
|
}
|
||||||
|
if (getId() != null) {
|
||||||
|
struct.put("postid", getId());
|
||||||
|
}
|
||||||
|
return struct;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.blogclient.metaweblog;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
|
||||||
|
import com.rometools.propono.blogclient.BlogClientException;
|
||||||
|
import com.rometools.propono.blogclient.BlogResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaWeblog API implementation of an resource entry.
|
||||||
|
*/
|
||||||
|
public class MetaWeblogResource extends MetaWeblogEntry implements BlogResource {
|
||||||
|
private final MetaWeblogBlog blog;
|
||||||
|
private final String name;
|
||||||
|
private final String contentType;
|
||||||
|
private byte[] bytes;
|
||||||
|
|
||||||
|
MetaWeblogResource(final MetaWeblogBlog blog, final String name, final String contentType, final byte[] bytes) {
|
||||||
|
super(blog, new HashMap<String, Object>());
|
||||||
|
this.blog = blog;
|
||||||
|
this.name = name;
|
||||||
|
this.contentType = contentType;
|
||||||
|
this.bytes = bytes;
|
||||||
|
content = new Content();
|
||||||
|
content.setType(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getToken() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get content-type of associated media resource.
|
||||||
|
*/
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get media resource as input stream.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public InputStream getAsStream() throws BlogClientException {
|
||||||
|
final HttpClient httpClient = new HttpClient();
|
||||||
|
final GetMethod method = new GetMethod(permalink);
|
||||||
|
try {
|
||||||
|
httpClient.executeMethod(method);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: error reading file", e);
|
||||||
|
}
|
||||||
|
if (method.getStatusCode() != 200) {
|
||||||
|
throw new BlogClientException("ERROR HTTP status=" + method.getStatusCode());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return method.getResponseBodyAsStream();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new BlogClientException("ERROR: error reading file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void save() throws BlogClientException {
|
||||||
|
blog.saveResource(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void update(final byte[] bytes) throws BlogClientException {
|
||||||
|
this.bytes = bytes;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource data as byte array.
|
||||||
|
*/
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not supported by MetaWeblog API
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void delete() throws BlogClientException {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.rometools.propono.blogclient.metaweblog;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
class NoOpIterator<T> implements Iterator<T> {
|
||||||
|
|
||||||
|
/** No-op */
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** No-op */
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** No-op */
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
154
src/main/java/com/rometools/propono/utils/ProponoException.java
Normal file
154
src/main/java/com/rometools/propono/utils/ProponoException.java
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.utils;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Propono exception class.
|
||||||
|
*/
|
||||||
|
public class ProponoException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private Throwable mRootCause = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct emtpy exception object.
|
||||||
|
*/
|
||||||
|
public ProponoException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct ProponoException with message string.
|
||||||
|
*
|
||||||
|
* @param s Error message string.
|
||||||
|
*/
|
||||||
|
public ProponoException(final String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct ProponoException with message string.
|
||||||
|
*
|
||||||
|
* @param s Error message string.
|
||||||
|
*/
|
||||||
|
public ProponoException(final String s, final String longMessage) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct ProponoException, wrapping existing throwable.
|
||||||
|
*
|
||||||
|
* @param s Error message
|
||||||
|
* @param t Existing connection to wrap.
|
||||||
|
*/
|
||||||
|
public ProponoException(final String s, final Throwable t) {
|
||||||
|
super(s);
|
||||||
|
mRootCause = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct ProponoException, wrapping existing throwable.
|
||||||
|
*
|
||||||
|
* @param s Error message
|
||||||
|
* @param t Existing connection to wrap.
|
||||||
|
*/
|
||||||
|
public ProponoException(final String s, final String longMessage, final Throwable t) {
|
||||||
|
super(s);
|
||||||
|
mRootCause = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct ProponoException, wrapping existing throwable.
|
||||||
|
*
|
||||||
|
* @param t Existing exception to be wrapped.
|
||||||
|
*/
|
||||||
|
public ProponoException(final Throwable t) {
|
||||||
|
mRootCause = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get root cause object, or null if none.
|
||||||
|
*
|
||||||
|
* @return Root cause or null if none.
|
||||||
|
*/
|
||||||
|
public Throwable getRootCause() {
|
||||||
|
return mRootCause;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get root cause message.
|
||||||
|
*
|
||||||
|
* @return Root cause message.
|
||||||
|
*/
|
||||||
|
public String getRootCauseMessage() {
|
||||||
|
String rcmessage = null;
|
||||||
|
if (getRootCause() != null) {
|
||||||
|
if (getRootCause().getCause() != null) {
|
||||||
|
rcmessage = getRootCause().getCause().getMessage();
|
||||||
|
}
|
||||||
|
rcmessage = rcmessage == null ? getRootCause().getMessage() : rcmessage;
|
||||||
|
rcmessage = rcmessage == null ? super.getMessage() : rcmessage;
|
||||||
|
rcmessage = rcmessage == null ? "NONE" : rcmessage;
|
||||||
|
}
|
||||||
|
return rcmessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print stack trace for exception and for root cause exception if htere is one.
|
||||||
|
*
|
||||||
|
* @see java.lang.Throwable#printStackTrace()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void printStackTrace() {
|
||||||
|
super.printStackTrace();
|
||||||
|
if (mRootCause != null) {
|
||||||
|
System.out.println("--- ROOT CAUSE ---");
|
||||||
|
mRootCause.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print stack trace for exception and for root cause exception if htere is one.
|
||||||
|
*
|
||||||
|
* @param s Stream to print to.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void printStackTrace(final PrintStream s) {
|
||||||
|
super.printStackTrace(s);
|
||||||
|
if (mRootCause != null) {
|
||||||
|
s.println("--- ROOT CAUSE ---");
|
||||||
|
mRootCause.printStackTrace(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print stack trace for exception and for root cause exception if htere is one.
|
||||||
|
*
|
||||||
|
* @param s Writer to write to.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void printStackTrace(final PrintWriter s) {
|
||||||
|
super.printStackTrace(s);
|
||||||
|
if (null != mRootCause) {
|
||||||
|
s.println("--- ROOT CAUSE ---");
|
||||||
|
mRootCause.printStackTrace(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
270
src/main/java/com/rometools/propono/utils/Utilities.java
Normal file
270
src/main/java/com/rometools/propono/utils/Utilities.java
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Dave Johnson (Blogapps project)
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.utils;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for file I/O and string manipulation.
|
||||||
|
*/
|
||||||
|
public final class Utilities {
|
||||||
|
|
||||||
|
private static final String LS = System.getProperty("line.separator");
|
||||||
|
|
||||||
|
private Utilities() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contents of the file in a byte array (from JavaAlmanac).
|
||||||
|
*/
|
||||||
|
public static byte[] getBytesFromFile(final File file) throws IOException {
|
||||||
|
|
||||||
|
final InputStream is = new FileInputStream(file);
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Get the size of the file
|
||||||
|
final long length = file.length();
|
||||||
|
|
||||||
|
// You cannot create an array using a long type.
|
||||||
|
// It needs to be an int type.
|
||||||
|
// Before converting to an int type, check
|
||||||
|
// to ensure that file is not larger than Integer.MAX_VALUE.
|
||||||
|
if (length > Integer.MAX_VALUE) {
|
||||||
|
// File is too large
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the byte array to hold the data
|
||||||
|
final byte[] bytes = new byte[(int) length];
|
||||||
|
|
||||||
|
// Read in the bytes
|
||||||
|
int offset = 0;
|
||||||
|
int numRead = 0;
|
||||||
|
while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
|
||||||
|
offset += numRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all the bytes have been read in
|
||||||
|
if (offset < bytes.length) {
|
||||||
|
throw new IOException("Could not completely read file " + file.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read input from stream and into string.
|
||||||
|
*/
|
||||||
|
public static String streamToString(final InputStream is) throws IOException {
|
||||||
|
final StringBuffer sb = new StringBuffer();
|
||||||
|
final BufferedReader in = new BufferedReader(new InputStreamReader(is));
|
||||||
|
String line;
|
||||||
|
while ((line = in.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
sb.append(LS);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy input stream to output stream using 8K buffer.
|
||||||
|
*/
|
||||||
|
public static void copyInputToOutput(final InputStream input, final OutputStream output) throws IOException {
|
||||||
|
final BufferedInputStream in = new BufferedInputStream(input);
|
||||||
|
final BufferedOutputStream out = new BufferedOutputStream(output);
|
||||||
|
final byte buffer[] = new byte[8192];
|
||||||
|
for (int count = 0; count != -1;) {
|
||||||
|
count = in.read(buffer, 0, 8192);
|
||||||
|
if (count != -1) {
|
||||||
|
out.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
out.close();
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new IOException("Closing file streams, " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces occurences of non-alphanumeric characters with a supplied char.
|
||||||
|
*/
|
||||||
|
public static String replaceNonAlphanumeric(final String str, final char subst) {
|
||||||
|
final StringBuffer ret = new StringBuffer(str.length());
|
||||||
|
final char[] testChars = str.toCharArray();
|
||||||
|
for (final char testChar : testChars) {
|
||||||
|
if (Character.isLetterOrDigit(testChar)) {
|
||||||
|
ret.append(testChar);
|
||||||
|
} else {
|
||||||
|
ret.append(subst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert string to string array.
|
||||||
|
*/
|
||||||
|
public static String[] stringToStringArray(final String instr, final String delim) throws NoSuchElementException, NumberFormatException {
|
||||||
|
final StringTokenizer toker = new StringTokenizer(instr, delim);
|
||||||
|
final String stringArray[] = new String[toker.countTokens()];
|
||||||
|
int i = 0;
|
||||||
|
while (toker.hasMoreTokens()) {
|
||||||
|
stringArray[i++] = toker.nextToken();
|
||||||
|
}
|
||||||
|
return stringArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert string array to string.
|
||||||
|
*/
|
||||||
|
public static String stringArrayToString(final String[] stringArray, final String delim) {
|
||||||
|
String ret = "";
|
||||||
|
for (final String element : stringArray) {
|
||||||
|
if (ret.length() > 0) {
|
||||||
|
ret = ret + delim + element;
|
||||||
|
} else {
|
||||||
|
ret = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Pattern absoluteURIPattern = Pattern.compile("^[a-z0-9]*:.*$");
|
||||||
|
|
||||||
|
// private static boolean isAbsoluteURI(final String uri) {
|
||||||
|
// return absoluteURIPattern.matcher(uri).find();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private static boolean isRelativeURI(final String uri) {
|
||||||
|
// return !isAbsoluteURI(uri);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * } Resolve URI based considering xml:base and baseURI.
|
||||||
|
// *
|
||||||
|
// * @param baseURI Base URI of feed
|
||||||
|
// * @param parent Parent from which to consider xml:base
|
||||||
|
// * @param url URL to be resolved
|
||||||
|
// */
|
||||||
|
// private static String resolveURI(final String baseURI, final Parent parent, String url) {
|
||||||
|
// if (isRelativeURI(url)) {
|
||||||
|
// url = !".".equals(url) && !"./".equals(url) ? url : "";
|
||||||
|
//
|
||||||
|
// // Relative URI with parent
|
||||||
|
// if (parent != null && parent instanceof Element) {
|
||||||
|
//
|
||||||
|
// // Do we have an xml:base?
|
||||||
|
// String xmlbase = ((Element) parent).getAttributeValue("base", Namespace.XML_NAMESPACE);
|
||||||
|
// if (xmlbase != null && xmlbase.trim().length() > 0) {
|
||||||
|
// if (isAbsoluteURI(xmlbase)) {
|
||||||
|
// // Absolute xml:base, so form URI right now
|
||||||
|
// if (url.startsWith("/")) {
|
||||||
|
// // Host relative URI
|
||||||
|
// final int slashslash = xmlbase.indexOf("//");
|
||||||
|
// final int nextslash = xmlbase.indexOf("/", slashslash + 2);
|
||||||
|
// if (nextslash != -1) {
|
||||||
|
// xmlbase = xmlbase.substring(0, nextslash);
|
||||||
|
// }
|
||||||
|
// return formURI(xmlbase, url);
|
||||||
|
// }
|
||||||
|
// if (!xmlbase.endsWith("/")) {
|
||||||
|
// // Base URI is filename, strip it off
|
||||||
|
// xmlbase = xmlbase.substring(0, xmlbase.lastIndexOf("/"));
|
||||||
|
// }
|
||||||
|
// return formURI(xmlbase, url);
|
||||||
|
// } else {
|
||||||
|
// // Relative xml:base, so walk up tree
|
||||||
|
// return resolveURI(baseURI, parent.getParent(), stripTrailingSlash(xmlbase) + "/" +
|
||||||
|
// stripStartingSlash(url));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // No xml:base so walk up tree
|
||||||
|
// return resolveURI(baseURI, parent.getParent(), url);
|
||||||
|
//
|
||||||
|
// // Relative URI with no parent (i.e. top of tree), so form URI right now
|
||||||
|
// } else if (parent == null || parent instanceof Document) {
|
||||||
|
// return formURI(baseURI, url);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return url;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Form URI by combining base with append portion and giving special consideration to append
|
||||||
|
// * portions that begin with ".."
|
||||||
|
// *
|
||||||
|
// * @param base Base of URI, may end with trailing slash
|
||||||
|
// * @param append String to append, may begin with slash or ".."
|
||||||
|
// */
|
||||||
|
// private static String formURI(String base, String append) {
|
||||||
|
// base = stripTrailingSlash(base);
|
||||||
|
// append = stripStartingSlash(append);
|
||||||
|
// if (append.startsWith("..")) {
|
||||||
|
// final String ret = null;
|
||||||
|
// final String[] parts = append.split("/");
|
||||||
|
// for (final String part : parts) {
|
||||||
|
// if ("..".equals(part)) {
|
||||||
|
// final int last = base.lastIndexOf("/");
|
||||||
|
// if (last != -1) {
|
||||||
|
// base = base.substring(0, last);
|
||||||
|
// append = append.substring(3, append.length());
|
||||||
|
// } else {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return base + "/" + append;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Strip starting slash from beginning of string.
|
||||||
|
// */
|
||||||
|
// private static String stripStartingSlash(String s) {
|
||||||
|
// if (s != null && s.startsWith("/")) {
|
||||||
|
// s = s.substring(1, s.length());
|
||||||
|
// }
|
||||||
|
// return s;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Strip trailing slash from end of string.
|
||||||
|
// */
|
||||||
|
// private static String stripTrailingSlash(String s) {
|
||||||
|
// if (s != null && s.endsWith("/")) {
|
||||||
|
// s = s.substring(0, s.length() - 1);
|
||||||
|
// }
|
||||||
|
// return s;
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
1
src/main/resources/propono-version.properties
Normal file
1
src/main/resources/propono-version.properties
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rome.propono.version=2.0.0
|
7
src/main/resources/rome.properties
Normal file
7
src/main/resources/rome.properties
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Configures Propono APP extension module
|
||||||
|
|
||||||
|
atom_1.0.item.ModuleParser.classes=\
|
||||||
|
com.rometools.propono.atom.common.rome.AppModuleParser
|
||||||
|
|
||||||
|
atom_1.0.item.ModuleGenerator.classes=\
|
||||||
|
com.rometools.propono.atom.common.rome.AppModuleGenerator
|
29
src/site/apt/ROMEProponoVersion0.4.apt
Normal file
29
src/site/apt/ROMEProponoVersion0.4.apt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
-----
|
||||||
|
ROME Propono Version 0.4
|
||||||
|
-----
|
||||||
|
mkurz
|
||||||
|
-----
|
||||||
|
2011-08-16 04:52:04.427
|
||||||
|
-----
|
||||||
|
|
||||||
|
ROME Propono Version 0.4
|
||||||
|
|
||||||
|
This is the first release of the {{{./index.html}Rome Propono}} publishing library. It's a beta release and will be followed closely by Propono 0.5.
|
||||||
|
|
||||||
|
*Downloads
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.4\-src.zip}rome\-propono\-0.4\-src.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.4.tar.gz}rome\-propono\-0.4.tar.gz}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.4.zip}rome\-propono\-0.4.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.4\-src.tar.gz}rome\-propono\-0.4\-src.tar.gz}}
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
*API Docs
|
||||||
|
|
||||||
|
* {{{https://rome.dev.java.net/apidocs/subprojects/propono/0.4/overview\-summary.html}Propono 0.4 API Docs}}
|
||||||
|
|
||||||
|
[]
|
39
src/site/apt/ROMEProponoVersion0.5.apt
Normal file
39
src/site/apt/ROMEProponoVersion0.5.apt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
-----
|
||||||
|
ROME Propono Version 0.5
|
||||||
|
-----
|
||||||
|
mkurz
|
||||||
|
-----
|
||||||
|
2011-08-16 04:50:16.111
|
||||||
|
-----
|
||||||
|
|
||||||
|
ROME Propono Version 0.5
|
||||||
|
|
||||||
|
This is the second release of the {{{./index.html}Rome Propono}} publishing library. It's a bug fix release follow\-on to 0.4. Here are the changes:
|
||||||
|
|
||||||
|
* Fixes in Blog Client constructors
|
||||||
|
|
||||||
|
* AtomServlet uses application/atomsvc\+xml for the Service Document
|
||||||
|
|
||||||
|
* Fixed issue #66: don't expect entry to be returned from update
|
||||||
|
|
||||||
|
* Made example builds more configurable
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
*Downloads
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.5\-src.zip}rome\-propono\-0.5\-src.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.5.tar.gz}rome\-propono\-0.5.tar.gz}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.5.zip}rome\-propono\-0.5.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.5\-src.tar.gz}rome\-propono\-0.5\-src.tar.gz}}
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
*API Docs
|
||||||
|
|
||||||
|
* {{{https://rome.dev.java.net/apidocs/subprojects/propono/0.5/overview\-summary.html}Propono 0.5 API Docs}}
|
||||||
|
|
||||||
|
[]
|
85
src/site/apt/ROMEProponoVersion0.6.apt
Normal file
85
src/site/apt/ROMEProponoVersion0.6.apt
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
-----
|
||||||
|
ROME Propono Version 0.6
|
||||||
|
-----
|
||||||
|
mkurz
|
||||||
|
-----
|
||||||
|
2011-08-16 04:48:12.766
|
||||||
|
-----
|
||||||
|
|
||||||
|
ROME Propono Version 0.6
|
||||||
|
|
||||||
|
September 30, 2007
|
||||||
|
|
||||||
|
This is the third release of the {{{./index.html}Rome Propono}} publishing library. It includes major changes to add support for the final
|
||||||
|
Atom Publishing Protocol specification relative URIs and out\-of\-line categories. It's an an interim release it includes a pre\-release
|
||||||
|
version of ROME 0.9.1\-dev. A new version will follow as soon as ROME 0.9.1 (or 1.0) has been finalized.
|
||||||
|
|
||||||
|
*Downloads
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.6\-src.zip}rome\-propono\-0.6\-src.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.6.tar.gz}rome\-propono\-0.6.tar.gz}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.6.zip}rome\-propono\-0.6.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-0.6\-src.tar.gz}rome\-propono\-0.6\-src.tar.gz}}
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
*API Docs
|
||||||
|
|
||||||
|
* {{{https://rome.dev.java.net/apidocs/subprojects/propono/0.6/overview\-summary.html}Propono 0.6 API Docs}}
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
*Changes
|
||||||
|
|
||||||
|
* Updated for APP final (draft #17) w/new APP URI "http://www.w3.org/2007/app"
|
||||||
|
|
||||||
|
* Tested file\-based server against Tim Bray's Ape (from CVS September 30, 2007).
|
||||||
|
|
||||||
|
* Now includes pre\-release of ROME 0.9.1 with key Atom parse fixes.
|
||||||
|
|
||||||
|
* Changed arguements in Atom server's AtomHandler interface to accept AtomRequest objects instead of String\[\] pathinfo arrays.
|
||||||
|
|
||||||
|
* Added support for relative URIs in the Service Document
|
||||||
|
|
||||||
|
* Fixes {{{http://java.net/jira/browse/ROME\-67}http://java.net/jira/browse/ROME\-67}}
|
||||||
|
|
||||||
|
* Added Collection.getHrefResolved()
|
||||||
|
|
||||||
|
* Added Categories.getHrefResolved()
|
||||||
|
|
||||||
|
* Added new options to the file\-based server's propono.properties file so you can turn on/off relative URIs and inline categories.
|
||||||
|
|
||||||
|
* propono.atomserver.filebased.relativeURIs\=true
|
||||||
|
|
||||||
|
* propono.atomserver.filebased.inlineCategories\=true
|
||||||
|
|
||||||
|
* Added support for out\-of\-line categories in Atom client classes
|
||||||
|
|
||||||
|
* Added new Categories.href property
|
||||||
|
|
||||||
|
* New ClientCategories classes can fetch remote categories from href URI
|
||||||
|
|
||||||
|
* Fixes {{{http://java.net/jira/browse/ROME\-68}http://java.net/jira/browse/ROME\-68}}
|
||||||
|
|
||||||
|
* Added support for out\-of\-line categories in Atom server classes
|
||||||
|
|
||||||
|
* New AtomHandler.getCategoriesDocument(String\[\] pathInfo) method
|
||||||
|
|
||||||
|
* New AtomHandler.isCategoriesDocumentURI(String\[\] pathInfo) method
|
||||||
|
|
||||||
|
* Renamed Introspection to Service Document
|
||||||
|
|
||||||
|
* AtomHandler.isIntrospectionURI() \-\> AtomHandler.isSerivceDocumentURI()
|
||||||
|
|
||||||
|
* AtomHandler.getIntrospection() \-\> AtomHandler.getServiceDocument()
|
||||||
|
|
||||||
|
* Added String\[\] pathInfo argument to getServiceDocument()
|
||||||
|
|
||||||
|
* Renamed PubControlModule to AppModule becuase it also supports app:edited
|
||||||
|
|
||||||
|
* Added rome.properties file to configure AppModule
|
||||||
|
|
||||||
|
[]
|
41
src/site/apt/ROMEProponoVersion1.0.apt
Normal file
41
src/site/apt/ROMEProponoVersion1.0.apt
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
-----
|
||||||
|
ROME Propono Version 1.0
|
||||||
|
-----
|
||||||
|
mkurz
|
||||||
|
-----
|
||||||
|
2011-08-16 04:44:34.302
|
||||||
|
-----
|
||||||
|
|
||||||
|
ROME Propono Version 1.0
|
||||||
|
|
||||||
|
April 2009
|
||||||
|
|
||||||
|
ROME Propono 1.0 is coming soon. If you'd like to help out, you can try 1.0 RC1 and provide feedback to us on the ROME dev mail list.
|
||||||
|
|
||||||
|
*Downloads
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-1.0RC1\-src.zip}rome\-propono\-1.0RC1\-src.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-1.0RC1.tar.gz}rome\-propono\-1.0RC1.tar.gz}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-1.0RC1.zip}rome\-propono\-1.0RC1.zip}}
|
||||||
|
|
||||||
|
* {{{./rome\-propono\-1.0RC1\-src.tar.gz}rome\-propono\-1.0RC1\-src.tar.gz}}
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
*API Docs
|
||||||
|
|
||||||
|
* {{{https://rome.dev.java.net/apidocs/subprojects/propono/1.0/overview\-summary.html}Propono 1.0 API Docs}}
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
*Changes
|
||||||
|
|
||||||
|
* Updated to ROME 1.0
|
||||||
|
|
||||||
|
* Added support for pluggable authentication in the AtomPub client
|
||||||
|
|
||||||
|
* Added support for OAuth in the AtomPub client, see Javadocs for details
|
||||||
|
|
||||||
|
[]
|
31
src/site/apt/index.apt
Normal file
31
src/site/apt/index.apt
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
-----
|
||||||
|
Home
|
||||||
|
-----
|
||||||
|
mkurz
|
||||||
|
-----
|
||||||
|
2011-08-16 04:43:25.062
|
||||||
|
-----
|
||||||
|
|
||||||
|
ROME Propono
|
||||||
|
|
||||||
|
The ROME Propono subproject is a Java class library that supports publishing protocols, specifically the Atom Publishing Protocol and the legacy MetaWeblog API.
|
||||||
|
Propono includes an Atom client library, an Atom server framework and a Blog client that supports both Atom protocol and the MetaWeblog API.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
|
||||||
|
* See the {{{./apidocs/overview\-summary.html}Propono API docs}} for an explanation of Propono usage, diagrams and code examples.
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
||||||
|
Releases
|
||||||
|
|
||||||
|
* {{{./ROMEProponoVersion1.0.html}ROME Propono Version 1.0 (propono)}} \- Release 1.0 coming soon...
|
||||||
|
|
||||||
|
* {{{./ROMEProponoVersion0.6.html}ROME Propono Version 0.6 (propono)}}
|
||||||
|
|
||||||
|
* {{{./ROMEProponoVersion0.5.html}ROME Propono Version 0.5 (propono)}}
|
||||||
|
|
||||||
|
* {{{./ROMEProponoVersion0.4.html}ROME Propono Version 0.4 (propono)}}
|
||||||
|
|
||||||
|
[]
|
||||||
|
|
0
src/site/resources/.nojekyll
Normal file
0
src/site/resources/.nojekyll
Normal file
8
src/site/resources/css/site.css
Normal file
8
src/site/resources/css/site.css
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
h1 {
|
||||||
|
padding: 4px 4px 4px 6px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
color: #900;
|
||||||
|
background-color: #ddd;
|
||||||
|
font-weight:900;
|
||||||
|
font-size: x-large;
|
||||||
|
}
|
BIN
src/site/resources/images/romelogo.png
Normal file
BIN
src/site/resources/images/romelogo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
src/site/resources/rome-propono-0.4-src.tar.gz
Normal file
BIN
src/site/resources/rome-propono-0.4-src.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.4-src.zip
Normal file
BIN
src/site/resources/rome-propono-0.4-src.zip
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.4.tar.gz
Normal file
BIN
src/site/resources/rome-propono-0.4.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.4.zip
Normal file
BIN
src/site/resources/rome-propono-0.4.zip
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.5-src.tar.gz
Normal file
BIN
src/site/resources/rome-propono-0.5-src.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.5-src.zip
Normal file
BIN
src/site/resources/rome-propono-0.5-src.zip
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.5.tar.gz
Normal file
BIN
src/site/resources/rome-propono-0.5.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.5.zip
Normal file
BIN
src/site/resources/rome-propono-0.5.zip
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.6-src.tar.gz
Normal file
BIN
src/site/resources/rome-propono-0.6-src.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.6-src.zip
Normal file
BIN
src/site/resources/rome-propono-0.6-src.zip
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.6.tar.gz
Normal file
BIN
src/site/resources/rome-propono-0.6.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-0.6.zip
Normal file
BIN
src/site/resources/rome-propono-0.6.zip
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-1.0RC1-src.tar.gz
Normal file
BIN
src/site/resources/rome-propono-1.0RC1-src.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-1.0RC1-src.zip
Normal file
BIN
src/site/resources/rome-propono-1.0RC1-src.zip
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-1.0RC1.tar.gz
Normal file
BIN
src/site/resources/rome-propono-1.0RC1.tar.gz
Normal file
Binary file not shown.
BIN
src/site/resources/rome-propono-1.0RC1.zip
Normal file
BIN
src/site/resources/rome-propono-1.0RC1.zip
Normal file
Binary file not shown.
30
src/site/site.xml
Normal file
30
src/site/site.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/DECORATION/1.3.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.3.0 http://maven.apache.org/xsd/decoration-1.3.0.xsd"
|
||||||
|
name="ROME Propono">
|
||||||
|
|
||||||
|
<skin>
|
||||||
|
<groupId>org.apache.maven.skins</groupId>
|
||||||
|
<artifactId>maven-fluido-skin</artifactId>
|
||||||
|
<version>1.3.0</version>
|
||||||
|
</skin>
|
||||||
|
|
||||||
|
<bannerLeft>
|
||||||
|
<name>ROME</name>
|
||||||
|
<src>images/romelogo.png</src>
|
||||||
|
<href>http://github.com/rometools/</href>
|
||||||
|
</bannerLeft>
|
||||||
|
|
||||||
|
<publishDate position="right" />
|
||||||
|
|
||||||
|
<version position="right" />
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<menu name="ROME Propono">
|
||||||
|
<item name="Overview" href="index.html" />
|
||||||
|
</menu>
|
||||||
|
<menu ref="reports" />
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,420 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import junit.framework.Test;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.framework.TestSuite;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.atom.common.Collection;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.feed.atom.Category;
|
||||||
|
import com.rometools.rome.feed.atom.Content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple APP test designed to run against a live Atom server.
|
||||||
|
*/
|
||||||
|
@Ignore
|
||||||
|
public class AtomClientTest extends TestCase {
|
||||||
|
|
||||||
|
private static Logger log = LoggerFactory.getLogger(AtomClientTest.class);
|
||||||
|
|
||||||
|
private static ClientAtomService service = null;
|
||||||
|
|
||||||
|
// Basic Auth example
|
||||||
|
private static String endpoint = "http://localhost:8080/sample-atomserver/app";
|
||||||
|
private static String username = "admin";
|
||||||
|
private static String password = "admin";
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
service = AtomClientFactory.getAtomService(endpoint, new BasicAuthStrategy(username, password));
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("ERROR creating service", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* // Roller OAuth example private static String endpoint =
|
||||||
|
* "http://macsnoopdave:8080/roller-services/app"; private static String consumerKey =
|
||||||
|
* "55132608a2fb68816bcd3d1caeafc933"; private static String consumerSecret =
|
||||||
|
* "bb420783-fdea-4270-ab83-36445c18c307"; private static String requestUri =
|
||||||
|
* "http://macsnoopdave:8080/roller-services/oauth/requestToken"; private static String
|
||||||
|
* authorizeUri =
|
||||||
|
* "http://macsnoopdave:8080/roller-services/oauth/authorize?userId=roller&oauth_callback=none";
|
||||||
|
* private static String accessUri =
|
||||||
|
* "http://macsnoopdave:8080/roller-services/oauth/accessToken"; private static String username
|
||||||
|
* = "roller"; private static String password = "n/a"; static { try { service =
|
||||||
|
* AtomClientFactory.getAtomService(endpoint, new OAuthStrategy( username, consumerKey,
|
||||||
|
* consumerSecret, "HMAC-SHA1", requestUri, authorizeUri, accessUri)); } catch (Exception e) {
|
||||||
|
* log.error("ERROR creating service", e); } }
|
||||||
|
*/
|
||||||
|
|
||||||
|
// GData Blogger API
|
||||||
|
/*
|
||||||
|
* private static String endpoint =
|
||||||
|
* "http://www.blogger.com/feeds/default/blogs?alt=atom-service"; private static String email =
|
||||||
|
* "EMAIL"; private static String password = "PASSWORD"; private static String serviceName =
|
||||||
|
* "blogger"; static { try { service = AtomClientFactory.getAtomService(endpoint, new
|
||||||
|
* GDataAuthStrategy(email, password, serviceName)); } catch (Exception e) {
|
||||||
|
* log.error("ERROR creating service", e); } }
|
||||||
|
*/
|
||||||
|
|
||||||
|
public AtomClientTest(final String testName) {
|
||||||
|
super(testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEndpoint() {
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Test suite() {
|
||||||
|
final TestSuite suite = new TestSuite(AtomClientTest.class);
|
||||||
|
return suite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that server has introspection doc with at least one workspace.
|
||||||
|
*/
|
||||||
|
public void testGetAtomService() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
log.debug("Workspace: " + space.getTitle());
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element2;
|
||||||
|
log.debug(" Collection: " + col.getTitle() + " Accepts: " + col.getAccepts());
|
||||||
|
log.debug(" href: " + col.getHrefResolved());
|
||||||
|
assertNotNull(col.getTitle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that entries can be posted and removed in all collections that accept entries. Fails if
|
||||||
|
* no collections found that accept entries.
|
||||||
|
*/
|
||||||
|
public void testSimpleEntryPostAndRemove() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element2;
|
||||||
|
if (col.accepts(Collection.ENTRY_TYPE)) {
|
||||||
|
|
||||||
|
// we found a collection that accepts entries, so post one
|
||||||
|
final ClientEntry m1 = col.createEntry();
|
||||||
|
m1.setTitle("Test post");
|
||||||
|
final Content c = new Content();
|
||||||
|
c.setValue("This is a test post");
|
||||||
|
c.setType("html");
|
||||||
|
m1.setContent(c);
|
||||||
|
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry m2 = col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m2.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that entries can be posted, updated and removed in all collections that accept entries.
|
||||||
|
* Fails if no collections found that accept entries.
|
||||||
|
*/
|
||||||
|
public void testSimpleEntryPostUpdateAndRemove() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element2;
|
||||||
|
if (col.accepts(Collection.ENTRY_TYPE)) {
|
||||||
|
|
||||||
|
// we found a collection that accepts entries, so post one
|
||||||
|
final ClientEntry m1 = col.createEntry();
|
||||||
|
m1.setTitle(col.getTitle() + ": Test post");
|
||||||
|
final Content c = new Content();
|
||||||
|
c.setValue("This is a test post");
|
||||||
|
c.setType("html");
|
||||||
|
m1.setContent(c);
|
||||||
|
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry m2 = col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
m2.setTitle(col.getTitle() + ": Updated title");
|
||||||
|
m2.update();
|
||||||
|
|
||||||
|
// entry should now be updated on server
|
||||||
|
final ClientEntry m3 = col.getEntry(m1.getEditURI());
|
||||||
|
assertEquals(col.getTitle() + ": Updated title", m3.getTitle());
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m3.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFindWorkspace() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
final ClientWorkspace ws = (ClientWorkspace) service.findWorkspace("adminblog");
|
||||||
|
if (ws != null) {
|
||||||
|
final ClientCollection col = (ClientCollection) ws.findCollection(null, "entry");
|
||||||
|
final ClientEntry entry = col.createEntry();
|
||||||
|
entry.setTitle("NPE on submitting order query");
|
||||||
|
entry.setContent("This is a <b>bad</b> one!", Content.HTML);
|
||||||
|
col.addEntry(entry);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry saved = col.getEntry(entry.getEditURI());
|
||||||
|
assertNotNull(saved);
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
saved.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(saved.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test posting an entry to every available collection with a fixed and an unfixed category if
|
||||||
|
* server support allows, then cleanup.
|
||||||
|
*/
|
||||||
|
public void testEntryPostWithCategories() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element2 : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element2;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element3 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element3;
|
||||||
|
if (col.accepts(Collection.ENTRY_TYPE)) {
|
||||||
|
|
||||||
|
// we found a collection that accepts GIF, so post one
|
||||||
|
final ClientEntry m1 = col.createEntry();
|
||||||
|
m1.setTitle("Test post");
|
||||||
|
final Content c = new Content();
|
||||||
|
c.setValue("This is a test post");
|
||||||
|
c.setType("html");
|
||||||
|
m1.setContent(c);
|
||||||
|
|
||||||
|
// if possible, pick one fixed an un unfixed category
|
||||||
|
Category fixedCat = null;
|
||||||
|
Category unfixedCat = null;
|
||||||
|
final List<Category> entryCats = new ArrayList<Category>();
|
||||||
|
for (int i = 0; i < col.getCategories().size(); i++) {
|
||||||
|
final Categories cats = col.getCategories().get(i);
|
||||||
|
if (cats.isFixed() && fixedCat == null) {
|
||||||
|
final String scheme = cats.getScheme();
|
||||||
|
fixedCat = cats.getCategories().get(0);
|
||||||
|
if (fixedCat.getScheme() == null) {
|
||||||
|
fixedCat.setScheme(scheme);
|
||||||
|
}
|
||||||
|
entryCats.add(fixedCat);
|
||||||
|
} else if (!cats.isFixed() && unfixedCat == null) {
|
||||||
|
final String scheme = cats.getScheme();
|
||||||
|
unfixedCat = new Category();
|
||||||
|
unfixedCat.setScheme(scheme);
|
||||||
|
unfixedCat.setTerm("tagster");
|
||||||
|
entryCats.add(unfixedCat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m1.setCategories(entryCats);
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry m2 = col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
if (fixedCat != null) {
|
||||||
|
// we added a fixed category, let's make sure it's there
|
||||||
|
boolean foundCat = false;
|
||||||
|
for (final Object element : m2.getCategories()) {
|
||||||
|
final Category cat = (Category) element;
|
||||||
|
if (cat.getTerm().equals(fixedCat.getTerm())) {
|
||||||
|
foundCat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundCat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unfixedCat != null) {
|
||||||
|
// we added an unfixed category, let's make sure it's there
|
||||||
|
boolean foundCat = false;
|
||||||
|
for (final Object element : m2.getCategories()) {
|
||||||
|
final Category cat = (Category) element;
|
||||||
|
if (cat.getTerm().equals(unfixedCat.getTerm())) {
|
||||||
|
foundCat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundCat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m2.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post media entry to every media colletion avialable on server, then cleanup.
|
||||||
|
*/
|
||||||
|
public void testMediaPost() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element2;
|
||||||
|
if (col.accepts("image/gif")) {
|
||||||
|
|
||||||
|
// we found a collection that accepts GIF, so post one
|
||||||
|
final ClientMediaEntry m1 = col.createMediaEntry("duke" + count, "duke" + count, "image/gif", new FileInputStream(
|
||||||
|
"test/testdata/duke-wave-shadow.gif"));
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientMediaEntry m2 = (ClientMediaEntry) col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m2.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post X media entries each media collection found, test paging, then cleanup.
|
||||||
|
*
|
||||||
|
* public void testMediaPaging() throws Exception { ClientAtomService service =
|
||||||
|
* getClientAtomService(); assertNotNull(service); assertTrue(service.getWorkspaces().size() >
|
||||||
|
* 0); int count = 0; for (Iterator it = service.getWorkspaces().iterator(); it.hasNext();) {
|
||||||
|
* ClientWorkspace space = (ClientWorkspace) it.next(); assertNotNull(space.getTitle());
|
||||||
|
*
|
||||||
|
* for (Iterator colit = space.getCollections().iterator(); colit.hasNext();) { ClientCollection
|
||||||
|
* col = (ClientCollection) colit.next(); if (col.accepts("image/gif")) {
|
||||||
|
*
|
||||||
|
* // we found a collection that accepts GIF, so post 100 of them List posted = new ArrayList();
|
||||||
|
* for (int i=0; i<maxPagingEntries; i++) { ClientMediaEntry m1 =
|
||||||
|
* col.createMediaEntry("duke"+count, "duke"+count, "image/gif", new
|
||||||
|
* FileInputStream("test/testdata/duke-wave-shadow.gif")); col.addEntry(m1); posted.add(m1); }
|
||||||
|
* int entryCount = 0; for (Iterator iter = col.getEntries(); iter.hasNext();) {
|
||||||
|
* ClientMediaEntry entry = (ClientMediaEntry) iter.next(); entryCount++; } for (Iterator delit
|
||||||
|
* = posted.iterator(); delit.hasNext();) { ClientEntry entry = (ClientEntry) delit.next();
|
||||||
|
* entry.remove(); } assertTrue(entryCount >= maxPagingEntries); count++; break; } } }
|
||||||
|
* assertTrue(count > 0); }
|
||||||
|
*/
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.client;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import junit.framework.Test;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.framework.TestSuite;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.atom.Content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple APP test designed to run against Blogger.com.
|
||||||
|
*/
|
||||||
|
@Ignore
|
||||||
|
public class BloggerDotComTest extends TestCase {
|
||||||
|
|
||||||
|
private final String collectionURI = "http://www.blogger.com/feeds/BLOGID/posts/default";
|
||||||
|
// private final String atomServiceURI =
|
||||||
|
// "http://www.blogger.com/feeds/default/blogs?alt=atom-service";
|
||||||
|
private final String email = "EMAIL";
|
||||||
|
private final String password = "PASSWORD";
|
||||||
|
|
||||||
|
public BloggerDotComTest(final String testName) {
|
||||||
|
super(testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Test suite() {
|
||||||
|
final TestSuite suite = new TestSuite(BloggerDotComTest.class);
|
||||||
|
return suite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that server returns service document containing workspaces containing collections.
|
||||||
|
*/
|
||||||
|
public void testGetEntries() throws Exception {
|
||||||
|
|
||||||
|
// no auth necessary for iterating through entries
|
||||||
|
ClientCollection col = AtomClientFactory.getCollection(collectionURI, new GDataAuthStrategy(email, password, "blogger"));
|
||||||
|
assertNotNull(col);
|
||||||
|
int count = 0;
|
||||||
|
for (final Iterator<ClientEntry> it = col.getEntries(); it.hasNext();) {
|
||||||
|
final ClientEntry entry = it.next();
|
||||||
|
assertNotNull(entry);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
|
||||||
|
col = AtomClientFactory.getCollection(collectionURI, new GDataAuthStrategy(email, password, "blogger"));
|
||||||
|
final ClientEntry p1 = col.createEntry();
|
||||||
|
p1.setTitle("Propono post");
|
||||||
|
final Content c = new Content();
|
||||||
|
c.setValue("This is content from ROME Propono");
|
||||||
|
p1.setContent(c);
|
||||||
|
col.addEntry(p1);
|
||||||
|
|
||||||
|
final ClientEntry p2 = col.getEntry(p1.getEditURI());
|
||||||
|
assertNotNull(p2);
|
||||||
|
|
||||||
|
final ClientAtomService atomService = AtomClientFactory.getAtomService(collectionURI, new GDataAuthStrategy(email, password, "blogger"));
|
||||||
|
assertNotNull(atomService);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.common;
|
||||||
|
|
||||||
|
import junit.framework.Test;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.framework.TestSuite;
|
||||||
|
|
||||||
|
import org.jdom2.Document;
|
||||||
|
import org.jdom2.Element;
|
||||||
|
import org.jdom2.input.SAXBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests reading and writing of service document, no server needed.
|
||||||
|
*/
|
||||||
|
public class AtomServiceTest extends TestCase {
|
||||||
|
|
||||||
|
public AtomServiceTest(final String testName) {
|
||||||
|
super(testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Test suite() {
|
||||||
|
final TestSuite suite = new TestSuite(AtomServiceTest.class);
|
||||||
|
|
||||||
|
return suite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of documentToService method, of class AtomService.
|
||||||
|
*/
|
||||||
|
public void testDocumentToService() {
|
||||||
|
try {
|
||||||
|
// Load service document from disk
|
||||||
|
final SAXBuilder builder = new SAXBuilder();
|
||||||
|
final Document document = builder.build(this.getClass().getResourceAsStream("/servicedoc1.xml"));
|
||||||
|
assertNotNull(document);
|
||||||
|
final AtomService service = AtomService.documentToService(document);
|
||||||
|
|
||||||
|
int workspaceCount = 0;
|
||||||
|
|
||||||
|
// Verify that service contains expected workspaces, collections and categories
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final Workspace space = (Workspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
workspaceCount++;
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final Collection col = (Collection) element2;
|
||||||
|
assertNotNull(col.getTitle());
|
||||||
|
assertNotNull(col.getHrefResolved());
|
||||||
|
int catCount = 0;
|
||||||
|
if (!col.getCategories().isEmpty()) {
|
||||||
|
for (final Object element3 : col.getCategories()) {
|
||||||
|
final Categories cats = (Categories) element3;
|
||||||
|
catCount += cats.getCategories().size();
|
||||||
|
assertTrue(catCount > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(workspaceCount > 0);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of documentToService method, of class AtomService.
|
||||||
|
*/
|
||||||
|
public void testServiceToDocument() {
|
||||||
|
try {
|
||||||
|
// Create service with workspace and collections
|
||||||
|
final AtomService service = new AtomService();
|
||||||
|
|
||||||
|
final Workspace workspace1 = new Workspace("workspace1", null);
|
||||||
|
final Workspace workspace2 = new Workspace("workspace1", null);
|
||||||
|
service.addWorkspace(workspace1);
|
||||||
|
service.addWorkspace(workspace2);
|
||||||
|
|
||||||
|
final Collection collection11 = new Collection("collection11", null, "http://example.com/app/col11");
|
||||||
|
final Collection collection12 = new Collection("collection12", null, "http://example.com/app/col12");
|
||||||
|
workspace1.addCollection(collection11);
|
||||||
|
workspace1.addCollection(collection12);
|
||||||
|
|
||||||
|
final Collection collection21 = new Collection("collection21", null, "http://example.com/app/col21");
|
||||||
|
final Collection collection22 = new Collection("collection22", null, "http://example.com/app/col22");
|
||||||
|
workspace2.addCollection(collection21);
|
||||||
|
workspace2.addCollection(collection22);
|
||||||
|
|
||||||
|
// TODO: add categories at collection level
|
||||||
|
|
||||||
|
// Convert to JDOM document
|
||||||
|
final Document document = service.serviceToDocument();
|
||||||
|
|
||||||
|
// verify that JDOM document contains service, workspace and collection
|
||||||
|
assertEquals("service", document.getRootElement().getName());
|
||||||
|
int workspaceCount = 0;
|
||||||
|
for (final Object element : document.getRootElement().getChildren()) {
|
||||||
|
final Element elem = (Element) element;
|
||||||
|
if ("workspace".equals(elem.getName())) {
|
||||||
|
workspaceCount++;
|
||||||
|
}
|
||||||
|
boolean workspaceTitle = false;
|
||||||
|
int collectionCount = 0;
|
||||||
|
for (final Object element2 : elem.getChildren()) {
|
||||||
|
final Element colelem = (Element) element2;
|
||||||
|
if ("title".equals(colelem.getName())) {
|
||||||
|
workspaceTitle = true;
|
||||||
|
} else if ("collection".equals(colelem.getName())) {
|
||||||
|
collectionCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test for categories at the collection level
|
||||||
|
}
|
||||||
|
assertTrue(workspaceTitle);
|
||||||
|
assertTrue(collectionCount > 0);
|
||||||
|
}
|
||||||
|
assertTrue(workspaceCount > 0);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.common;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests Collection class, no server needed.
|
||||||
|
*/
|
||||||
|
public class CollectionTest extends TestCase {
|
||||||
|
|
||||||
|
public CollectionTest(final String testName) {
|
||||||
|
super(testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of accepts method, of class com.rometools.rome.propono.atom.common.Collection.
|
||||||
|
*/
|
||||||
|
public void testAccepts() {
|
||||||
|
|
||||||
|
final Collection col = new Collection("dummy_title", "dummy_titletype", "dummy_href");
|
||||||
|
|
||||||
|
col.setAccepts(Collections.singletonList("image/*"));
|
||||||
|
assertTrue(col.accepts("image/gif"));
|
||||||
|
assertTrue(col.accepts("image/jpg"));
|
||||||
|
assertTrue(col.accepts("image/png"));
|
||||||
|
assertFalse(col.accepts("test/html"));
|
||||||
|
|
||||||
|
final List<String> accepts = new ArrayList<String>();
|
||||||
|
accepts.add("image/*");
|
||||||
|
accepts.add("text/*");
|
||||||
|
col.setAccepts(accepts);
|
||||||
|
assertTrue(col.accepts("image/gif"));
|
||||||
|
assertTrue(col.accepts("image/jpg"));
|
||||||
|
assertTrue(col.accepts("image/png"));
|
||||||
|
assertTrue(col.accepts("text/html"));
|
||||||
|
|
||||||
|
col.setAccepts(Collections.singletonList("*/*"));
|
||||||
|
assertTrue(col.accepts("image/gif"));
|
||||||
|
assertTrue(col.accepts("image/jpg"));
|
||||||
|
assertTrue(col.accepts("image/png"));
|
||||||
|
assertTrue(col.accepts("text/html"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,428 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
import static junit.framework.TestCase.assertNotNull;
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mortbay.http.HttpContext;
|
||||||
|
import org.mortbay.http.HttpServer;
|
||||||
|
import org.mortbay.http.SocketListener;
|
||||||
|
import org.mortbay.jetty.servlet.ServletHandler;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.rometools.propono.atom.client.AtomClientFactory;
|
||||||
|
import com.rometools.propono.atom.client.BasicAuthStrategy;
|
||||||
|
import com.rometools.propono.atom.client.ClientAtomService;
|
||||||
|
import com.rometools.propono.atom.client.ClientCollection;
|
||||||
|
import com.rometools.propono.atom.client.ClientEntry;
|
||||||
|
import com.rometools.propono.atom.client.ClientMediaEntry;
|
||||||
|
import com.rometools.propono.atom.client.ClientWorkspace;
|
||||||
|
import com.rometools.propono.atom.common.Categories;
|
||||||
|
import com.rometools.propono.atom.common.Collection;
|
||||||
|
import com.rometools.propono.atom.common.Workspace;
|
||||||
|
import com.rometools.propono.utils.ProponoException;
|
||||||
|
import com.rometools.rome.feed.atom.Category;
|
||||||
|
import com.rometools.rome.feed.atom.Content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Propono Atom Client against Atom Server via Jetty. Extends <code>AtomClientTest</code> to
|
||||||
|
* start Jetty server, run tests and then stop the Jetty server.
|
||||||
|
*/
|
||||||
|
public class AtomClientServerTest {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AtomClientServerTest.class);
|
||||||
|
|
||||||
|
private HttpServer server = null;
|
||||||
|
public static final int TESTPORT = 8283;
|
||||||
|
public static final String ENDPOINT = "http://localhost:" + TESTPORT + "/rome/app";
|
||||||
|
public static final String USERNAME = "admin";
|
||||||
|
public static final String PASSWORD = "admin";
|
||||||
|
|
||||||
|
private static ClientAtomService service = null;
|
||||||
|
|
||||||
|
public String getEndpoint() {
|
||||||
|
return ENDPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return USERNAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return PASSWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpServer getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUpClass() throws Exception {
|
||||||
|
|
||||||
|
LOG.info("---------------------------------------------");
|
||||||
|
LOG.info("Starting Jetty");
|
||||||
|
LOG.info("---------------------------------------------");
|
||||||
|
|
||||||
|
setupServer();
|
||||||
|
final HttpContext context = createContext();
|
||||||
|
final ServletHandler servlets = createServletHandler();
|
||||||
|
context.addHandler(servlets);
|
||||||
|
server.addContext(context);
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
service = AtomClientFactory.getAtomService(getEndpoint(), new BasicAuthStrategy(getUsername(), getPassword()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDownClass() throws Exception {
|
||||||
|
if (server != null) {
|
||||||
|
LOG.info("Stoping Jetty");
|
||||||
|
server.stop();
|
||||||
|
server.destroy();
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupServer() throws InterruptedException {
|
||||||
|
// Create the server
|
||||||
|
if (server != null) {
|
||||||
|
server.stop();
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
server = new HttpServer();
|
||||||
|
|
||||||
|
// Create a port listener
|
||||||
|
final SocketListener listener = new SocketListener();
|
||||||
|
listener.setPort(TESTPORT);
|
||||||
|
server.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServletHandler createServletHandler() {
|
||||||
|
System.setProperty("com.rometools.propono.atom.server.AtomHandlerFactory", "com.rometools.propono.atom.server.TestAtomHandlerFactory");
|
||||||
|
final ServletHandler servlets = new ServletHandler();
|
||||||
|
servlets.addServlet("app", "/app/*", "com.rometools.propono.atom.server.AtomServlet");
|
||||||
|
return servlets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpContext createContext() {
|
||||||
|
final HttpContext context = new HttpContext();
|
||||||
|
context.setContextPath("/rome/*");
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that server has introspection doc with at least one workspace.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetAtomService() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
for (final Workspace workspace : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) workspace;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
LOG.debug("Workspace: {}", space.getTitle());
|
||||||
|
for (final Object element : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element;
|
||||||
|
LOG.debug(" Collection: {} Accepts: {}", col.getTitle(), col.getAccepts());
|
||||||
|
LOG.debug(" href: {}", col.getHrefResolved());
|
||||||
|
assertNotNull(col.getTitle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that entries can be posted and removed in all collections that accept entries. Fails if
|
||||||
|
* no collections found that accept entries.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSimpleEntryPostAndRemove() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element2;
|
||||||
|
if (col.accepts(Collection.ENTRY_TYPE)) {
|
||||||
|
|
||||||
|
// we found a collection that accepts entries, so post one
|
||||||
|
final ClientEntry m1 = col.createEntry();
|
||||||
|
m1.setTitle("Test post");
|
||||||
|
final Content c = new Content();
|
||||||
|
c.setValue("This is a test post");
|
||||||
|
c.setType("html");
|
||||||
|
m1.setContent(c);
|
||||||
|
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry m2 = col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m2.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that entries can be posted, updated and removed in all collections that accept entries.
|
||||||
|
* Fails if no collections found that accept entries.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSimpleEntryPostUpdateAndRemove() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element2;
|
||||||
|
if (col.accepts(Collection.ENTRY_TYPE)) {
|
||||||
|
|
||||||
|
// we found a collection that accepts entries, so post one
|
||||||
|
final ClientEntry m1 = col.createEntry();
|
||||||
|
m1.setTitle(col.getTitle() + ": Test post");
|
||||||
|
final Content c = new Content();
|
||||||
|
c.setValue("This is a test post");
|
||||||
|
c.setType("html");
|
||||||
|
m1.setContent(c);
|
||||||
|
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry m2 = col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
m2.setTitle(col.getTitle() + ": Updated title");
|
||||||
|
m2.update();
|
||||||
|
|
||||||
|
// entry should now be updated on server
|
||||||
|
final ClientEntry m3 = col.getEntry(m1.getEditURI());
|
||||||
|
assertEquals(col.getTitle() + ": Updated title", m3.getTitle());
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m3.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindWorkspace() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
final ClientWorkspace ws = (ClientWorkspace) service.findWorkspace("adminblog");
|
||||||
|
if (ws != null) {
|
||||||
|
final ClientCollection col = (ClientCollection) ws.findCollection(null, "entry");
|
||||||
|
final ClientEntry entry = col.createEntry();
|
||||||
|
entry.setTitle("NPE on submitting order query");
|
||||||
|
entry.setContent("This is a <b>bad</b> one!", Content.HTML);
|
||||||
|
col.addEntry(entry);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry saved = col.getEntry(entry.getEditURI());
|
||||||
|
assertNotNull(saved);
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
saved.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(saved.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test posting an entry to every available collection with a fixed and an unfixed category if
|
||||||
|
* server support allows, then cleanup.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEntryPostWithCategories() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element2 : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element2;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element3 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element3;
|
||||||
|
if (col.accepts(Collection.ENTRY_TYPE)) {
|
||||||
|
|
||||||
|
// we found a collection that accepts GIF, so post one
|
||||||
|
final ClientEntry m1 = col.createEntry();
|
||||||
|
m1.setTitle("Test post");
|
||||||
|
final Content c = new Content();
|
||||||
|
c.setValue("This is a test post");
|
||||||
|
c.setType("html");
|
||||||
|
m1.setContent(c);
|
||||||
|
|
||||||
|
// if possible, pick one fixed an un unfixed category
|
||||||
|
Category fixedCat = null;
|
||||||
|
Category unfixedCat = null;
|
||||||
|
final List<Category> entryCats = new ArrayList<Category>();
|
||||||
|
for (int i = 0; i < col.getCategories().size(); i++) {
|
||||||
|
final Categories cats = col.getCategories().get(i);
|
||||||
|
if (cats.isFixed() && fixedCat == null) {
|
||||||
|
final String scheme = cats.getScheme();
|
||||||
|
fixedCat = cats.getCategories().get(0);
|
||||||
|
if (fixedCat.getScheme() == null) {
|
||||||
|
fixedCat.setScheme(scheme);
|
||||||
|
}
|
||||||
|
entryCats.add(fixedCat);
|
||||||
|
} else if (!cats.isFixed() && unfixedCat == null) {
|
||||||
|
final String scheme = cats.getScheme();
|
||||||
|
unfixedCat = new Category();
|
||||||
|
unfixedCat.setScheme(scheme);
|
||||||
|
unfixedCat.setTerm("tagster");
|
||||||
|
entryCats.add(unfixedCat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m1.setCategories(entryCats);
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientEntry m2 = col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
if (fixedCat != null) {
|
||||||
|
// we added a fixed category, let's make sure it's there
|
||||||
|
boolean foundCat = false;
|
||||||
|
for (final Object element : m2.getCategories()) {
|
||||||
|
final Category cat = (Category) element;
|
||||||
|
if (cat.getTerm().equals(fixedCat.getTerm())) {
|
||||||
|
foundCat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundCat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unfixedCat != null) {
|
||||||
|
// we added an unfixed category, let's make sure it's there
|
||||||
|
boolean foundCat = false;
|
||||||
|
for (final Object element : m2.getCategories()) {
|
||||||
|
final Category cat = (Category) element;
|
||||||
|
if (cat.getTerm().equals(unfixedCat.getTerm())) {
|
||||||
|
foundCat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundCat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m2.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post media entry to every media colletion avialable on server, then cleanup.
|
||||||
|
*/
|
||||||
|
public void testMediaPost() throws Exception {
|
||||||
|
assertNotNull(service);
|
||||||
|
assertTrue(!service.getWorkspaces().isEmpty());
|
||||||
|
int count = 0;
|
||||||
|
for (final Object element : service.getWorkspaces()) {
|
||||||
|
final ClientWorkspace space = (ClientWorkspace) element;
|
||||||
|
assertNotNull(space.getTitle());
|
||||||
|
|
||||||
|
for (final Object element2 : space.getCollections()) {
|
||||||
|
final ClientCollection col = (ClientCollection) element2;
|
||||||
|
if (col.accepts("image/gif")) {
|
||||||
|
|
||||||
|
// we found a collection that accepts GIF, so post one
|
||||||
|
final ClientMediaEntry m1 = col.createMediaEntry("duke" + count, "duke" + count, "image/gif", new FileInputStream(
|
||||||
|
"test/testdata/duke-wave-shadow.gif"));
|
||||||
|
col.addEntry(m1);
|
||||||
|
|
||||||
|
// entry should now exist on server
|
||||||
|
final ClientMediaEntry m2 = (ClientMediaEntry) col.getEntry(m1.getEditURI());
|
||||||
|
assertNotNull(m2);
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
m2.remove();
|
||||||
|
|
||||||
|
// fetching entry now should result in exception
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
col.getEntry(m1.getEditURI());
|
||||||
|
} catch (final ProponoException e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assertTrue(failed);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* 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 com.rometools.propono.atom.server;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
public class TestAtomHandlerFactory extends AtomHandlerFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AtomHandler newAtomHandler(final HttpServletRequest req, final HttpServletResponse res) {
|
||||||
|
return new TestAtomHandlerImpl(req, "target/testuploaddir");
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue